Hangfire 文档
Hangfire GitHub使用示例源码
在线Cron表达式生成器
● Hangfire允许您以非常简单但可靠的方式在请求管道之外启动方法调用。 这种 后台线程 中执行方法的行为称为 后台任务。
● 它是由:客户端、作业存储、服务端 组成的。
● Hangfire可以在 ASP.NET Web应用程序 、非ASP.NET Web应用程序、控制台应用程序 或 Windows服务 中使用。
● 可以使用Hangfire创建任何类型的后台作业: fire-and-forget (自助调用), delayed (在一段时间后执行调用)、 recurring (按小时,每天执行方法等)
● Hangfire将后台任务及相关的其他信息保存到持久库,SQL Server,MySql,Redis,MemoryStorage
Fire-and-Forget Jobs(基于队列的任务处理):Enqueue,仅执行一次,即发即弃作业只执行一次,而且几乎在创建后立即执行。
Delayed Jobs(延迟任务执行):Schedule,延迟的工作,延迟的作业也只执行一次,但在一定的时间间隔后不会立即执行。
Recurring Jobs(定时循环任务执行):AddOrUpdate,经常性工作,重复作业在指定的CRON计划中多次触发。
Continuations(延续性任务执行):延续,当其父作业完成时,将执行连续操作。
Batches:批量,批处理是一组以原子方式创建的后台作业,并被视为单个实体。
Batch Continuations:当父批次中的所有后台作业完成时,将激发批次延续。
1、管理 NuGet 程序包
Hangfire.AspNetCore
Hangfire.SqlServer
Hangfire.MySql
Hangfire.MemoryStorage
Hangfire.MySqlStorage
Hangfire.PostgreSql
2、Program.cs
using Hangfire;
using Hangfire.MemoryStorage;
using Hangfire.Redis.StackExchange;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.Configuration;
var builder = WebApplication.CreateBuilder(args);
var Configuration = builder.Configuration;
// Add services to the container.
#region Add Hangfire services.
// MemoryStorage
builder.Services.AddHangfire(configuration => configuration.UseMemoryStorage());
//builder.Services.AddHangfire(configuration => configuration.UseStorage(new MemoryStorage()));
// Redis
//builder.Services.AddHangfire(x =>
//{
// RedisStorageOptions options = new RedisStorageOptions()
// {
// Prefix = "hangfire:", //键前缀
// };
// x.UseRedisStorage(Configuration.GetValue<string>("ConnectionStrings:Redis:ConnectionString"), options);
//});
// SqlServer 方式1
//builder.Services.AddHangfire(configuration => configuration.UseSqlServerStorage(config.GetConnectionString("HangfireConnection")));
// SqlServer 方式2
//builder.Services.AddHangfire(configuration => configuration
// .SetDataCompatibilityLevel(CompatibilityLevel.Version_170) // 设置数据兼容级别
// .UseSimpleAssemblyNameTypeSerializer() // 使用简单程序集名称类型序列化程序
// .UseRecommendedSerializerSettings() // 使用推荐的序列化设置
// .UseSqlServerStorage(config.GetValue<string>("ConnectionStrings:BaseDb:ConnectionString"), new SqlServerStorageOptions //数据库设置
// //.UseSqlServerStorage(config.GetConnectionString("HangfireConnection"), new SqlServerStorageOptions
// {
// TransactionIsolationLevel = IsolationLevel.ReadCommitted, // 事务隔离级别。默认值为读提交。
// TransactionTimeout = TimeSpan.FromMinutes(1), // 事务超时。默认为1分钟
// JobExpirationCheckInterval = TimeSpan.FromHours(1), // 作业过期检查间隔(管理过期记录)。默认为1小时
// CountersAggregateInterval = TimeSpan.FromMinutes(5), // 间隔到聚合计数器。默认为5分钟
// PrepareSchemaIfNecessary = true, // 如果设置为true,则创建数据库表。默认值为true
// CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), // 命令批处理最大超时时间
// SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), // 滑动超时时间
// QueuePollInterval = TimeSpan.Zero, // 作业队列轮询间隔。默认值为15秒
// UseRecommendedIsolationLevel = true, // 是否使用建议的隔离级别
// DisableGlobalLocks = true, // 是否禁用全局锁,需要迁移到模式7
// DashboardJobListLimit = 50000, // 仪表板作业列表上限。默认值为50000
// }));
// Add the processing server as IHostedService
builder.Services.AddHangfireServer();
#endregion
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddControllers();
var app = builder.Build();
// Configure the HTTP request pipeline.
//Hangfire 仪表盘
//访问地址 http://localhost:5000/hangfire
app.UseHangfireDashboard();
app.UseAuthorization();
#region Hangfire
// 基于队列的任务处理
var jobId = BackgroundJob.Enqueue(() => Console.WriteLine("即时任务!"));
// 延迟任务执行
var jobId2 = BackgroundJob.Schedule(() => Console.WriteLine("延迟任务!"), TimeSpan.FromMilliseconds(10));
// 循环任务执行,最小单位是分钟,可以自定义复杂模式
//RecurringJob.AddOrUpdate("myrecurringjob", () => Console.WriteLine("重复任务!"), Cron.Minutely);
RecurringJob.AddOrUpdate("myrecurringjob", () => Console.Write("重复任务,每1分钟执行任务"), "0 0/1 * * * ?");
RecurringJob.AddOrUpdate<BackgroundJobService>("SendEmail", x => x.SendEmail("test@example.com", "Subject", "Body"), Cron.MinuteInterval(1));
RecurringJob.AddOrUpdate<IUserService>("User", u => u.Get(), Cron.Minutely);
// 延续性任务执行,在第一个任务执行完之后紧接着再次执行另外的任务
BackgroundJob.ContinueJobWith(jobId2, () => Console.WriteLine("jobId2执行完了再继续执行!"));
using (var server = new BackgroundJobServer())
{
BackgroundJob.Enqueue(() => Console.WriteLine("Simple111"));
}
#endregion
app.MapControllers();
#region Hangfire
app.Map("/index", r =>
{
r.Run(async context =>
{
//任务每分钟执行一次
RecurringJob.AddOrUpdate(() => Console.WriteLine($"ASP.NET Core LineZero"), Cron.Minutely());
await context.Response.WriteAsync("ok");
});
});
app.Map("/one", r =>
{
r.Run(context =>
{
//任务执行一次
BackgroundJob.Enqueue(() => Console.WriteLine($"ASP.NET Core One Start LineZero{DateTime.Now}"));
return context.Response.WriteAsync("ok");
});
});
app.Map("/await", r =>
{
r.Run(context =>
{
//任务延时两分钟执行
BackgroundJob.Schedule(() => Console.WriteLine($"ASP.NET Core await LineZero{DateTime.Now}"), TimeSpan.FromMinutes(2));
return context.Response.WriteAsync("ok");
});
});
#endregion
app.Run();
3、CustomJobController.cs
using Hangfire.Server;
using Hangfire;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Web.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CustomJobController : ControllerBase
{
private readonly ILogger _logger;
private readonly IBackgroundJobClient _backgroundJobClient;
public CustomJobController(ILogger<CustomJobController> logger, IBackgroundJobClient backgroundJobClient)
{
_logger = logger;
_backgroundJobClient = backgroundJobClient;
}
[HttpGet]
public IActionResult Get()
{
var jobId = _backgroundJobClient.Enqueue(() => FireAndForgetJob(null));
Thread.Sleep(5000);
return Ok($"Job Id: {jobId} completed...");
}
[HttpGet]
public IActionResult Get1()
{
var jobId = BackgroundJob.Schedule(() => Console.WriteLine("This is an example of a delayed job"), TimeSpan.FromDays(1));
return Ok($"Job Id: {jobId} completed...");
}
[HttpGet]
public IActionResult Get2()
{
var jobId = _backgroundJobClient.Enqueue(() => FireAndForgetJob(null));
Thread.Sleep(5000);
return Ok($"Job Id: {jobId} completed...");
}
public Task FireAndForgetJob(PerformContext context)
{
var jobId = context.BackgroundJob.Id;
_logger.LogInformation($"Executing Job Id: {jobId}...");
return Task.CompletedTask;
}
}
}
4、BackgroundJobService
namespace Web
{
public class BackgroundJobService
{
public void SendEmail(string emailAddress, string subject, string body)
{
// 发送电子邮件
Console.WriteLine(DateTime.Now.ToString() + " " + emailAddress + "\r\n");
}
}
}
5、IUserService
namespace Web.Services
{
public interface IUserService
{
string Get();
}
public class UserService : IUserService
{
public string Get()
{
DateTime dt = DateTime.Now;
Console.WriteLine(dt.ToString() + "\r\n");
return dt.ToString() + "\r\n";
}
}
}
*、
//自动取消订单任务
RecurringJob.AddOrUpdate<AutoCancelOrderJob>(s => s.Execute(), "0 0/5 * * * ? ", TimeZoneInfo.Local); // 每5分钟取消一次订单
//自动完成订单任务
RecurringJob.AddOrUpdate<CompleteOrderJob>(s => s.Execute(), "0 0 0/1 * * ? ", TimeZoneInfo.Local); // 每小时自动完成订单
//自动评价订单任务
RecurringJob.AddOrUpdate<EvaluateOrderJob>(s => s.Execute(), "0 0 0/1 * * ? ", TimeZoneInfo.Local); // 每小时自动完成订单
//自动签收订单任务
RecurringJob.AddOrUpdate<AutoSignOrderJob>(s => s.Execute(), "0 0 0/1 * * ? ", TimeZoneInfo.Local); // 每小时自动完成订单
//催付款订单
RecurringJob.AddOrUpdate<RemindOrderPayJob>(s => s.Execute(), "0 0/5 * * * ? ", TimeZoneInfo.Local); // 每5分钟催付款订单
//拼团自动取消到期团(每分钟执行一次)
RecurringJob.AddOrUpdate<AutoCanclePinTuanJob>(s => s.Execute(), "0 0/2 * * * ? ", TimeZoneInfo.Local); // 每分钟取消一次订单
//每天凌晨5点定期清理7天前操作日志
RecurringJob.AddOrUpdate<RemoveOperationLogJob>(s => s.Execute(), "0 0 5 * * ? ", TimeZoneInfo.Local); // 每天5点固定时间清理一次
//定时刷新获取微信AccessToken
RecurringJob.AddOrUpdate<RefreshWeChatAccessTokenJob>(s => s.Execute(), "0 0/4 * * * ? ", TimeZoneInfo.Local); // 每2分钟刷新获取微信AccessToken
//
//
//
//延迟
//如果想要延迟某些任务的执行,可以是用以下任务。在给定延迟时间后,任务会被排入队列,并且和发布 / 订阅任务一样执行。
//BackgroundJob.Schedule(() => Console.WriteLine("Delayed"), TimeSpan.FromDays(1));
//循环
//按照周期性(小时,天等)来调用方法,请使用RecurringJob类。在复杂的场景,您可以使用CRON表达式指定计划时间来处理任务。
//RecurringJob.AddOrUpdate(() => Console.WriteLine("Daily Job"), Cron.Daily);
//连续
//连续性允许您通过将多个后台任务链接在一起来定义复杂的工作流。
//var id = BackgroundJob.Enqueue(() => Console.WriteLine("Hello, "));
//BackgroundJob.ContinueWith(id, () => Console.WriteLine("world!"));
//这里呢就是需要触发的方法 "0/10 * * * * ? " 可以自行搜索cron表达式 代表循环的规律很简单
//CancelOrderJob代表你要触发的类 Execute代表你要触发的方法
//自动取消订单任务 每5分钟取消一次订单
//RecurringJob.AddOrUpdate<AutoCancelOrderJob>(s => s.Execute(), "0 0/5 * * * ? ", TimeZoneInfo.Local); // 每5分钟取消一次订单
// 任务每分钟执行一次
//RecurringJob.AddOrUpdate(() => Console.WriteLine($"ASP.NET Core LineZero"), Cron.Minutely());
自动完成订单任务 每小时自动完成订单
//RecurringJob.AddOrUpdate<CompleteOrderJob>(s => s.Execute(), "0 0 0/1 * * ? ", TimeZoneInfo.Local); // 每小时自动完成订单
自动评价订单任务
//RecurringJob.AddOrUpdate<EvaluateOrderJob>(s => s.Execute(), "0 0 0/1 * * ? ", TimeZoneInfo.Local); // 每小时自动完成订单
自动签收订单任务
//RecurringJob.AddOrUpdate<AutoSignOrderJob>(s => s.Execute(), "0 0 0/1 * * ? ", TimeZoneInfo.Local); // 每小时自动完成订单
催付款订单
//RecurringJob.AddOrUpdate<RemindOrderPayJob>(s => s.Execute(), "0 0/5 * * * ? ", TimeZoneInfo.Local); // 每5分钟催付款订单
拼团自动取消到期团(每分钟执行一次)
//RecurringJob.AddOrUpdate<AutoCanclePinTuanJob>(s => s.Execute(), "0 0/2 * * * ? ", TimeZoneInfo.Local); // 每分钟取消一次订单
每天凌晨5点定期清理7天前操作日志
//RecurringJob.AddOrUpdate<RemoveOperationLogJob>(s => s.Execute(), "0 0 5 * * ? ", TimeZoneInfo.Local); // 每天5点固定时间清理一次
定时刷新获取微信AccessToken
//RecurringJob.AddOrUpdate<RefreshWeChatAccessTokenJob>(s => s.Execute(), "0 0/4 * * * ? ", TimeZoneInfo.Local); // 每2分钟刷新获取微信AccessToken
6、Cron 表达式
是一个时间表达式,包含6或7个字段组成,每个字段代表不同的时间单位,前6个字段是必须的,最后一个【年】是可选的,从左到右依次为
7个字段:秒 分 时 日 月 星期 年
6个字段:秒 分 时 日 月 星期 (年可为空)
其中年可为空,不是必须;
在大部分使用cron的场景下, - * / ? 这几个常用字符就可以满足我们的需求了。
【*】:每的意思。在不同的字段上,就代表每秒,每分,每小时等。
【-】:指定值的范围。比如[1-10],在秒字段里就是每分钟的第1到10秒,在分就是每小时的第1到10分钟,以此类推。
【,】:指定某几个值。比如[2,4,5],在秒字段里就是每分钟的第2,第4,第5秒,以此类推。
【/】:指定值的起始和增加幅度。比如[3/5],在秒字段就是每分钟的第3秒开始,每隔5秒生效一次,也就是第3秒、8秒、13秒,以此类推。
【?】:仅用于【日】和【周】字段。因为在指定某日和周几的时候,这两个值实际上是冲突的,所以需要用【?】标识不生效的字段。比如【0 1 * * * ?】就代表每年每月每日每小时的1分0秒触发任务。这里的周就没有效果了。
*
分钟字段,表示程序应该在哪些分钟运行
星号(*):表示每隔1分钟触发
逗号(,):用于指定多个分钟,例如【0,15,30,45】表示每小时的0、15、30和45分钟
中划线(-):用于指定一个时间段,例如:【10-20】表示从10分钟到20分钟
斜杠(/):用于指定一个时间间隔,例如:【*/5】表示每5分钟
*
示例:
0 0/5 * * * *:每5分钟运行程序
0 10,20 * * * *:在每个小时的10和20分钟运行程序
0 10-20/5 * * * *:在每个小时的10到20分钟之间每5分钟运行程序
*
小时字段表示程序应该在哪些小时运行
- 星号(*):表示所有小时
- 逗号(,):用于指定多个小时,例如:——0,12,18——表示每天的0、12、18点
- 中划线(-):用于指定一个时间段,例如:——9-17——表示从9点到17点
- 斜杠(/):用于指定一个时间间隔,例如:——*/2——表示每2小时
*
示例:
0 0 */2 * * *:每2小时运行程序
0 9-17 * * * *:在每天的9点到17点之间运行程序
0 0-12/3 * * * *:在每天的0点到12点之间每3小时运行程序
*
经典案例:
"5 * * * * ?" 每分钟的第5秒执行一次
"30 * * * * ?" 每分钟的第30秒触发任务
"30 10 * * * ?" 每小时的10分30秒触发任务
"30 10 1 * * ?" 每天1点10分30秒触发任务
"30 10 1 20 * ?" 每月20号1点10分30秒触发任务
"30 10 1 20 10 ? *" 每年10月20号1点10分30秒触发任务
"30 10 1 20 10 ? 2011" 2011年10月20号1点10分30秒触发任务
"30 10 1 ? 10 * 2011" 2011年10月每天1点10分30秒触发任务
"30 10 1 ? 10 SUN 2011" 2011年10月每周日1点10分30秒触发任务
"15,30,45 * * * * ?" 每15秒,30秒,45秒时触发任务
"15-45 * * * * ?" 15到45秒内,每秒都触发任务
"15/5 * * * * ?" 每分钟的每15秒开始触发,每隔5秒触发一次
"15-30/5 * * * * ?" 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
"0 0/3 * * * ?" 每小时的第0分0秒开始,每三分钟触发一次
"0 15 10 ? * MON-FRI" 星期一到星期五的10点15分0秒触发任务
"0 15 10 L * ?" 每个月最后一天的10点15分0秒触发任务
"0 15 10 LW * ?" 每个月最后一个工作日的10点15分0秒触发任务
"0 15 10 ? * 5L" 每个月最后一个星期四的10点15分0秒触发任务
"0 15 10 ? * 5#3" 每个月第三周的星期四的10点15分0秒触发任务
常用cron表达式例子
(1)0/2 * * * * ? 表示每2秒 执行任务
(1)0 0/2 * * * ? 表示每2分钟 执行任务
(1)0 0 2 1 * ? 表示在每月的1日的凌晨2点调整任务
(2)0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业
(3)0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作
(4)0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
(5)0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
(6)0 0 12 ? * WED 表示每个星期三中午12点
(7)0 0 12 * * ? 每天中午12点触发
(8)0 15 10 ? * * 每天上午10:15触发
(9)0 15 10 * * ? 每天上午10:15触发
(10)0 15 10 * * ? 每天上午10:15触发
(11)0 15 10 * * ? 2005 2005年的每天上午10:15触发
(12)0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
(13)0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
(14)0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
(15)0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
(16)0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
(17)0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
(18)0 15 10 15 * ? 每月15日上午10:15触发
(19)0 15 10 L * ? 每月最后一日的上午10:15触发
(20)0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
(21)0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
(22)0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
*
*
*