使用Host模型创建长时间运行的代码
分类: .Net技术 ◆ 标签: #Azure #.Net #.Net Host ◆ 发布于: 2023-06-15 15:35:51

我们前面学习了Host
的基本编程模型,也了解了Host
模型提供的一些基本要素,例如:
- 配置
- 日志
- 依赖注入
IHostedService
接口
使用Host
模型创建长时间运行的代码主要的接口就是需要实现IHostedService
,然后通过扩展方法AddHostedService
注册到Host
模型中,Host
启动之后会按照注册的顺序依次启动这些服务,然后通过调用Host
的run
或者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 /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>
我们接下来的文章学习一些基本的编程实例。