使用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>
我们接下来的文章学习一些基本的编程实例。