Skip to main content

使用Host模型创建长时间运行的代码

分类:  .Net技术 标签:  #Azure #.Net #.Net Host 发布于: 2023-06-15 15:35:51

我们前面学习了Host的基本编程模型,也了解了Host模型提供的一些基本要素,例如:

  • 配置
  • 日志
  • 依赖注入
  • IHostedService接口

使用Host模型创建长时间运行的代码主要的接口就是需要实现IHostedService,然后通过扩展方法AddHostedService注册到Host模型中,Host启动之后会按照注册的顺序依次启动这些服务,然后通过调用Hostrun或者await RunAsync 阻止Main方法线程,直到Host模型运行结束,当然你也可以如下调用:

//..... 创建host, 设置Host, 注册服务.....

var hostTask = yourHost.RunAsync();

//然后这里运行其他的代码,例如创建UI block, 然后运行UI线程
var uiTask = yourHI.RunAsync();

await Task.WhenAll(hostTask, uiTask);

使用await同时block 后台任务和前台的UI任务,这样用户既可以有前台UI,也可以直接操作后台任务。

关于长时间运行的代码,我们有很多理由需要该类型的应用,例如大家经常用的基于Asp.net Core的web应用,还有后台处理数据的应用,以及定时运行的应用等等,这些应用都是基于Host模型来开发。我们本节绕开Asp.net Core是如何通过Host模型来实现的,这是一个非常大的话题,我们本章只关注非Web的项目。

前面我们一再强调需要实现IHostedService接口,然后注入到Host模型中,以及可以在实现了IHostedService的代码里注入由Host模型提供的一些服务,用于和Host交互,除了自己实现IHostedService接口之外,我们还可以通过.Net已经实现了抽象类BackgroundService直接编写自己的代码,从这个类继承后,直接实现方法ExecuteAsync来运行自己的代码就可以了。

术语

  • Background Service: 主要是指继承了BackgroundService的子类或者该类本身。
  • Hosted Service: 基于Host模型编程,实现了IHostedService接口,或者该接口自己。
  • Long-running Service: 所有需要长时间,连续运行的任务都可以这么称呼。
  • Windows Service: 代表Windows服务,之前只能使用.Net Framework实现,现在可以直接使用.Net来实现。
  • Worker Service: 指代.Net提供的模板。

Worker Service

.Net为了开发非Web应用提供的一个模板,为了使用该模板,你可以直接使用.Net cli来创建,或者使用Visual Studio创建。

dotnet new worker -o WorkerDemo1
cd WorkerDemo1
code .

.csproj说明

创建好了之后,使用VS Code打开,可以先观察.csproj工程文件:

<Project Sdk="Microsoft.NET.Sdk.Worker">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UserSecretsId>dotnet-WorkerDemo2-DACB34C0-4873-4DB0-8E4B-6CBDA336B1DD</UserSecretsId>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
  </ItemGroup>
</Project>

从工程文件里可以看到如下几点不同:

  • Project应用的SDK是Microsoft.NET.SDK.Worker, 而不是Microsoft.Net.SDK.Web
  • 明确引用了包Microsft.Extensions.Hosting
  • 启用了User Security Manager

关于Project引用SDK的说明,您可以详细的参考文档:https://docs.microsoft.com/en-us/dotnet/core/project-sdk/overview

注意
Worker Service模板没有启用Server GC, 如果为了性能你可以使用如下的方式启用基于Server GC:

<PropertyGroup>
    <ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>  

Program.cs说明

打开Program.cs文件,内容如下:

using WorkerDemo2;

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
    })
    .Build();

await host.RunAsync();

重要的是:

  • 使用了services.AddhostedService<IHostedService> 添加用户服务的接口,并且会按照添加顺序启动。
  • 使用了await host.RunAsync()来block Main的线程。

Worker.cs模板实现

打开Worker.cs文件:

namespace WorkerDemo2;

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

模板默认是实现了BackgroundService类,该类中主要需要实现的方法是ExecuteAsync, 当然用户也可以自己直接实现接口IHostedService

启用Docker支持

如果需要启用Docker支持,可以在项目目录下添加如下的Dockerfile, 用于编译Docker镜像:

# See https://aka.ms/containerfastmode to understand how Visual Studio uses this
# Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["background-service/App.WorkerService.csproj", "background-service/"]
RUN dotnet restore "background-service/App.WorkerService.csproj"
COPY . .
WORKDIR "/src/background-service"
RUN dotnet build "App.WorkerService.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "App.WorkerService.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "App.WorkerService.dll"]

同时也需要在.csproj文件里添加:

<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>

<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.11.1" />

完整文件如下:

<Project Sdk="Microsoft.NET.Sdk.Worker">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>
    <RootNamespace>App.WorkerService</RootNamespace>
    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.11.1" />
  </ItemGroup>
</Project>

我们接下来的文章学习一些基本的编程实例。