工作方式:将所有细节(如类型、方法名称、参数等)都被序列化并放入存储中,没有数据保存在进程的内存中。
客户端:负责创建后台作业并将其保存到存储中。后台作业是应该在当前执行上下文之外执行的工作单元,例如在后台线程、其他进程中,甚至在不同的服务器上,这一切都可以通过 Hangfire 实现,即使没有额外的配置。
服务器:通过查询存储来处理后台作业。粗略地说,它是一组后台线程,用于侦听存储中是否有新的后台作业,并通过反序列化类型、方法和参数来执行它们。
Enqueued(已入队): 任务已被加入队列,等待执行。这是任务的初始状态。
Scheduled(已调度): 任务已被调度,但尚未执行。这可能是因为任务设置了延迟执行或者设置了具体的执行时间。
Awaiting(等待执行): 任务正在等待执行,可能是由于父任务正在执行,而当前任务是子任务,需要等待父任务完成。
Processing(处理中): 任务正在执行。这表示任务当前正在被某个 Worker 处理。
Failed(执行失败): 任务执行过程中发生了异常或失败,被标记为失败。
Succeeded(执行成功): 任务成功完成,没有发生异常。
Deleted(已删除): 任务被删除,可能是由于某些原因,任务在执行之前被手动删除或过期自动删除。仅限Succeeded内置Deleted状态,但不是Failed最终状态,默认情况下将在 24 小时后自动过期。
当使用 Hangfire 进行任务排队时,任务由专用的工作线程池处理。每个工作线程(Worker)执行以下流程:
1.获取下一份工作并对其他工人隐藏: Worker 从队列中获取下一个待执行的工作。这个工作可能是由你的应用程序中的某个地方排队的任务。在 Worker 获取到工作之后,它会将这个工作标记为“隐藏”,以避免其他 Worker 同时执行相同的工作。
2.执行作业及其所有扩展过滤器: Worker 开始执行获取到的工作。这个工作可能是你在应用程序中定义的一个后台任务。在执行工作之前,Hangfire 允许你定义扩展过滤器,这些过滤器可以在执行工作前后进行一些额外的处理。例如,你可以在执行工作之前检查某些条件,或者在工作执行完成后执行一些清理操作。
3.从队列中删除作业: 一旦工作执行完成,Worker 将该工作从队列中删除。这表示该工作已经成功执行,不再需要被其他 Worker 处理。
这个流程确保了任务的有序执行,避免了并发执行相同任务的问题。Hangfire 的工作线程池负责调度和执行任务,确保它们按照先进先出的顺序得到处理。这使得你可以方便地在后台执行一些耗时的任务,而不影响应用程序的性能和响应性。
hangfire任务定期检查计划机制:Hangfire Server定期检查计划,将计划的作业排入其队列,从而允许工作人员执行它们。
计划:相当于一个定时任务。
Hangfire Server 定期检查: Hangfire Server 是一个独立的进程或服务,定期(通常每秒)检查计划信息。它从持久化存储中读取计划信息,查看是否有计划的作业需要被执行。这个检查过程是 Hangfire Server 在后台默默运行的。默认时间是15s,可以设置BackgroundJobServer构造函数中SchedulePollingInterval属性改变检查时间间隔。
将计划的作业添加到队列: 当 Hangfire Server 检测到有计划的作业需要执行时,它将这些作业添加到队列中。这意味着这些作业将被放入待执行的队列,等待工作线程池中的工人处理。
工作线程执行作业: 一旦计划的作业被添加到队列,工作线程池中的工人将开始处理这些作业。每个工人会从队列中取出一个作业,执行它,然后将结果报告回 Hangfire Server。
BackgroundJob.Enqueue:如果没有指定队列会创建一个新的队列
BackgroundJob.Schedule:如果没有指定会默认加入一个队列
ecurringJob.AddOrUpdate:如果没有指定会默认加入一个队列
BackgroundJob.Enqueue:即发即忘作业仅执行一次,并且几乎在创建后立即执行。
_backgroundJobClient . Enqueue < IFoodDataProvider > ( i =>
i . CreatedFoodAsync ( _mapper . Map < Foods > ( command . Food ) , CancellationToken . None ) ) ;
/*
请注意,这不是委托,而是表达式树。Hangfire 不会立即调用该方法,而是序列化类型 ( System.Console)、方法名称(WriteLine以及所有参数类型以供稍后识别)以及所有给定参数,并将其放入存储中。
*/
BackgroundJob.Schedule:延迟作业也仅在一定时间间隔后 执行一次,但不会立即执行。
_backgroundJobClient . Schedule < IFoodDataProvider > (
i => i . UpdateFoodAsync ( _mapper . Map < UpdateFoodDto > ( command . Food ) , CancellationToken . None ) ,
TimeSpan . FromMinutes ( 1 ) ) ;
RecurringJob.AddOrUpdate:执行重复任务,通过Cron表达式 指定执行时间,例如每年一月一号十二点执行一次:
RecurringJob.AddOrUpdate("powerfuljob", () => Console.Write("Powerful!"), "0 12 1 1 *");
HangfireCounter : 用于存储计数器信息 ,这可能与Hangfire的某些统计信息相关 。
HangfireDistributedLock: 用于分布式锁定,确保在多个应用程序实例中不会同时执行相同的任务。
HangfireHash: 用于存储哈希表数据,这可能与某些任务的关联数据有关。
HangfireJob: 存储调度的任务信息,包括任务的标识符、状态、类型等。
HangfireJobParameter : 用于存储与任务相关的参数信息 。
HangfireJobQueue : 用于存储任务队列信息 ,包括队列名称和相关设置。
HangfireJobState: 存储任务的当前状态,例如Enqueued、Failed、Succeeded等。
HangfireList: 用于存储列表数据,这可能与某些任务的关联数据有关。
HangfireServer: 存储Hangfire服务器实例的信息,包括服务器标识符、最后一次活动时间等。
HangfireSet : 用于存储集合数据 ,这可能与某些任务的关联数据有关 。
HangfireState: 存储任务的状态信息,与HangfireJobState可能有一些交叉,用于跟踪任务的状态历史。
┌──────────── 分钟 (0–59)
│ ┌──────────── 小时 (0–23)
│ │ ┌──────────── 一个月中的某一天 (1–31)
│ │ │ ┌────────────── 月 (1–12)
│ │ │ │ ┌──────────── 星期几 (0–6)(星期日至星期六;
│ │ │ │ │ 7 在某些系统上也是星期日)
│ │ │ │ │
│ │ │ │ │
* * * * * <要执行的命令>
在 Hangfire 中,你可以将 CancellationToken 传递给调度的后台任务或者在任务内部使用。这允许你通过取消标记来停止或中断任务的执行,通常用于取消长时间运行的任务。
对于不支持取消的任务,可以使用 CancellationToken.None
Hangfire.Core
Hangfire.MySqlStorage
Hangfire.AspNetCore
public static class HangFireExtension
{
public static void AddHangFireService ( this IServiceCollection services , IConfiguration configuration )
{
services . AddHangfire ( config => config
. SetDataCompatibilityLevel ( CompatibilityLevel . Version_170 ) //设置兼容性级别,指定与Hangfire版本1.7.0兼容。
. UseSimpleAssemblyNameTypeSerializer ( ) //使用简单的程序集名称类型序列化器。
. UseRecommendedSerializerSettings ( ) //使用建议的序列化设置。
. UseStorage ( //配置Hangfire使用的存储后端。在这里,是用来Mysql数据库作为存储,通过MySqlStorage类进行配置。
new MySqlStorage (
configuration . GetConnectionString ( "Default" ) , //获取连接字符串
new MySqlStorageOptions
{
QueuePollInterval = TimeSpan . FromSeconds ( 10 ) , //检查队列的时间间隔。
JobExpirationCheckInterval = TimeSpan . FromHours ( 1 ) , //作业到期检查的时间间隔。
CountersAggregateInterval = TimeSpan . FromMinutes ( 5 ) , //计数器聚合的时间间隔。
DashboardJobListLimit = 25000 , //仪表板上作业列表的限制。
TransactionTimeout = TimeSpan . FromMilliseconds ( 1 ) , //事务超过时间
TablesPrefix = "Hangfire" //表的前缀
}
)
) ) ;
services . AddHangfireServer ( options => options . WorkerCount = 1 ) ; //启用Hangfire服务器,它处理后台任务。options.WorkerCount设置了同时处理任务的工作线程数量。
}
}
3.在Startup中ConfigureServices添加Hangfire服务,在Configure启动Hangfire
public void ConfigureServices ( IServiceCollection services )
{
services . AddHangFireService ( Configuration ) ;
}
public void Configure ( IApplicationBuilder app , IWebHostEnvironment env )
{
app . UseHangfireServer ( ) ; //启动Hangfire服务
app . UseHangfireDashboard ( ) ; //启动Hangfire面板
}