一. 本文说明
本文档主要是介绍Hangfire的项目集成与使用方式, 参考官方文档和网络文章, 实践目前只基于.Net Framework WebApi项目进行实践可用, 所以本文也主要以此项目为示例进行步骤说明, 后续可能会更新.Net Core 项目的嵌入和使用方法
Hangfire.Dashboard.Authorization仓库及官方说明
Hangfire.RecurringJobAdmin仓库及官方说明
Hangfire.HttpJob 官方Wiki (github.com)
二. 服务搭建及配置
(一). 宿主程序
HangFire可以寄宿在多种.Net应用中,例:ASP.NET Web应用程序、非ASP.NET Web应用程序,控制台应用程序或Windows服务中使用。
支持.Net Framework 和 .Net Core ( Hangfire V1.6 + ), Framework 需要依托 Owin 实现。
本文以.Net Framework WebApi 程序为例进行部署示例, 其他宿主程序使用方式可见官方示例项目:HangfireIO/Hangfire.Samples(github.com)
(二). 安装依赖包
//owin,运行根本
PM> Install-Package Microsoft.Owin
//hangfire 核心包
PM> Install-Package Hangfire
PM> Install-Package Hangfire.Core
//hangfire 用于面板信息输出
PM> Install-Package Hangfire.Console
//hangfire 用于面板登录验证授权
PM> Install-Package Hangfire.Dashboard.Authorization
//hangfire 用于添加Http请求类型的后台任务,Hangfire.HttpJob是负责调度的, HttpJob.Client是负责发起job的
PM> Install-Package Hangfire.HttpJob
PM> Install-Package Hangfire.HttpJob.Client
//hangfire 用于持久化存储到sqlserver
PM> Install-Package Hangfire.SqlServer
//hangfire 用于面板添加任务(原面板是只能查看任务执行情况)
PM> Install-Package Hangfire.RecurringJobAdmin
(三). 配置服务
主要有两种方式:一种是基于Owin的Startup.cs进行配置,第二种是基于
依托于Owin的Startup.cs文件,没有可以创建该文件
using Hangfire; using Hangfire.Console; using Hangfire.Dashboard; using Hangfire.Heartbeat; using Hangfire.HttpJob; using Hangfire.MemoryStorage; using Hangfire.RecurringJobAdmin; using Microsoft.AspNetCore.Builder; using Microsoft.Owin; using Owin; using System; using System.Configuration; using System.Diagnostics; using System.IO; using GlobalConfiguration = Hangfire.GlobalConfiguration; using Path = System.IO.Path; [assembly: OwinStartup(typeof(WebApiServer.Startup))] namespace WebApiServer { /// <summary> /// Owin开始文件 /// </summary> public class Startup { /// <summary> /// Owin配置 /// </summary> /// <param name="app"></param> public void Configuration(IAppBuilder app) { string serverBasePath = System.Web.Hosting.HostingEnvironment.MapPath("~/"); if (string.IsNullOrEmpty(serverBasePath)) { serverBasePath = AppDomain.CurrentDomain.BaseDirectory; } //配置hangfire面板路由路径 var hangfireRoutePath = "hf"; //通过全局配置类,配置hangfire功能,依次为 /*sqlserver静态存储,其中 "sqlserver_connection" 为web.config中 connectionStrings 中一项,如下 <connectionStrings> <add name="sqlserver_connection" connectionString="Data Source = 10.30.0.3;Initial Catalog = GT_Informatization_V2; User ID = sa; Pwd = Bim@Center" providerName="System.Data.SqlClient" /> </connectionStrings> */ /*使用插件RecurringJobAdmin,参数:传递项目所有类引用*/ /*使用插件HangfireHttpJob,参数:传递配置项,主要是面板上显示的内容*/ /*使用插件Hangfire.Console,参数:面板上显示输出的背景颜色*/ GlobalConfiguration.Configuration.UseSqlServerStorage("sqlserver_connection").UseRecurringJobAdmin(true,typeof(Startup).Assembly).UseHangfireHttpJob(new HangfireHttpJobOptions() { AddHttpJobButtonName = "添加计划任务", AddRecurringJobHttpJobButtonName = "添加定时任务", EditRecurringJobButtonName = "编辑定时任务", PauseJobButtonName = "暂停或开始", DashboardTitle = "任务管理", DashboardName = "后台任务管理", DashboardFooter = "后台任务管理V1.0.0.0" }).UseConsole(new ConsoleOptions() { BackgroundColor = "#000079" }); //强制使用中文 System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("zh-CN"); //处理Hangfire.RecurringJobAdmin 插件的页面样式bug,替换原来的css文件,css文件见下个代码块 app.Use(async (ctx, next) => { if (ctx.Request.Path.Value == $"/{hangfireRoutePath}/JobConfiguration/css/jobExtension") { await ctx.Response.WriteAsync(File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + "/css/DashBoardRecurringJobAdminfix.css")); } else { await next(); } }); //如果不使用数据库持久化存储, 可使用内存存储记录任务,缺点是项目重启后, 数据会丢失,此配置需要安装Hangfire.MemoryStorage包 // GlobalConfiguration.Configuration.UseMemoryStorage(); //启用HangfireServer这个中间件(它会自动释放) var queues = new List<string> { "default", "apis", "recurring" }.ToArray(); app.UseHangfireServer(new BackgroundJobServerOptions { ServerTimeout = TimeSpan.FromMinutes(4), SchedulePollingInterval = TimeSpan.FromSeconds(15),//秒级任务需要配置短点,一般任务可以配置默认时间,默认15秒 ShutdownTimeout = TimeSpan.FromMinutes(30),//超时时间 Queues = queues,//队列 WorkerCount = Math.Max(Environment.ProcessorCount, 40)//工作线程数,当前允许的最大线程,默认20 }); //配置面板登录认证 var options = new DashboardOptions { Authorization = new[] { new BasicAuthAuthorizationFilter( new BasicAuthAuthorizationFilterOptions { SslRedirect = false, // Require secure connection for dashboard RequireSsl = false, // Case sensitive login checking LoginCaseSensitive = false, // Users Users = new[] { new BasicAuthAuthorizationUser { Login = "admin",//用户名 PasswordClear = "Admin@123"//密码 } } } ) } }; //启用Hangfire的仪表盘(可以看到任务的状态,进度等信息,结合插件可以添加任务) app.UseHangfireDashboard($"/{hangfireRoutePath}", options); //项目其他配置项.... } } }
DashBoardRecurringJobAdminfix.css 文件内容如下:
/** Main */ #app { position: relative !important; left: 0px !important; } #app > div > div { margin-top: 30px !important; width: 1626px !important } /* Modal */ .modal-mask { position: fixed; z-index: 9998; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, .5); display: table; transition: opacity .3s ease; } .modal-wrapper { display: table-cell; vertical-align: middle; } .modal-container { width: 500px; margin: 0px auto; padding: 20px 30px; background-color: #fff; border-radius: 2px; box-shadow: 0 2px 8px rgba(0, 0, 0, .33); transition: all .3s ease; font-family: Helvetica, Arial, sans-serif; } .modal-header h3 { margin-top: 0; color: #42b983; } .modal-body { margin: 20px 0; } .modal-default-button { float: right; } /* * The following styles are auto-applied to elements with * transition="modal" when their visibility is toggled * by Vue.js. * * You can easily play with the modal transition by editing * these styles. */ .modal-enter { opacity: 0; } .modal-leave-active { opacity: 0; } .modal-enter .modal-container, .modal-leave-active .modal-container { -webkit-transform: scale(1.1); transform: scale(1.1); } /* Customize check */ .check-container { width: 50px; display: block; position: relative; padding-left: 35px; margin-bottom: 10px; cursor: pointer; font-size: 18px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .check-container input { position: absolute; opacity: 0; cursor: pointer; height: 0; width: 0; } .check-container .checkmark { position: absolute; top: 0; left: 0; height: 25px; width: 25px; background-color: #eee; } .check-container:hover input ~ .check-container .checkmark { background-color: #ccc; } .check-container input:checked ~ .checkmark { background-color: #2196F3; } .checkmark:after { content: ""; position: absolute; display: none; } .check-container input:checked ~ .checkmark:after { display: block; } .check-container .checkmark:after { left: 9px; top: 5px; width: 5px; height: 10px; border: solid white; border-width: 0 3px 3px 0; -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); } .check-container input:disabled ~ .checkmark { background-color: #dddddd !important; } /* CronInput */ .cronContainer { position: relative; } .cronContainer input[type=text] { width: 96%; } .cronContainer button { position: absolute; right: 0px; height: 34px; } .cronContainer button:hover, .cronContainer button:active, .cronContainer button:focus { height: 34px !important; outline: none !important; }
三. 服务使用
(一). 通过SDK代码调用(通用)
1. 普通Job官方 SDK
(1) 即时任务
//用户自定义类,自定义函数, 静态类,静态方法也可
public class HF
{
public string Test(string a)
{
return a;
}
}
//方式一
var client = new BackgroundJobClient();
//第一个参数标识队列名, 可不传, 默认为default队列,不同的队列使用不同的处理器(核心)进行处理
//返回值为该任务的唯一标识Id
client.Enqueue("queueName",() => Console.WriteLine("Easy!"));
client.Enqueue("queueName",()=>new HF().Test("aaa"));
//方式二(推荐)
//第一个参数标识队列名, 可不传, 默认为default队列,不同的队列使用不同的处理器(核心)进行处理
//返回值为该任务的唯一标识Id
BackgroundJob.Enqueue("queueName",() => Console.WriteLine("Hello!"));
BackgroundJob.Enqueue("queueName",() => new HF().Test("aaa"));
(2) 延时任务
//用户自定义类,自定义函数, 静态类,静态方法也可
public class HF
{
public string Test(string a)
{
return a;
}
}
//方式一
var client = new BackgroundJobClient();
//第一个参数标识队列名, 可不传, 默认为default队列,不同的队列使用不同的处理器(核心)进行处理
//返回值为该任务的唯一标识Id
client.Schedule("queueName",() => Console.WriteLine("Reliable!"), TimeSpan.FromDays(1));
client.Schedule("queueName",()=>new HF().Test("aaa"), TimeSpan.FromDays(1));
client.Schedule("queueName",()=>new HF().Test("aaa"), new DateTime(2023, 12, 25, 10, 30, 0));
client.Schedule("queueName",()=>new HF().Test("aaa"), Convert.ToDateTime("2020.12.11 12:30:01"));
client.Schedule("queueName",()=>new HF().Test("aaa"), DateTimeOffset.Parse("2020.12.11 12:30:01"));
//方式二(推荐)
//第一个参数标识队列名, 可不传, 默认为default队列,不同的队列使用不同的处理器(核心)进行处理
//返回值为该任务的唯一标识Id
BackgroundJob.Schedule("queueName",() => Console.WriteLine("Reliable!"), TimeSpan.FromDays(1));
BackgroundJob.Schedule("queueName",() => new HF().Test("aaa"), TimeSpan.FromDays(1));
BackgroundJob.Schedule("queueName",() => new HF().Test("aaa"), new DateTime(2023, 12, 25, 10, 30, 0));
BackgroundJob.Schedule("queueName"() => new HF().Test("aaa"), Convert.ToDateTime("2020.12.11 12:30:01"));
BackgroundJob.Schedule("queueName"() => new HF().Test("aaa"), DateTimeOffset.Parse("2020.12.11 12:30:01"));
(3) 周期任务
//第一个参数是周期任务的Id,id重复会更新任务
//第二个参数是队列名称,不填,默认是default
//第三个参数是间隔的内置枚举,或者是cron表达式,cron表达式规则,见下本文 三->(三). Cron表达式详解
RecurringJob.AddOrUpdate("jobId","queueName",() => Console.WriteLine("Daily Job"), Cron.Daily);
RecurringJob.AddOrUpdate("jobId","queueName",() => Console.WriteLine("Daily Job"), "30 10 1 * * ?" );
//如果存在就删除周期任务
RecurringJob.RemoveIfExists("jobId");
//触发周期任务
RecurringJob.TriggerJob("jobId");
(4) 连续任务
var id = BackgroundJob.Enqueue(() => Console.WriteLine("Hello, "));
BackgroundJob.ContinueWith(id, () => Console.WriteLine("world!"));
2. HttpJob SDK
(1) 即时/延时任务
// serverUrl是hangfire dashbord的访问地址
var serverUrl = "http://localhost:5000/hf";
//下面用的是同步的方式,也可以使用异步: await HangfireJobClient.AddBackgroundJobAsync
var result = HangfireJobClient.AddBackgroundJob(serverUrl, new BackgroundJob
{
JobName = "测试api",
Method = "Get",
Url = "http://localhost:5000/testaaa"
SendSuccess = true,
DelayFromMinutes = 1 //这里是在多少分钟后执行
//RunAt = new DateTime(2020,7,25,10,5,1) // 也可以不用指定 DelayFromMinutes 参数 直接指定在什么时候运行。
}, new HangfireServerPostOption
{
BasicUserName = "admin",//这里是hangfire设置的basicauth
BasicPassword = "test"//这里是hangfire设置的basicauth
});
//result.JobId 就是这个一次性job的id 可以通过这个id在它没有运行前删除它
//删除即时任务
var result = HangfireJobClient.RemoveBackgroundJob(serverUrl, jobId, new HangfireServerPostOption
{
BasicUserName = "admin",//这里是hangfire设置的basicauth
BasicPassword = "test"//这里是hangfire设置的basicauth
});
//注意: BackgroundJob 不支持修改。如果创建后要修改 就得删除后重新创建
(2) 周期性任务
// serverUrl是hangfire dashbord的访问地址
var serverUrl = "http://localhost:5000/hf";
//下面用的是同步的方式,也可以使用异步: await HangfireJobClient.AddRecurringJobAsync
var result = HangfireJobClient.AddRecurringJob(serverUrl, new RecurringJob()
{
JobName = "测试5点40执行",
Method = "Post",
Data = new {name = "aaa",age = 10},
Url = "http://localhost:5000/testpost"
Cron = "40 17 * * *"
}, new HangfireServerPostOption
{
BasicUserName = "admin",//这里是hangfire设置的basicauth
BasicPassword = "test"//这里是hangfire设置的basicauth
});
//删除周期任务
var result = HangfireJobClient.RemoveRecurringJob(serverUrl, jobId, new HangfireServerPostOption
{
BasicUserName = "admin",//这里是hangfire设置的basicauth
BasicPassword = "test"//这里是hangfire设置的basicauth
});
//注意: RecurringJob如果同一个JobName多次调用创建 就等同于修改。删除可以调用 RemoveRecurringJob 进行删除
(二). 通过面板进行使用(局限)
1. 函数式Job - 周期性任务
引用并正确配置使用了Hangfire.RecurringJobAdmin
方可在面板上添加任务, 添加的任务函数,必须为提前写好的函数。
① 顶部切换到Job Configuration
后, 在下方点击 Add new Job 即可弹出添加任务按钮, 该插件只支持添加周期性任务.
参数说明:
参数 | 说明 |
---|---|
JobId | 任务Id |
Cron | 执行周期Cron表达式, 点击后面UI, 可以弹出UI编辑界面 |
Timezone | 时区,下拉选择 |
Class | Job函数所在的类名, 要写全称,从命名空间到类名, 例: Hangfire.RecurringJob.MyClass |
Method | 函数名,不带括号 |
Queue | 指定任务执行的队列 |
注意事项:
//通过此插件进行添加的任务函数, 要求函数访问级别是public, 且函数不能有参数和返回值
public class MyClass{
public void MyJob(){
Console.WriteLine("我是Job,我被执行了!");
}
}
② 编辑\停止任务
每条任务的后方可以编辑\停止任务, 如下图所示
2. HttpJob - 即时/延时任务
① 进入hangfire的后台 点击 左侧栏【计划】, 会看到一个按钮名称叫 【新增常规作业】如下图所示:
② 点击【新增常规作业】会出现一个json编辑器
如下图所示
json编辑器的参数说明如下
字段名称 | 备注 |
---|---|
JobName | 你给这个httpjob起的名称【必填项】 |
Method | 这个httpjob的请求方式 “get” 或者 “post” 【必填项】 |
ContentType | 这个httpjob的请求ContentType 默认”application/json” 【必填项】 |
Url | 这个httpjob的请求url 【必填项】 |
DelayFromMinutes | 需要延迟执行的分钟,==注意:0 代表立刻执行; -1代表只能手动触发; >=1代表延迟分钟数== 【必填项】 |
Data | 这个httpjob在Method=“post”的时候可以指定post的内容,可以是一个对象也可以是一个string或者其他类型 |
Timeout | 这个httpjob请求的超时时间(单位是毫秒 例如5000 代表是5秒) |
BasicUserName | 这个httpjob请求需要启用basic认证时设置的username |
BasicPassword | 这个httpjob请求需要启用basic认证时设置的密码 |
EnableRetry | 失败的时候(比如超时 远程服务器请求错误等)是否启用重试 默认false |
RetryTimes | 错误尝试次数自定义,EnableRetry=true的时候启用 |
RetryDelaysInSeconds | 失败重试区间,半角逗号隔开,EnableRetry=true的时候启用 |
SendSucMail | 这个httpjob请求无异常的时候是否发送通知邮件 默认false, 如要使用邮件功能, 需要单独配置邮件服务,详见插件官方wiki |
SendFaiMail | 这个httpjob请求异常的时候是否发送通知邮件 默认true, 如要使用邮件功能, 需要单独配置邮件服务,详见插件官方wiki |
设置通知邮件地址 如果有多个用半角逗号隔开, 如要使用邮件功能, 需要单独配置邮件服务,详见插件官方wiki | |
AgentClass | 如果是==AgentJob开发的httpjob== 则需要填写,填写的是完整的类型格式{namespace},{程序集的名称} 例如:TestHangfireAgent.Jobs,TestHangfireAgent |
AgentTimeout | 如果是AgentJob开发的job 可以配置允许job在agent里异步运行允许的最大时长。为0代表不限制 |
CallbackEL | el表达式来判断返回体是否成功还是失败,请查看wiki专门针对这个功能的介绍 |
③ 举例
{
"JobName": "checkOrder", //Job名称
"Method": "POST", //http请求的方法
"ContentType": "application/json", //http参数类型
"Url": "http://localhost:5000/checkOrder",//接口的地址
"DelayFromMinutes": 15, //15分钟后执行
"Data": {
"OrderId":123456 //传的参数 orderId
},
"Timeout": 5000, //http调用超时设置
"BasicUserName": "admin", //http调用的basicAuth
"BasicPassword": "test", //http调用的basicAuth
"EnableRetry": false,
"RetryTimes" : 3 , //错误尝试次数自定义,EnableRetry=true的时候启用
"RetryDelaysInSeconds" : "5,20,30" //失败重试区间,半角逗号隔开,EnableRetry=true的时候启用
"SendSucMail": false,
"SendFaiMail": true, //http失败时发邮件通知
"Mail": "1877682825@qq.com", //http调用失败通知我
"AgentClass": ""
}
④ 点击【提交】添加job 成功如下图所示:
可以看到会在15分钟后执行该job
针对该job 可以看到有四个按钮
按钮名称 | 说明 |
---|---|
加入队列 | 如果你想让这个job立即执行 可以点击该按钮 |
删除选中 | 如果你想删除这个job 可以点击该按钮 |
带参数执行 | 这个按钮的作用是 重新定义JSON里面的Data的值 并立即执行的意思,==在AgentJob场景下使用较多== |
停止Job | ==这个只能在AgentJob才可以使用== |
job到时执行 如下图所示
job执行完毕 在完成列表可以查询
点击job编号进入job详情页查看具体执行情况和日志
3. HttpJob - 周期性任务
- 进入hangfire的后台 点击 上侧栏【周期性作业】
如下图所示:
针对周期性作业 可以看到有三个按钮
按钮名称 | 说明 |
---|---|
新增周期性作业 | 新增一个周期性httpjob作业 |
编辑周期性作业 | 如果你重新编辑该周期性httpjob作业内容可以点击该按钮 |
Cron表达式生成 | 跳转到Cron表达式生成页面 |
- 生成Cron表达式
点击【Cron表达式生成】 进入Cron表达式生成页面 如下图:
例如 我想要 每天晚上8点05分执行
得出结果:【5 20 * * * 】
2.新增周期性作业
点击【新增周期性作业】按钮 会出现一个json编辑器
json编辑器的参数说明如下
字段名称 | 备注 |
---|---|
JobName | 你给这个httpjob起的名称(别名)【必填项】 |
RecurringJobIdentifier | 唯一id 如果你不填的话 就等于JobName 【周期性job的修改是根据这个唯一id来修改的,如果你编辑修改某个周期性job的时候修改了这个id那么提交修改会创建一个新的周期性job】 |
Method | 这个httpjob的请求方式 “get” 或者 “post” 【必填项】 |
ContentType | 这个httpjob的请求ContentType 默认”application/json” 【必填项】 |
Url | 这个httpjob的请求url 【必填项】 |
Cron | Cron表达式 可以先用【Cron表达式生成】功能生成好,如果为空则代表只能是手动出发执行! |
Data | 这个httpjob在Method=“post”的时候可以指定post的内容,可以是一个对象也可以是一个string或者其他类型 |
Timeout | 这个httpjob请求的超时时间(单位是毫秒 例如5000 代表是5秒) |
BasicUserName | 这个httpjob请求需要启用basic认证时设置的username |
BasicPassword | 这个httpjob请求需要启用basic认证时设置的密码 |
EnableRetry | 失败的时候(比如超时 远程服务器请求错误等)是否启用重试 默认false |
RetryTimes | 错误尝试次数自定义,EnableRetry=true的时候启用 |
RetryDelaysInSeconds | 失败重试区间,半角逗号隔开,EnableRetry=true的时候启用 |
SendSucMail | 这个httpjob请求无异常的时候是否发送通知邮件 默认false |
SendFaiMail | 这个httpjob请求异常的时候是否发送通知邮件 默认true |
设置通知邮件地址 如果有多个用半角逗号隔开 | |
AgentClass | 如果是==AgentJob开发的httpjob== 则需要填写,填写的是完整的类型格式{namespace},{程序集的名称} 例如:TestHangfireAgent.Jobs.XXXJOB,TestHangfireAgent |
AgentTimeout | 如果是AgentJob开发的job 可以配置允许job在agent里异步运行允许的最大时长。为0代表不限制 |
CallbackEL | el表达式来判断返回体是否成功还是失败,请查看wiki专门针对这个功能的介绍 |
举例
{
"JobName": "scoreOrder", //Job名称
"Method": "POST", //http请求的方法
"ContentType": "application/json", //http参数类型
"Url": "http://localhost:5000/scoreOrder",//接口的地址
"Cron": "5 20 * * *", //每天晚上8点05分执行
"Data": {
"Hour":48 //传的参数超过48小时
},
"Timeout": 5000, //http调用超时设置
"BasicUserName": "admin", //http调用的basicAuth
"BasicPassword": "test", //http调用的basicAuth
"EnableRetry": false, //http调用失败的错误重试
"RetryTimes" : 3 , //错误尝试次数自定义,EnableRetry=true的时候启用
"RetryDelaysInSeconds" : "5,20,30" //失败重试区间,半角逗号隔开,EnableRetry=true的时候启用
"SendSucMail": false,
"SendFaiMail": true, //http失败时发邮件通知
"Mail": "1877682825@qq.com", //http调用失败通知我
"AgentClass": ""
}
点击【提交】
添加成功 在job列表可以查
我刚才设置的是20:05分执行
目前时间是19:50
正好是还有15分钟就要执行了 说明Cron表达式没有问题
对于周期性job 有3个按钮可以操作
按钮名称 | 说明 |
---|---|
立即执行 | 如果你希望不要等到8点05想立即就执行可以点击它 |
停止或开始任务 | 如果你希望暂停这个周期性job 可以点它,点完之后再次点击就是开启 |
编辑周期任务 | 如果你刚刚添加的参数有错误,可以点击这个按钮重新编辑提交 |
停止或开始任务
如果一个周期性job是暂停的 会以红色字体展示 注意:点击停止就会把当前job的cron设置为空也就会暂停hangfire对于这个job的调度了。 注意:点击开始,会重新恢复你之前设置的cron,则hangfire就会重新调度了
编辑周期任务
点击提交修改
周期性job执行
周期性job执行完毕 在完成列表可以查询
点击job编号进入job详情页查看具体执行情况和日志
(三). Cron表达式详解
cron表达式格式:
{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}
例 “0 0 12 ? * WED” 在每星期三下午12:00 执行(年份通常 省略)
每个字段的允许值:
字段 允许值 允许的特殊字符 秒 0-59 - * / 分 0-59 - * / 小时 0-23 - * / 日期 1-31 - * ? / L W C 月份 1-12 或者 JAN-DEC - * / 星期 1-7 或者 SUN-SAT - * ? / L C # 年(可选) 留空, 1970-2099 - * / 允许值的解释:
位置 解释 Seconds (秒) 可以用数字0-59 表示, Minutes(分) 可以用数字0-59 表示, Hours(时) 可以用数字0-23表示, Day-of-Month(天) 可以用数字1-31 中的任一一个值,但要注意一些特别的月份 Month(月) 可以用0-11 或用字符串 “JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC” 表示 Day-of-Week(每周) 可以用数字1-7表示(1 = 星期日)或用字符口串“SUN, MON, TUE, WED, THU, FRI and SAT”表示 每个符号的意义:
* 表示所有值;
? 表示未说明的值,即不关心它为何值;
- 表示一个指定的范围;
, 表示附加一个可能值;
/ 符号前表示开始时间,符号后表示每次递增的值;
L(“last”) (“last”) “L” 用在day-of-month字段意思是 “这个月最后一天”;用在 day-of-week字段, 它简单意思是 “7” or “SAT”。 如果在day-of-week字段里和数字联合使用,它的意思就是 “这个月的最后一个星期几” – 例如: “6L” means “这个月的最后一个星期五”. 当我们用“L”时,不指明一个列表值或者范围是很重要的,不然的话,我们会得到一些意想不到的结果。
W(“weekday”) 只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个 月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第 16天即周一触发;如果这个月第15天是周二,那么就在触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在 day-of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日。
# 只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用”6#3”指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。
C 指和calendar联系后计算过的值。例:在day-of-month 字段用“5C”指在这个月第5天或之后包括calendar的第一天;在day-of-week字段用“1C”指在这周日或之后包括calendar的第一天。一些cron表达式案例:
表达式 解释 */5 * * * * ? 每隔5秒执行一次 0 */1 * * * ? 每隔1分钟执行一次 0 0 5-15 * * ? 每天5-15点整点触发 0 0/3 * * * ? 每三分钟触发一次 0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发 0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发 0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时 0 0 10,14,16 * * ? 每天上午10点,下午2点,4点 0 0 12 ? * WED 表示每个星期三中午12点 0 0 17 ? * TUES,THUR,SAT 每周二、四、六下午五点 0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发 0 15 10 ? * MON-FRI 周一至周五的上午10:15触发 0 0 23 L * ? 每月最后一天23点执行一次 0 15 10 L * ? 每月最后一日的上午10:15触发 0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发 0 15 10 * * ? 2005 2005年的每天上午10:15触发 0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发 0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发 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秒触发任务 表达式生成器:
在线类的很多, 上面不好使了可以自行搜索其他生成器使用
Hangfire服务, 添加了
Hangfire.RecurringJobAdmin
和Hangfire.HttpJob
插件后, 在面板中添加周期任务时,自带对应的生成器
(四). 注意事项:
关于HttpJob
HttpJob官方建议使用此插件的Hangfire.HttpJob.Client类,通过代码添加httpjob时,此添加项目尽量不要使用IIS部署运行, 因为IIS 应用池的回收问题, 可能导致某些问题。
发起HttpJob调度器只能负责定期执行http请求, 无法准确的获取http请求对应方法的执行进度、执行结果,心跳等信息, 为了解决这个问题, 官方开发了
Hangfire.HttpJob.Agent
库, 此包主要是在被执行的http请求项目中引入, 随后继承相关的类JobAgent
实现其中的OnStart
函数, 在OnStart中执行具体任务, 并打印进度等等。具体可参考下方视频