ASP.NET Core Library – Hangfire
前言
以前写过 Hangfire 的学习笔记, 但写的很乱. 这篇做个整理.
介绍
Hangfire 是用来做 server task 的, 比如: background job, delay job, schedule job 等等. 它可以做到分钟级别的 schedule, 任务会通过 SQL Server 来管理 (也可以支持其它 database 但不推荐)
参考:
安装 & Startup
参考: 官网教程
安装 nuget
dotnet add package Hangfire.Core
dotnet add package Hangfire.AspNetCore
dotnet add package Hangfire.SqlServer
startup
builder.Services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage("Server=192.168.1.152;Database=TestHangfire;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True", new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true
}));
builder.Services.AddHangfireServer();
配置不重要, 这里是按照官网 example 默认配置, 链接上 database 就可以了 (注: Hangfire 会负责创建 tables, 但我们需要先创建好 database, 不然会报错)
启动
app.MapHangfireDashboard();
Background Job
一般网站都有一个 send enquiry 的功能, 当用户提交 enquiry 表单后, 系统需要发 email 给管理人.
发 SMTP 是很慢的, 如果让用户等待会影响用户体验. 所以这种情况就很适合跑一个 background job.
request 设定好 background job 后就可以直接 response user. 然后系统才背地里去发 SMTP.
这种场景就可以用 Hangfire 来实现了.
public void OnPost()
{
var enquiryId = 1; // create enquiry to database
BackgroundJob.Enqueue<EmailService>(e => e.SendEmail(enquiryId));
}
调用 BackgroundJob.Enqueue 就可以了. 它会在 response 之后立刻被执行.
有几个点需要注意
1. BackgroundJob.Enqueue 的参数是 Expression 而不是 Func, 所以它只能简单地表达式, 如果要写负责逻辑就需要开一个方法, 让表达式去调用方法.
2. 方法必须是 public 的
3. Hangfire 执行 job 时, 会动态的创建这个方法的 class / interface, 它是通过 ActivatorUtilities.CreateInstance 创建的, 支持依赖注入哦, 如果创建失败 task 就 fail 了, 会去 rety.
4. 方法执行时是完全独立的一个 scope (线程), 和之前的 request 完全没有关系. 如果注入 HttpContext 会发现它是 null.
public class IndexModel : PageModel
{
public string Value { get; set; } = "default"; public void OnPost()
{
Value = "Not Default";
BackgroundJob.Enqueue(() => DoSomething());
} public void DoSomething()
{
var value = Value; // default
}
}
上面, DoSomething 执行是是全新的一个 scope, IndexModel 会被创建, 所以 value 是 default.
5. 尽量不要让方法依赖原本环境的东西, 做一个中间人负责.
比如, 与其把把所有信息以 parameters 形式传入方法, 更好的做法是让方法自己去获取所有信息, 通过一个 Id 作为中间人.
Delay Background Job
上面提到的例子是是属于马上执行的 background job, 还有一种是 delay job. 比如, 希望 user submit 之后 10 second 才发 email.
public void OnPost()
{
var enquiryId = 1; // create enquiry in database
BackgroundJob.Schedule<IEmailService>(s => s.SendEmail(enquiryId), TimeSpan.FromSeconds(10));
}
调用的方法是 .Schedule, 传入 delay 的 timespan 或者一个绝对时间 DateTimeOffset.
Hangfire 是通过一个 interval 在背后检查 schedule 的, 它默认的时间是 15 second 检查一次.
可以通过 options 修改它, 估计是性能考量所以才放 15 秒吧, 不然一直要去 query database check job 也挺伤的.
builder.Services.AddHangfireServer(options => {
options.SchedulePollingInterval = TimeSpan.FromSeconds(1);
});
Recurring Job
上面说的都是一次性执行, recurring job 是用来处理那种每星期/月要执行的 job.
说到这个就得说说 cron expression 了. 它就是用来表达, 每星期, 每月, 还是每逢...什么时辰的.
参考:
crontab guru 小工具
Hangfire create/remove recurring job
RecurringJob.AddOrUpdate("job name", () => Console.Write("Easy!"), "cron expression");
RecurringJob.AddOrUpdate("job name", () => Console.Write("Easy!"), Cron.Daily);
RecurringJob.RemoveIfExists("job name");
Cron.Daily 是 Hangfire 的 helper 类, 帮我们创建 cron expression.
也可以封装成 Service
RecurringJob.AddOrUpdate<FacebookReviewService>("Sync Facebook Review", service => service.SyncToDatabaseAsync(), Cron.Daily(hour: 1));
Service.cs
public class FacebookReviewService
{
private readonly IWebHostEnvironment _env; public FacebookReviewService(
IWebHostEnvironment env
)
{
_env = env;
} [AutomaticRetry(Attempts = 0)]
public async Task SyncToDatabaseAsync()
{
// do anything
}
}
AutomaticRetry 是声明是否失败了要自动重试. 0 就是不要.
状况
在设计 job 的时候要记得, server schedule 并不稳定. 有可能遇到 server down, job runtime error 等等的情况.
1. Runtime error and retry
当 job 出现 runtime error 时, Hangfire 默认会 retry 10 次, 每次 retry 都会有一个间隔时间.
它的 delay 算法是
如果想修 retry count 和 delay, 可以放 AutomaticRetryAttribute 到 job 方法上, 0 表示不要 retry, AttemptsExceededAction.Delete | Fail 意思是 error 以后是否要把这个 job 洗掉后者留一个 status fail 做计入 (这个不影响它 retry).
public class EmailService : IEmailService
{
[AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
public void SendEmail(int enquiryId)
{ }
}
由于它有 retry 的机制, 所以在设计 job 时, 需要做 transaction 确保原子性, 或者把方法做成幂等,
2. Miss execute time
Server down, retry 都有可能导致 job 运行的时间和预想的不一致. 比如预设每星期一凌晨 12 点跑.
结果那个时段 server down 了, Hangfire 会在 server up 的时候立刻补上错过的 job.
Retry 的 delay 间隔, 也会造成运行时间和预期不符.
所以在设计 job 时也需要考虑到时间.
3. 超时任务
job 运行太耗时, 与至于下一次的运行也启动了. 这时就容易出现混乱. 这个视乎是风水的问题. 应该避免大任务执行, 把它切分成小任务.
部分部分去 complete.
数据结构
Job 是查看所有运行过的 job, 不管是成功失败.
State 是所有 job 每一次 state change 的记入, 包括了 Enqueued, Processing, Succeeded 等
Set 是 recurring job 的 definition, crod expression 这些
其它就比较少会去看.
Dashboard
26-01-2022 Issue: Dashboard page blank after upgrade to .net 6.0
hot reload 和 Hangfire dashboard 撞. 目前没有看到有 github issue. wordaround 是关掉 hot reload.
访问 /Hangfire 就可以看见 build-in 的 dashboard 了
这里还可以 manual trigger job 或者移除 job 哦. 这也是 Hangfire 的一大卖点.
想自定义 url 可以这样做
app.MapHangfireDashboard("/jobs");
Read-only
app.MapHangfireDashboard("/Hangfire", new DashboardOptions
{
IsReadOnlyFunc = (DashboardContext context) => true
});
Authorize
默认只有 dev 情况下才能无授权访问 dashboard, 通过自定义 IDashboardAuthorizationFilter 就可以设定权限访问.
public class MyAuthorizationFilter : IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
var httpContext = context.GetHttpContext(); // Allow all authenticated users to see the Dashboard (potentially dangerous).
return httpContext.User.Identity.IsAuthenticated;
}
}
注: UseHangfireDashboard 要在 authentication, authorize middleware 之后.
app.MapHangfireDashboard("/Hangfire", new DashboardOptions
{
Authorization = new [] { new MyAuthorizationFilter() }
});
上面这个方法比较过时了, 如果是有搭配 login 界面的话推荐使用 MapHangfireDashboardWithAuthorizationPolicy 来做
builder.Services.AddAuthorization(options =>
options.AddPolicy("HangfireDashboard", policy => policy.RequireAuthenticatedUser())
);
app.MapHangfireDashboardWithAuthorizationPolicy("HangfireDashboard");
还有一招是用 basic authentication, 参考: Stack Overflow – ASP.NET Core MVC Hangfire custom authentication
MapHangfireDashboard vs UseHangfireDashboard
Map 是 endpoint routing 的版本, 尽量用 Map 呗
Error : JobStorage.Current property value has not been initialized
如果没有调用 app.MapHangfireDashboard 而直接使用 RecurringJob.AddOrUpdate 是会报错的.
解决方式有 2 种
1. 调用 MapHangfireDashboard
2. RequiredService<JobStorage> 激活它一下
app.Services.GetRequiredService<JobStorage>();
RecurringJob.AddOrUpdate("job name", () => Console.Write("Easy!"), Cron.Daily(hour: 6));
Multiple Server
今天突然发现 job duplicated 了. 然后发现既然有 2 个 server instance.
后来发现, 原来是我忘了把 staging server 关掉. 低级错误. 哈哈
下面这个代码可以查看当前的 server instance 有哪些
var monitoringApi = JobStorage.Current.GetMonitoringApi();
var removingServers = monitoringApi.Servers().Where(e => e.Name.Contains("myserver")).ToList();
removingServers.ForEach(removingServer => JobStorage.Current.GetConnection().RemoveServer(removingServer.Name));
ASP.NET Core Library – Hangfire的更多相关文章
- ASP.NET Core 使用 Hangfire 定时任务
定时任务组件,除了 Hangfire 外,还有一个 Quarz.NET,不过 Hangfire .NET Core 支持的会更好些. ASP.NET Core 使用 Hangfire 很简单,首先,N ...
- Asp.Net Core 集成 Hangfire 配置使用 Redis 存储
Hangfire 官方支持 MSSQL 与 Redis(Hangfire.Pro.Redis) 两种 ,由于我的数据库是 MYSQL ,粗略查询了一下文档,现在对 .NET Core 支持的并不够好, ...
- asp.net core 中hangfire面板的配置及使用
1.定义校验授权类DyDashboardAuthorizationFilter /// <summary> /// Hangfire仪表盘配置授权 /// </summary> ...
- ASP.NET Core开发-后台任务利器Hangfire使用
ASP.NET Core开发系列之后台任务利器Hangfire 使用. Hangfire 是一款强大的.NET开源后台任务利器,无需Windows服务/任务计划程序. 可以使用于ASP.NET 应用也 ...
- 解决 ASP.NET Core Hangfire 未授权(401 Unauthorized)
相关文章:ASP.NET Core 使用 Hangfire 定时任务 ASP.NET Core Hangfire 在正式环境发布之后,如果访问 http://10.1.2.31:5000/hangfi ...
- 《ASP.NET Core 高性能系列》致敬伟大的.NET斗士甲骨文!
写在开始 三年前,曾写过一篇文章:从.NET和Java之争谈IT这个行业,当时遭到某些自认为懂得java就了不起的Javaer抨击, 现在可以致敬伟大的.NET斗士甲骨文了 (JDK8以上都需要收费, ...
- ASP.NET Core and .NET Core Library Support
ASP.NET Core and .NET Core Library Support 详情参见:https://github.com/linezero/NETCoreLibrary/blob/mast ...
- Hangfire在ASP.NET CORE中的简单实现
hangfire是执行后台任务的利器,具体请看官网介绍:https://www.hangfire.io/ 新建一个asp.net core mvc 项目 引入nuget包 Hangfire.AspNe ...
- ASP.NET CORE MVC 2.0 项目中引用第三方DLL报错的解决办法 - InvalidOperationException: Cannot find compilation library location for package
目前在学习ASP.NET CORE MVC中,今天看到微软在ASP.NET CORE MVC 2.0中又恢复了允许开发人员引用第三方DLL程序集的功能,感到甚是高兴!于是我急忙写了个Demo想试试,我 ...
- 在Asp.Net Core中使用DI的方式使用Hangfire构建后台执行脚本
最近项目中需要用到后台Job,原有在Windows中我们会使用命令行程序结合计划任务或者直接生成Windows Service,现在.Net Core跨平台了,虽然Linux下也有计划任务,但跟原有方 ...
随机推荐
- [oeasy]python0105_七段数码管_7_SEGMENT_数码管驱动_4511
七位数码管 回忆上次内容 上次回顾了 指示灯 辉光管 并了解了 驱动(driver) 驱动 就是 控制设备 工作的人(模块) 辉光管离我们的生活很远了 添加图片注释,不超过 140 ...
- [oeasy]python0074_修改字体背景颜色_background_color_背景色
修改背景色 回忆上次内容 上次将asciiart和颜色一起来玩 7 种基本色 变化多端 不过到目前为止 改的 都是前景色 背景色可以修改吗? 重温参数 具体动手试试 print("\033[ ...
- npm私服 verdaccio 搭建
1.什么是npm 私服 我们前端(web,nodejs)平常使用的各种包,什么vue,react,react-router, zustand等,都会从 https://registry.npmjs.o ...
- 关于构建一个可视化+code系统的思路
思路是有参考UE的现有功能,加之前的逻辑. 大概分为三个模块: 底层, 即native层 ,这一层实际上分为三个部分: 1.GUI层的解析,2.数据存储 3.Code的解析 这三部分关键在于他们 ...
- 整段 html实现其中的每一个 a 标签跨域下载操作 window.URL.createObjectURL(blob)
window.URL.createObjectURL(blob) a 标签下载问题,通常在 a 标签中加上download属性,就可完成对href属性链接文件的下载,但仅仅是限于同源文件,如果是非同源 ...
- 一款.NET开源、跨平台的DASH/HLS/MSS下载工具
前言 今天大姚给大家分享一款.NET开源(MIT License).免费.跨平台的DASH/HLS/MSS下载工具,并且支持点播和直播(DASH/HLS)的内容下载:N_m3u8DL-RE. 网络流媒 ...
- scratch源码下载 | 超大太空游戏【80MB】
按方向键或AWSD键控制角色移动,按空格键或X键攻击. 程序超级大,共80MB,耐心等待加载. 截图: 点击下载源码 更多源码访问:小虎鲸scratch资源站
- 对比python学julia(第二章)--(第三节)玫瑰曲线—数学之美
3.1.问题描述 在数学世界中有一些美丽的曲线图形,有螺旋线.摆线.双纽线.蔓叶线且.心脏线.渐开线.玫瑰曲线.蝴蝶曲线-- 这些形状各异.简有繁别的数学曲线图形为看似枯燥的数学公式披上精彩纷呈的美丽 ...
- 10、Git之国内项目托管平台(Gitee码云)
10.1.简介 众所周知,GitHub 服务器在国外,如果网络不好的话,严重影响使用体验,甚至会出现登录不上的情况. 针对这个情况,可以使用国内的项目托管平台-- Gitee 码云,来替代 Githu ...
- 【MQTT】Mosquitto 入门案例
参考博主StoneGeek的文章 https://www.cnblogs.com/sxkgeek/p/9140180.html 之前接触的是在应用程序之间的消息中间件技术 RabbitMQ, Kafk ...