【.NET生态系列】使用Hangfire+.NET 6实现定时任务管理

打印 上一主题 下一主题

主题 871|帖子 871|积分 2623

在.NET开发生态中,我们以前开发定时任务都是用的Quartz.NET完成的。在这篇文章里,记录一下另一个很强大的定时任务框架的使用方法:Hangfire。两个框架各自都有特色和优势,可以根据参考文章里张队的那篇文章对两个框架的对比来进行选择。
引入Nuget包和配置

引入Hangfire相关的Nuget包:
Hangfire.AspNetCore
Hangfire.MemoryStorage
Hangfire.Dashboard.Basic.Authentication
并对Hangfire进行服务配置:
  1. builder.Services.AddHangfire(c =>
  2. {
  3.     // 使用内存数据库演示,在实际使用中,会配置对应数据库连接,要保证该数据库要存在
  4.     c.UseMemoryStorage();
  5. });
  6. // Hangfire全局配置
  7. GlobalConfiguration.Configuration
  8.     .UseColouredConsoleLogProvider()
  9.     .UseSerilogLogProvider()
  10.     .UseMemoryStorage()
  11.     .WithJobExpirationTimeout(TimeSpan.FromDays(7));
  12. // Hangfire服务器配置
  13. builder.Services.AddHangfireServer(options =>
  14. {
  15.     options.HeartbeatInterval = TimeSpan.FromSeconds(10);
  16. });
复制代码
使用Hangfire中间件:
  1. // 添加Hangfire Dashboard
  2. app.UseHangfireDashboard();
  3. app.UseAuthorization();
  4. app.MapControllers();
  5. // 配置Hangfire Dashboard路径和权限控制
  6. app.MapHangfireDashboard("/hangfire", new DashboardOptions
  7. {
  8.     AppPath = null,
  9.     DashboardTitle = "Hangfire Dashboard Test",
  10.     Authorization = new []
  11.     {
  12.         new HangfireCustomBasicAuthenticationFilter
  13.         {
  14.             User = app.Configuration.GetSection("HangfireCredentials:UserName").Value,
  15.             Pass = app.Configuration.GetSection("HangfireCredentials:Password").Value
  16.         }
  17.     }
  18. });
复制代码
对应的配置如下:

  • appsettings.json
  1. "HangfireCredentials": {
  2.   "UserName": "admin",
  3.   "Password": "admin@123"
  4. }
复制代码
编写Job

Hangfire免费版本支持以下类型的定时任务:

  • 周期性定时任务:Recurring Job
  • 执行单次任务:Fire and Forget
  • 连续顺序执行任务:Continouus Job
  • 定时单次任务:Schedule Job
Fire and Forget

这种类型的任务一般是在应用程序启动的时候执行一次结束后不再重复执行,最简单的配置方法是这样的:
  1. using Hangfire;
  2. BackgroundJob.Enqueue(() => Console.WriteLine("Hello world from Hangfire with Fire and Forget job!"));
复制代码
Continuous Job

这种类型的任务一般是进行顺序型的任务执行调度,比如先完成任务A,结束后执行任务B:
  1. var jobId = BackgroundJob.Enqueue(() => Console.WriteLine("Hello world from Hangfire with Fire and Forget job!"));
  2. // Continuous Job, 通过指定上一个任务的Id来跟在上一个任务后执行
  3. BackgroundJob.ContinueJobWith(jobId, () => Console.WriteLine("Hello world from Hangfire using continuous job!"));
复制代码
Scehdule Job

这种类型的任务是用于在未来某个特定的时间点被激活运行的任务,也被叫做Delayed Job:
  1. // 指定5天后执行
  2. BackgroundJob.Schedule(() => Console.WriteLine("Hello world from Hangfire using scheduled job!"), TimeSpan.FromDays(5));
复制代码
Recurring Job

这种类型的任务应该是我们最常使用的类型,使用Cron表达式来设定一个执行周期时间,每到设定时间就被激活执行一次。对于这种相对常见的场景,我们可以演示一下使用单独的类来封装任务逻辑:

  • IJob.cs
  1. namespace HelloHangfire;
  2. public interface IJob
  3. {
  4.     public Task<bool> RunJob();
  5. }
复制代码

  • Job.cs
  1. using Serilog;
  2. namespace HelloHangfire;
  3. public class Job : IJob
  4. {
  5.     public async Task<bool> RunJob()
  6.     {
  7.         Log.Information($"start time: {DateTime.Now}");
  8.         // 模拟任务执行
  9.         await Task.Delay(1000);
  10.         Log.Information("Hello world from Hangfire in Recurring mode!");
  11.         Log.Information($"stop time: {DateTime.Now}");
  12.         return true;
  13.     }
  14. }
复制代码
在Program.cs中使用Cron来注册任务:
  1. builder.Services.AddTransient<IJob, Job>();
  2. // ...
  3. var app = builder.Build();
  4. // ...
  5. var JobService = app.Services.GetRequiredService<IJob>();
  6. // Recurring job
  7. RecurringJob.AddOrUpdate("Run every minute", () => JobService.RunJob(), "* * * * *");
复制代码
Run

控制台输出:
  1. info: Hangfire.BackgroundJobServer[0]
  2.       Starting Hangfire Server using job storage: 'Hangfire.MemoryStorage.MemoryStorage'
  3. info: Hangfire.BackgroundJobServer[0]
  4.       Using the following options for Hangfire Server:
  5.           Worker count: 20
  6.           Listening queues: 'default'
  7.           Shutdown timeout: 00:00:15
  8.           Schedule polling interval: 00:00:15
  9. info: Hangfire.Server.BackgroundServerProcess[0]
  10.       Server b8d0de54-caee-4c5e-86f5-e79a47fad51f successfully announced in 11.1236 ms
  11. info: Hangfire.Server.BackgroundServerProcess[0]
  12.       Server b8d0de54-caee-4c5e-86f5-e79a47fad51f is starting the registered dispatchers: ServerWatchdog, ServerJobCancellationWatcher, ExpirationManager, CountersAggregator, Worker, DelayedJobScheduler, RecurringJobScheduler...
  13. info: Hangfire.Server.BackgroundServerProcess[0]
  14.       Server b8d0de54-caee-4c5e-86f5-e79a47fad51f all the dispatchers started
  15. Hello world from Hangfire with Fire and Forget job!
  16. Hello world from Hangfire using continuous job!
  17. info: Microsoft.Hosting.Lifetime[14]
  18.       Now listening on: https://localhost:7295
  19. info: Microsoft.Hosting.Lifetime[14]
  20.       Now listening on: http://localhost:5121
  21. info: Microsoft.Hosting.Lifetime[0]
  22.       Application started. Press Ctrl+C to shut down.
  23. info: Microsoft.Hosting.Lifetime[0]
  24.       Hosting environment: Development
  25. info: Microsoft.Hosting.Lifetime[0]
  26.       Content root path: /Users/yu.li1/Projects/asinta/Net6Demo/HelloHangfire/HelloHangfire/
  27. [16:56:14 INF] start time: 02/25/2022 16:56:14
  28. [16:57:14 INF] start time: 02/25/2022 16:57:14
  29. [16:57:34 INF] Hello world from Hangfire in Recurring mode!
  30. [16:57:34 INF] stop time: 02/25/2022 16:57:34
复制代码
通过配置的dashboard来查看所有的job运行的状况:

长时间运行任务的并发控制???

从上面的控制台日志可以看出来,使用Hangfire进行周期性任务触发的时候,如果执行时间大于执行的间隔周期,会产生任务的并发。如果我们不希望任务并发,可以在配置并发数量的时候配置成1,或者在任务内部去判断当前是否有相同的任务正在执行,如果有则停止继续执行。但是这样也无法避免由于执行时间过长导致的周期间隔不起作用的问题,比如我们希望不管在任务执行多久的情况下,前后两次激活都有一个固定的间隔时间,这样的实现方法我还没有试出来。有知道怎么做的小伙伴麻烦说一下经验。
Job Filter记录Job的全部事件

有的时候我们希望记录Job运行生命周期内的所有事件,可以参考官方文档:Using job filters来实现该需求。
参考文章

关于Hangfire更加详细和生产环境的使用,张队写过一篇文章:Hangfire项目实践分享

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

篮之新喜

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表