GVKun编程网logo

循序渐进学.Net Core Web Api 开发系列【17】:.Net core 自动作业之 Hangfire

1

在本文中,我们将带你了解循序渐进学.NetCoreWebApi开发系列【17】:.Netcore自动作业之Hangfire在这篇文章中,同时我们还将给您一些技巧,以帮助您实现更有效的.netCoreH

在本文中,我们将带你了解循序渐进学.Net Core Web Api 开发系列【17】:.Net core 自动作业之 Hangfire在这篇文章中,同时我们还将给您一些技巧,以帮助您实现更有效的.net Core HangFire 设置 Cron 时间、.NET Core 下开源任务调度框架 Hangfire 的 Api 任务拓展 (支持秒级任务)、.net core 之 Hangfire 任务调度、.NET Core 开源组件:后台任务利器之 Hangfire

本文目录一览:

循序渐进学.Net Core Web Api 开发系列【17】:.Net core 自动作业之 Hangfire

循序渐进学.Net Core Web Api 开发系列【17】:.Net core 自动作业之 Hangfire

<div id="cnblogs_post_body"><p>nuget 搜索:Hangfire</p> <p > 安装即可,这里我选择的是 & nbsp;1.7.0-beta1 版本 </p> <p>&nbsp;</p> <p > 我是用这个集成到了 mvc api 里 </p> <p>&nbsp;</p> <p > 这里需要在 & nbsp;Startup 文件里进行如下配置 </p> <p>&nbsp;</p> <p > 在配置方法 ConfigureServices 里配置数据库,这是用的是 sqlserver 数据库初始化 </p> <div> <pre> <span>var</span> hangfireConnStr = _configuration [<span>"</span><span>AppSettings:HangfireConnectionString</span><span>"</span><span>]; services.AddHangfire (configuration </span>=&gt; configuration.UseSqlServerStorage (hangfireConnStr));</pre> </div> <p>&nbsp;</p> <p > 如果数据库是 mysql,使用下面语句初始化,需要引用 & nbsp;Hangfire.MySql</p> <div><div><span><a href="javascript:void (0);" onclick="copyCnblogsCode (this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div> <pre> <span>var</span> hangfireMysqlConnStr = _configuration [<span>"</span><span>AppSettings:HangfireMysqlConnectionString</span><span>"</span><span>]; services.AddHangfire (configuration </span>=&gt;<span> configuration.UseStorage ( </span><span>new</span><span> MySqlStorage ( hangfireMysqlConnStr, </span><span>new</span><span> MySqlStorageOptions { TransactionIsolationLevel </span>=<span> IsolationLevel.ReadCommitted, QueuePollInterval </span>= TimeSpan.FromSeconds (<span>15</span><span>), JobExpirationCheckInterval </span>= TimeSpan.FromHours (<span>1</span><span>), CountersAggregateInterval </span>= TimeSpan.FromMinutes (<span>5</span><span>), PrepareSchemaIfNecessary </span>= <span>true</span><span>, DashboardJobListLimit </span>= <span>50000</span><span>, TransactionTimeout </span>= TimeSpan.FromMinutes (<span>1</span><span>), TablesPrefix </span>= <span>""</span><span> })));</span></pre> <div><span><a href="javascript:void (0);"onclick="copyCnblogsCode (this)"title=" 复制代码 "><img src="//common.cnblogs.com/images/copycode.gif"alt=" 复制代码 "></a></span></div></div> <p>&nbsp;</p> <p > 在配置方法 Configure 里需要配置下管理员后台 </p> <div> <pre><span> app.UseHangfireServer (); app.UseHangfireDashboard ();</span></pre> </div> <p>&nbsp;</p> <p > 这样我们就可以通过后台操作管理作业了 </p> <p > 地址如下 </p> <p>https://localhost:5001/hangfire</p> <p><img src="https://img2018.cnblogs.com/blog/711792/201811/711792-20181127161704974-838252726.png"alt=""></p> <p>&nbsp;</p> <p > 这里可以看到正在跑的作业和执行情况 </p> <p><img src="https://img2018.cnblogs.com/blog/711792/201811/711792-20181127161813135-167191502.png" alt=""></p> <p>&nbsp;</p> <p>&nbsp; 不过这个后台只能在服务器本机上访问,为了保证安全,无法通过域名访问操作 </p> <p>&nbsp;</p> <p > 添加调用代码很简单,在每次系统启动的时候,配置如下,如果没有添加;有了就更新 </p> <div> <pre>RecurringJob.AddOrUpdate (() =&gt; UpdateMerchIndex (), Cron.MinuteInterval (<span>3</span>));</pre> </div> <p > 这里是指每隔三分钟调用一次 & nbsp;UpdateMerchIndex () 方法。</p> <p>&nbsp;</p> <p > 是不是很简单呢 </p> <p>&nbsp;</p> <p > 数据库需要初始化几个表 </p> <p><img src="https://img2018.cnblogs.com/blog/711792/201901/711792-20190122141653745-1302906924.png"alt=""></p> <p>&nbsp;</p> <p>&nbsp;</p> <p > 这里附送数据库初始化建表 SQL 语句 </p> <p>sqlserver</p> <divonclick="cnblogs_code_show (''d5742dde-540e-47fe-8717-897d4ede3fe1'')"><img id="code_img_closed_d5742dde-540e-47fe-8717-897d4ede3fe1"src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code_img_opened_d5742dde-540e-47fe-8717-897d4ede3fe1"onclick="cnblogs_code_hide (''d5742dde-540e-47fe-8717-897d4ede3fe1'',event)"src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif"alt=""> <div id="cnblogs_code_open_d5742dde-540e-47fe-8717-897d4ede3fe1"> <pre><span>USE</span> <span>[</span><span>GEDU_Hangfire</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[Job] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>Job</span><span>]</span><span>( </span><span>[</span><span>Id</span><span>]</span> <span>[</span><span>bigint</span><span>]</span> <span>IDENTITY</span>(<span>1</span>,<span>1</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>StateId</span><span>]</span> <span>[</span><span>bigint</span><span>]</span> <span>NULL</span><span>, </span><span>[</span><span>StateName</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>20</span>) <span>NULL</span><span>, </span><span>[</span><span>InvocationData</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>max</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Arguments</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>max</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>CreatedAt</span><span>]</span> <span>[</span><span>datetime</span><span>]</span> <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>ExpireAt</span><span>]</span> <span>[</span><span>datetime</span><span>]</span> <span>NULL</span><span>, </span><span>CONSTRAINT</span> <span>[</span><span>PK_HangFire_Job</span><span>]</span> <span>PRIMARY</span> <span>KEY</span> <span>CLUSTERED</span><span> ( </span><span>[</span><span>Id</span><span>]</span> <span>ASC</span><span> )</span><span>WITH</span> (PAD_INDEX <span>=</span> <span>OFF</span>, STATISTICS_NORECOMPUTE <span>=</span> <span>OFF</span>, IGNORE_DUP_KEY <span>=</span> <span>OFF</span>, ALLOW_ROW_LOCKS <span>=</span> <span>ON</span>, ALLOW_PAGE_LOCKS <span>=</span> <span>ON</span>) <span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[Hash] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>Hash</span><span>]</span><span>( </span><span>[</span><span>Key</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>100</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Field</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>100</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Value</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>max</span>) <span>NULL</span><span>, </span><span>[</span><span>ExpireAt</span><span>]</span> <span>[</span><span>datetime2</span><span>]</span>(<span>7</span>) <span>NULL</span><span>, </span><span>CONSTRAINT</span> <span>[</span><span>PK_HangFire_Hash</span><span>]</span> <span>PRIMARY</span> <span>KEY</span> <span>CLUSTERED</span><span> ( </span><span>[</span><span>Key</span><span>]</span> <span>ASC</span><span>, </span><span>[</span><span>Field</span><span>]</span> <span>ASC</span><span> )</span><span>WITH</span> (PAD_INDEX <span>=</span> <span>OFF</span>, STATISTICS_NORECOMPUTE <span>=</span> <span>OFF</span>, IGNORE_DUP_KEY <span>=</span> <span>OFF</span>, ALLOW_ROW_LOCKS <span>=</span> <span>ON</span>, ALLOW_PAGE_LOCKS <span>=</span> <span>ON</span>) <span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[Counter] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>Counter</span><span>]</span><span>( </span><span>[</span><span>Key</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>100</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Value</span><span>]</span> <span>[</span><span>int</span><span>]</span> <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>ExpireAt</span><span>]</span> <span>[</span><span>datetime</span><span>]</span> <span>NULL</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[AggregatedCounter] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>AggregatedCounter</span><span>]</span><span>( </span><span>[</span><span>Key</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>100</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Value</span><span>]</span> <span>[</span><span>bigint</span><span>]</span> <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>ExpireAt</span><span>]</span> <span>[</span><span>datetime</span><span>]</span> <span>NULL</span><span>, </span><span>CONSTRAINT</span> <span>[</span><span>PK_HangFire_CounterAggregated</span><span>]</span> <span>PRIMARY</span> <span>KEY</span> <span>CLUSTERED</span><span> ( </span><span>[</span><span>Key</span><span>]</span> <span>ASC</span><span> )</span><span>WITH</span> (PAD_INDEX <span>=</span> <span>OFF</span>, STATISTICS_NORECOMPUTE <span>=</span> <span>OFF</span>, IGNORE_DUP_KEY <span>=</span> <span>OFF</span>, ALLOW_ROW_LOCKS <span>=</span> <span>ON</span>, ALLOW_PAGE_LOCKS <span>=</span> <span>ON</span>) <span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[Set] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>Set</span><span>]</span><span>( </span><span>[</span><span>Key</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>100</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Score</span><span>]</span> <span>[</span><span>float</span><span>]</span> <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Value</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>256</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>ExpireAt</span><span>]</span> <span>[</span><span>datetime</span><span>]</span> <span>NULL</span><span>, </span><span>CONSTRAINT</span> <span>[</span><span>PK_HangFire_Set</span><span>]</span> <span>PRIMARY</span> <span>KEY</span> <span>CLUSTERED</span><span> ( </span><span>[</span><span>Key</span><span>]</span> <span>ASC</span><span>, </span><span>[</span><span>Value</span><span>]</span> <span>ASC</span><span> )</span><span>WITH</span> (PAD_INDEX <span>=</span> <span>OFF</span>, STATISTICS_NORECOMPUTE <span>=</span> <span>OFF</span>, IGNORE_DUP_KEY <span>=</span> <span>OFF</span>, ALLOW_ROW_LOCKS <span>=</span> <span>ON</span>, ALLOW_PAGE_LOCKS <span>=</span> <span>ON</span>) <span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[Server] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>Server</span><span>]</span><span>( </span><span>[</span><span>Id</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>100</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Data</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>max</span>) <span>NULL</span><span>, </span><span>[</span><span>LastHeartbeat</span><span>]</span> <span>[</span><span>datetime</span><span>]</span> <span>NOT</span> <span>NULL</span><span>, </span><span>CONSTRAINT</span> <span>[</span><span>PK_HangFire_Server</span><span>]</span> <span>PRIMARY</span> <span>KEY</span> <span>CLUSTERED</span><span> ( </span><span>[</span><span>Id</span><span>]</span> <span>ASC</span><span> )</span><span>WITH</span> (PAD_INDEX <span>=</span> <span>OFF</span>, STATISTICS_NORECOMPUTE <span>=</span> <span>OFF</span>, IGNORE_DUP_KEY <span>=</span> <span>OFF</span>, ALLOW_ROW_LOCKS <span>=</span> <span>ON</span>, ALLOW_PAGE_LOCKS <span>=</span> <span>ON</span>) <span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[Schema] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>Schema</span><span>]</span><span>( </span><span>[</span><span>Version</span><span>]</span> <span>[</span><span>int</span><span>]</span> <span>NOT</span> <span>NULL</span><span>, </span><span>CONSTRAINT</span> <span>[</span><span>PK_HangFire_Schema</span><span>]</span> <span>PRIMARY</span> <span>KEY</span> <span>CLUSTERED</span><span> ( </span><span>[</span><span>Version</span><span>]</span> <span>ASC</span><span> )</span><span>WITH</span> (PAD_INDEX <span>=</span> <span>OFF</span>, STATISTICS_NORECOMPUTE <span>=</span> <span>OFF</span>, IGNORE_DUP_KEY <span>=</span> <span>OFF</span>, ALLOW_ROW_LOCKS <span>=</span> <span>ON</span>, ALLOW_PAGE_LOCKS <span>=</span> <span>ON</span>) <span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[List] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>List</span><span>]</span><span>( </span><span>[</span><span>Id</span><span>]</span> <span>[</span><span>bigint</span><span>]</span> <span>IDENTITY</span>(<span>1</span>,<span>1</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Key</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>100</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Value</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>max</span>) <span>NULL</span><span>, </span><span>[</span><span>ExpireAt</span><span>]</span> <span>[</span><span>datetime</span><span>]</span> <span>NULL</span><span>, </span><span>CONSTRAINT</span> <span>[</span><span>PK_HangFire_List</span><span>]</span> <span>PRIMARY</span> <span>KEY</span> <span>CLUSTERED</span><span> ( </span><span>[</span><span>Key</span><span>]</span> <span>ASC</span><span>, </span><span>[</span><span>Id</span><span>]</span> <span>ASC</span><span> )</span><span>WITH</span> (PAD_INDEX <span>=</span> <span>OFF</span>, STATISTICS_NORECOMPUTE <span>=</span> <span>OFF</span>, IGNORE_DUP_KEY <span>=</span> <span>OFF</span>, ALLOW_ROW_LOCKS <span>=</span> <span>ON</span>, ALLOW_PAGE_LOCKS <span>=</span> <span>ON</span>) <span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[JobQueue] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>JobQueue</span><span>]</span><span>( </span><span>[</span><span>Id</span><span>]</span> <span>[</span><span>int</span><span>]</span> <span>IDENTITY</span>(<span>1</span>,<span>1</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>JobId</span><span>]</span> <span>[</span><span>bigint</span><span>]</span> <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Queue</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>50</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>FetchedAt</span><span>]</span> <span>[</span><span>datetime</span><span>]</span> <span>NULL</span><span>, </span><span>CONSTRAINT</span> <span>[</span><span>PK_HangFire_JobQueue</span><span>]</span> <span>PRIMARY</span> <span>KEY</span> <span>CLUSTERED</span><span> ( </span><span>[</span><span>Queue</span><span>]</span> <span>ASC</span><span>, </span><span>[</span><span>Id</span><span>]</span> <span>ASC</span><span> )</span><span>WITH</span> (PAD_INDEX <span>=</span> <span>OFF</span>, STATISTICS_NORECOMPUTE <span>=</span> <span>OFF</span>, IGNORE_DUP_KEY <span>=</span> <span>OFF</span>, ALLOW_ROW_LOCKS <span>=</span> <span>ON</span>, ALLOW_PAGE_LOCKS <span>=</span> <span>ON</span>) <span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[State] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>State</span><span>]</span><span>( </span><span>[</span><span>Id</span><span>]</span> <span>[</span><span>bigint</span><span>]</span> <span>IDENTITY</span>(<span>1</span>,<span>1</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>JobId</span><span>]</span> <span>[</span><span>bigint</span><span>]</span> <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Name</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>20</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Reason</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>100</span>) <span>NULL</span><span>, </span><span>[</span><span>CreatedAt</span><span>]</span> <span>[</span><span>datetime</span><span>]</span> <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Data</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>max</span>) <span>NULL</span><span>, </span><span>CONSTRAINT</span> <span>[</span><span>PK_HangFire_State</span><span>]</span> <span>PRIMARY</span> <span>KEY</span> <span>CLUSTERED</span><span> ( </span><span>[</span><span>JobId</span><span>]</span> <span>ASC</span><span>, </span><span>[</span><span>Id</span><span>]</span> <span>ASC</span><span> )</span><span>WITH</span> (PAD_INDEX <span>=</span> <span>OFF</span>, STATISTICS_NORECOMPUTE <span>=</span> <span>OFF</span>, IGNORE_DUP_KEY <span>=</span> <span>OFF</span>, ALLOW_ROW_LOCKS <span>=</span> <span>ON</span>, ALLOW_PAGE_LOCKS <span>=</span> <span>ON</span>) <span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: Table [HangFire].[JobParameter] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>SET</span> ANSI_NULLS <span>ON</span> <span>GO</span> <span>SET</span> QUOTED_IDENTIFIER <span>ON</span> <span>GO</span> <span>CREATE</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>JobParameter</span><span>]</span><span>( </span><span>[</span><span>JobId</span><span>]</span> <span>[</span><span>bigint</span><span>]</span> <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Name</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>40</span>) <span>NOT</span> <span>NULL</span><span>, </span><span>[</span><span>Value</span><span>]</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>max</span>) <span>NULL</span><span>, </span><span>CONSTRAINT</span> <span>[</span><span>PK_HangFire_JobParameter</span><span>]</span> <span>PRIMARY</span> <span>KEY</span> <span>CLUSTERED</span><span> ( </span><span>[</span><span>JobId</span><span>]</span> <span>ASC</span><span>, </span><span>[</span><span>Name</span><span>]</span> <span>ASC</span><span> )</span><span>WITH</span> (PAD_INDEX <span>=</span> <span>OFF</span>, STATISTICS_NORECOMPUTE <span>=</span> <span>OFF</span>, IGNORE_DUP_KEY <span>=</span> <span>OFF</span>, ALLOW_ROW_LOCKS <span>=</span> <span>ON</span>, ALLOW_PAGE_LOCKS <span>=</span> <span>ON</span>) <span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span><span> ) </span><span>ON</span> <span>[</span><span>PRIMARY</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: ForeignKey [FK_HangFire_JobParameter_Job] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>ALTER</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>JobParameter</span><span>]</span> <span>WITH</span> <span>CHECK</span> <span>ADD</span> <span>CONSTRAINT</span> <span>[</span><span>FK_HangFire_JobParameter_Job</span><span>]</span> <span>FOREIGN</span> <span>KEY</span>(<span>[</span><span>JobId</span><span>]</span><span>) </span><span>REFERENCES</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>Job</span><span>]</span> (<span>[</span><span>Id</span><span>]</span><span>) </span><span>ON</span> <span>UPDATE</span> <span>CASCADE</span> <span>ON</span> <span>DELETE</span> <span>CASCADE</span> <span>GO</span> <span>ALTER</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>JobParameter</span><span>]</span> <span>CHECK</span> <span>CONSTRAINT</span> <span>[</span><span>FK_HangFire_JobParameter_Job</span><span>]</span> <span>GO</span> <span>/*</span><span>***** Object: ForeignKey [FK_HangFire_State_Job] Script Date: 01/22/2019 14:16:29 *****</span><span>*/</span> <span>ALTER</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>State</span><span>]</span> <span>WITH</span> <span>CHECK</span> <span>ADD</span> <span>CONSTRAINT</span> <span>[</span><span>FK_HangFire_State_Job</span><span>]</span> <span>FOREIGN</span> <span>KEY</span>(<span>[</span><span>JobId</span><span>]</span><span>) </span><span>REFERENCES</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>Job</span><span>]</span> (<span>[</span><span>Id</span><span>]</span><span>) </span><span>ON</span> <span>UPDATE</span> <span>CASCADE</span> <span>ON</span> <span>DELETE</span> <span>CASCADE</span> <span>GO</span> <span>ALTER</span> <span>TABLE</span> <span>[</span><span>HangFire</span><span>]</span>.<span>[</span><span>State</span><span>]</span> <span>CHECK</span> <span>CONSTRAINT</span> <span>[</span><span>FK_HangFire_State_Job</span><span>]</span> <span>GO</span></pre> </div> <span>View Code</span></div> <p>&nbsp;</p> <p>mysql</p> <divonclick="cnblogs_code_show (''5de148cc-a57f-412f-be86-18550ae426f8'')"><img id="code_img_closed_5de148cc-a57f-412f-be86-18550ae426f8"src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code_img_opened_5de148cc-a57f-412f-be86-18550ae426f8"onclick="cnblogs_code_hide (''5de148cc-a57f-412f-be86-18550ae426f8'',event)"src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif"alt=""> <div id="cnblogs_code_open_5de148cc-a57f-412f-be86-18550ae426f8"> <pre><span>/*</span><span> Navicat Premium Data Transfer

Source Server : mysql47.93.198.115 Source Server Type : MySQL Source Server Version : 50562 Source Host : 47.93.198.115:3306 Source Schema : gedu_hangfire

Target Server Type : MySQL Target Server Version : 50562 File Encoding : 65001

Date: 22/01/2019 14:17:37 </span><span>*/</span>

<span>SET</span><span> NAMES utf8mb4; </span><span>SET</span> FOREIGN_KEY_CHECKS <span>=</span> <span>0</span><span>;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for aggregatedcounter</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> aggregatedcounter; </span><span>CREATE</span> <span>TABLE</span><span> aggregatedcounter ( Id </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span> AUTO_INCREMENT, </span><span>Key</span> <span>varchar</span>(<span>100</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Value </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span>, ExpireAt </span><span>datetime</span> <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE, </span><span>UNIQUE</span> <span>INDEX</span> IX_CounterAggregated_Key(<span>Key</span><span>) USING BTREE ) ENGINE </span><span>=</span> InnoDB AUTO_INCREMENT <span>=</span> <span>3482</span> <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for counter</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> counter; </span><span>CREATE</span> <span>TABLE</span><span> counter ( Id </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span> AUTO_INCREMENT, </span><span>Key</span> <span>varchar</span>(<span>100</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Value </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span>, ExpireAt </span><span>datetime</span> <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE, </span><span>INDEX</span> IX_Counter_Key(<span>Key</span><span>) USING BTREE ) ENGINE </span><span>=</span> InnoDB AUTO_INCREMENT <span>=</span> <span>17107</span> <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for distributedlock</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> distributedlock; </span><span>CREATE</span> <span>TABLE</span><span> distributedlock ( Resource </span><span>varchar</span>(<span>100</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, CreatedAt </span><span>datetime</span> <span>NOT</span> <span>NULL</span><span> ) ENGINE </span><span>=</span> InnoDB <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for hash</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> hash; </span><span>CREATE</span> <span>TABLE</span><span> hash ( Id </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span> AUTO_INCREMENT, </span><span>Key</span> <span>varchar</span>(<span>100</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Field </span><span>varchar</span>(<span>40</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Value longtext </span><span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NULL</span><span>, ExpireAt </span><span>datetime</span> <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE, </span><span>UNIQUE</span> <span>INDEX</span> IX_Hash_Key_Field(<span>Key</span><span>, Field) USING BTREE ) ENGINE </span><span>=</span> InnoDB AUTO_INCREMENT <span>=</span> <span>29028</span> <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for job</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> job; </span><span>CREATE</span> <span>TABLE</span><span> job ( Id </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span> AUTO_INCREMENT, StateId </span><span>int</span>(<span>11</span>) <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, StateName </span><span>varchar</span>(<span>20</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, InvocationData longtext </span><span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Arguments longtext </span><span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, CreatedAt </span><span>datetime</span> <span>NOT</span> <span>NULL</span><span>, ExpireAt </span><span>datetime</span> <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE, </span><span>INDEX</span><span> IX_Job_StateName(StateName) USING BTREE ) ENGINE </span><span>=</span> InnoDB AUTO_INCREMENT <span>=</span> <span>5703</span> <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for jobparameter</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> jobparameter; </span><span>CREATE</span> <span>TABLE</span><span> jobparameter ( Id </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span> AUTO_INCREMENT, JobId </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span>, Name </span><span>varchar</span>(<span>40</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Value longtext </span><span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE, </span><span>UNIQUE</span> <span>INDEX</span><span> IX_JobParameter_JobId_Name(JobId, Name) USING BTREE, </span><span>INDEX</span><span> FK_JobParameter_Job(JobId) USING BTREE, </span><span>CONSTRAINT</span> FK_JobParameter_Job <span>FOREIGN</span> <span>KEY</span> (JobId) <span>REFERENCES</span> job (Id) <span>ON</span> <span>DELETE</span> <span>CASCADE</span> <span>ON</span> <span>UPDATE</span> <span>CASCADE</span><span> ) ENGINE </span><span>=</span> InnoDB AUTO_INCREMENT <span>=</span> <span>17107</span> <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for jobqueue</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> jobqueue; </span><span>CREATE</span> <span>TABLE</span><span> jobqueue ( Id </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span> AUTO_INCREMENT, JobId </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span>, Queue </span><span>varchar</span>(<span>50</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, FetchedAt </span><span>datetime</span> <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, FetchToken </span><span>varchar</span>(<span>36</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE, </span><span>INDEX</span><span> IX_JobQueue_QueueAndFetchedAt(Queue, FetchedAt) USING BTREE ) ENGINE </span><span>=</span> InnoDB AUTO_INCREMENT <span>=</span> <span>5703</span> <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for jobstate</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> jobstate; </span><span>CREATE</span> <span>TABLE</span><span> jobstate ( Id </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span> AUTO_INCREMENT, JobId </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span>, Name </span><span>varchar</span>(<span>20</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Reason </span><span>varchar</span>(<span>100</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, CreatedAt </span><span>datetime</span> <span>NOT</span> <span>NULL</span><span>, Data longtext </span><span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE, </span><span>INDEX</span><span> FK_JobState_Job(JobId) USING BTREE, </span><span>CONSTRAINT</span> FK_JobState_Job <span>FOREIGN</span> <span>KEY</span> (JobId) <span>REFERENCES</span> job (Id) <span>ON</span> <span>DELETE</span> <span>CASCADE</span> <span>ON</span> <span>UPDATE</span> <span>CASCADE</span><span> ) ENGINE </span><span>=</span> InnoDB AUTO_INCREMENT <span>=</span> <span>1</span> <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for list</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> list; </span><span>CREATE</span> <span>TABLE</span><span> list ( Id </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span> AUTO_INCREMENT, </span><span>Key</span> <span>varchar</span>(<span>100</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Value longtext </span><span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NULL</span><span>, ExpireAt </span><span>datetime</span> <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE ) ENGINE </span><span>=</span> InnoDB AUTO_INCREMENT <span>=</span> <span>1</span> <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for server</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> server; </span><span>CREATE</span> <span>TABLE</span><span> server ( Id </span><span>varchar</span>(<span>100</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Data longtext </span><span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, LastHeartbeat </span><span>datetime</span> <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE ) ENGINE </span><span>=</span> InnoDB <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for set</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span> <span>set</span><span>; </span><span>CREATE</span> <span>TABLE</span> <span>set</span><span> ( Id </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span> AUTO_INCREMENT, </span><span>Key</span> <span>varchar</span>(<span>100</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Value </span><span>varchar</span>(<span>256</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Score </span><span>float</span> <span>NOT</span> <span>NULL</span><span>, ExpireAt </span><span>datetime</span> <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE, </span><span>UNIQUE</span> <span>INDEX</span> IX_Set_Key_Value(<span>Key</span><span>, Value) USING BTREE ) ENGINE </span><span>=</span> InnoDB AUTO_INCREMENT <span>=</span> <span>95</span> <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>--</span><span> ----------------------------</span><span> --</span><span> Table structure for state</span><span> --</span><span> ----------------------------</span> <span>DROP</span> <span>TABLE</span> <span>IF</span> <span>EXISTS</span><span> state; </span><span>CREATE</span> <span>TABLE</span><span> state ( Id </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span> AUTO_INCREMENT, JobId </span><span>int</span>(<span>11</span>) <span>NOT</span> <span>NULL</span><span>, Name </span><span>varchar</span>(<span>20</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NOT</span> <span>NULL</span><span>, Reason </span><span>varchar</span>(<span>100</span>) <span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NULL</span> <span>DEFAULT</span> <span>NULL</span><span>, CreatedAt </span><span>datetime</span> <span>NOT</span> <span>NULL</span><span>, Data longtext </span><span>CHARACTER</span> <span>SET</span> latin1 COLLATE latin1_swedish_ci <span>NULL</span><span>, </span><span>PRIMARY</span> <span>KEY</span><span> (Id) USING BTREE, </span><span>INDEX</span><span> FK_HangFire_State_Job(JobId) USING BTREE, </span><span>CONSTRAINT</span> FK_HangFire_State_Job <span>FOREIGN</span> <span>KEY</span> (JobId) <span>REFERENCES</span> job (Id) <span>ON</span> <span>DELETE</span> <span>CASCADE</span> <span>ON</span> <span>UPDATE</span> <span>CASCADE</span><span> ) ENGINE </span><span>=</span> InnoDB AUTO_INCREMENT <span>=</span> <span>17108</span> <span>CHARACTER</span> <span>SET</span> <span>=</span> latin1 COLLATE <span>=</span> latin1_swedish_ci ROW_FORMAT <span>=</span><span> Compact;

</span><span>SET</span> FOREIGN_KEY_CHECKS <span>=</span> <span>1</span>;</pre>

</div> <span>View Code</span></div> <p>&nbsp;</p> <p>redis 配置(Startup.cs 文件里的 & nbsp;ConfigureServices)</p> <div><div><span><a href="javascript:void (0);" onclick="copyCnblogsCode (this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div> <pre> <span>//</span><span> 注入 Hangfire 服务 redis</span> <span>var</span> hangfireRedisConnStr = Configuration [<span>"</span><span>AppSettings:HangfireRedisConnectionString</span><span>"</span><span>]; _redis </span>=<span> ConnectionMultiplexer.Connect (hangfireRedisConnStr); </span><span>var</span> prefix = Configuration [<span>"</span><span>AppSettings:HangfireRedisPrefixName</span><span>"</span><span>]; </span><span>var</span> option = <span>new</span><span> RedisStorageOptions { Prefix </span>=<span> prefix, Db </span>= <span>0</span><span>, }; </span><span>if</span> (<span>int</span>.TryParse (Configuration [<span>"</span><span>AppSettings:HangfireRedisDefaultDatabse</span><span>"</span>], <span>out</span> <span>int</span><span> dfaultdb)) { </span><span>if</span> (dfaultdb &gt;= <span>0</span> &amp;&amp; dfaultdb &lt;= <span>15</span><span>) { option.Db </span>=<span> dfaultdb; } } services.AddHangfire (config </span>=&gt; config.UseRedisStorage (_redis, option));</pre> <div><span><a href="javascript:void (0);" onclick="copyCnblogsCode (this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div> <p>&nbsp;</p> <p > 配置文件 </p> <divonclick="cnblogs_code_show (''b0255353-f8b2-498a-8302-ae81d63bed66'')"><img id="code_img_closed_b0255353-f8b2-498a-8302-ae81d63bed66"src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code_img_opened_b0255353-f8b2-498a-8302-ae81d63bed66"onclick="cnblogs_code_hide (''b0255353-f8b2-498a-8302-ae81d63bed66'',event)"src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif"alt=""> <div id="cnblogs_code_open_b0255353-f8b2-498a-8302-ae81d63bed66"> <pre><span>{ </span><span>"</span><span>Logging</span><span>"</span><span>: { </span><span>"</span><span>IncludeScopes</span><span>"</span>: <span>false</span><span>, </span><span>"</span><span>Debug</span><span>"</span><span>: { </span><span>"</span><span>LogLevel</span><span>"</span><span>: { </span><span>"</span><span>Default</span><span>"</span>: <span>"</span><span>Warning</span><span>"</span><span> } }, </span><span>"</span><span>Console</span><span>"</span><span>: { </span><span>"</span><span>LogLevel</span><span>"</span><span>: { </span><span>"</span><span>Default</span><span>"</span>: <span>"</span><span>Warning</span><span>"</span><span> } } }, </span><span>"</span><span>AppSettings</span><span>"</span><span>: { </span><span>"</span><span>HangfireRedisConnectionString</span><span>"</span>: <span>"</span><span>127.0.0.1:6379,password=,defaultDatabase=1,ssl=false,writeBuffer=10240</span><span>"</span><span>, </span><span>"</span><span>HangfireRedisPrefixName</span><span>"</span>: <span>"</span><span>{hangfire_muniumini}:</span><span>"</span><span>, </span><span>"</span><span>HangfireRedisDefaultDatabse</span><span>"</span>: <span>"</span><span>1</span><span>"</span><span>, </span><span>"</span><span>RedisConnectionString</span><span>"</span>: <span>"</span><span>127.0.0.1:6379,password=,defaultDatabase=1,poolsize=50,ssl=false,writeBuffer=10240</span><span>"</span><span>, </span><span>"</span><span>UseSwagger</span><span>"</span>: <span>"</span><span>true</span><span>"</span><span> }, </span><span>"</span><span>AllowedHosts</span><span>"</span>: <span>"</span><span>*</span><span>"</span><span> }</span></pre> </div> <span>View Code</span></div> <p>&nbsp;</p> <p > 其中 AppSetting 里的配置说明 </p> <p>HangfireRedisConnectionString(连接字符串,格式参考上面的配置文件)</p> <p>HangfireRedisPrefixName(redis 的字典集名称)</p> <p>HangfireRedisDefaultDatabse(redis 默认数据库序号)</p> <p>&nbsp;</p> <p>&nbsp; 搞定 </p> <p>&nbsp;</p></div>

.net Core HangFire 设置 Cron 时间

.net Core HangFire 设置 Cron 时间

如何解决.net Core HangFire 设置 Cron 时间

嗨,我必须使用 hangfire cron,但我无法理解设置 cron 时间(“***** 是我知道的每一秒”)

我也搜索了一些关于此的网站,但是当我设置某个日期时,我复制并粘贴了另一个站点,并且站点显示错误类型。

Hangfire 提供了一些简单的方法来设置时间,例如

  1. Cron.Daily //work on every day 00.00
  2. Cron.HourInterval

我必须每天 14 点工作一个函数,我怎样才能让它像 Cron.Daily(14) 或其他。谢谢

我真的不确定。一个站点可以使用 genareta cron 并提供此

每个工作日 14:00 工作

  1. 0 0 14 ? * MON-FRI *

其他不接受,它接受这个

  1. 0 14 * * *

哪个适用于hangfire

解决方法

如果您希望在工作日下午 2 点运行作业,

  1. "0 14 * * MON,TUE,WED,THU,FRI"
  2. "0 14 * * MON-FRI"
  3. "0 14 * * 1,2,3,4,5"
  4. "0 14 * * 1-5"

如果你想每天都做这项工作,

  1. "0 14 * * SUN–SAT"

Documentation 关于 Hangfire 使用的 cron 表达式。

Cron

Hangfire 确实有它自己的 Cron 表达式内部表示,但不像工作日那样给你那么多的控制权......但它们就在那里。

您可以使用 Hangfire.Core 中的 Cron 类使用以下语法创建重复作业。这个循环作业将每天运行 1400 小时。

  1. RecurringJob.AddOrUpdate("name",() => Test(),Cron.Daily(14));

您还可以使用各种其他可用的 Cron 方法,包括 Weekly(),Monthly(),Yearly 等。所有这些方法的定义都可以是 found here

.NET Core 下开源任务调度框架 Hangfire 的 Api 任务拓展 (支持秒级任务)

.NET Core 下开源任务调度框架 Hangfire 的 Api 任务拓展 (支持秒级任务)

HangFire 的拓展和使用

看了很多博客,小白第一次写博客。

最近由于之前的任务调度框架总出现问题,因此想寻找一个替代品,之前使用的是 Quartz.Net,这个框架方便之处就是支持 cron 表达式适合复杂日期场景使用,以及秒级任务。但是配置比较复杂,而且管理不方便,自己开发了个 web 管理页面,不过这个需要额外的单独线程去统一管理工作状态,很容易出现问题。

有考虑过 “FluentScheduler” ,使用简单,但是管理配置也很麻烦,我希望能做到配置简单,管理方便,高性能。最后想到了以前听过的 hangfire,它的好处就是自带控制面板,在园子里看了很多相关资料,偶然发现了有人拓展过 hangfire 通过调用 api 接口来执行任务,这种方式可以避免依赖本地代码,方便部署,在此基础上,我用空闲时间拓展了一下现在已经基本可以满足需求。

 

所拓展的功能全部属于外部拓展,因此 hangfire 版本可以一直更新,现在已经更新最新版,支持秒级任务

gitHub 地址

 

由于更新到最新版 hangfire 1.7 支持秒级任务,使用的在线表达式生成部分表达式有问题,注掉了秒级任务表达式生成,有时间需要详细测试更改,可以参考 (hangfire 官方提供的表达式)

现在已经实现的功能有:

1,部署及调试:只需要配置数据库连接,然后编译即可运行,无需建表,支持(redis,mysql, sqlserver)其他数据库暂时用不到没测试。推荐使用 redis 集群。项目中直接添加了 redis 的存储包,已经更新 StackExchange.Redis 到最新版本方便拓展,调试时可以直接调试。部署,只需要发布项目,运行创建 windows 服务的 bat 命令,命令已经包含在项目中,或者发布至 Linux。

 

2,周期任务:支持在控制面板页面上添加周期任务,编辑周期任务,删除周期任务,手动触发周期任务,暂停和继续周期任务 (暂停实现的原理是通过 set 中添加属性,在 job 执行前,过滤掉,直接跳过执行,因为 hangfire 中 job 一旦创建就失去了控制权,只能通过过滤器去拦截),任务暂停后会查询状态并渲染面板列表为红色字体方便查找哪个任务被暂停。

3,计划任务在作业选项卡中,计划作业中可以实现添加计划任务,计划任务可以使任务在指定的分钟后执行,只执行一次。

 

4,只读面板通过配置的用户名密码,使用户只具有读取面板的权限,这样可以防止误操作

 

 

 1  //只读面板,只能读取不能操作
 2             app.UseHangfireDashboard("/job-read", new DashboardOptions
 3             {
 4                 AppPath = "#",//返回时跳转的地址
 5                 DisplayStorageConnectionString = false,//是否显示数据库连接信息
 6                 IsReadOnlyFunc = Context =>
 7                 {
 8                     return true;
 9                 },
10                 Authorization = new[] { new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions
11                 {
12                     RequireSsl = false,//是否启用ssl验证,即https
13                     SslRedirect = false,
14                     LoginCaseSensitive = true,
15                     Users = new []
16                     {                    
17                         new BasicAuthAuthorizationUser
18                         {
19                             Login = "read",
20                             PasswordClear = "only"
21                         },
22                         new BasicAuthAuthorizationUser
23                         {
24                             Login = "test",
25                             PasswordClear = "123456"
26                         },
27                         new BasicAuthAuthorizationUser
28                         {
29                             Login = "guest",
30                             PasswordClear = "123@123"
31                         }
32                     }
33                 })
34                 }
35             });
View Code

 

 

 5,邮件推送:目前使用的方式是,任务错误重试达到指定次数后,发送邮件通知,使用的 MailKit

 1   catch (Exception ex)
 2             {
 3                 //获取重试次数
 4                 var count = context.GetJobParameter<string>("RetryCount");
 5                 context.SetTextColor(ConsoleTextColor.Red);
 6                 //signalR推送
 7                 //SendRequest(ConfigSettings.Instance.URL+"/api/Publish/EveryOne", "测试");
 8                 if (count == "3")//重试达到三次的时候发邮件通知
 9                 {
10                     SendEmail(item.JobName, item.Url, ex.ToString());
11                 }
12                 logger.Error(ex, "HttpJob.Excute");
13                 context.WriteLine($"执行出错:{ex.Message}");
14                 throw;//不抛异常不会执行重试操作
15             }
View Code
 1 /// <summary>
 2         /// 邮件模板
 3         /// </summary>
 4         /// <param name="jobname"></param>
 5         /// <param name="url"></param>
 6         /// <param name="exception"></param>
 7         /// <returns></returns>
 8         private static string SethtmlBody(string jobname, string url, string exception)
 9         {
10             var htmlbody = $@"<h3 align=''center''>{HangfireHttpJobOptions.SMTPSubject}</h3>
11                             <h3>执行时间:</h3>
12                             <p>
13                                 {DateTime.Now}
14                             </p>
15                             <h3>
16                                 任务名称:<span> {jobname} </span><br/>
17                             </h3>
18                             <h3>
19                                 请求路径:{url}
20                             </h3>
21                             <h3><span></span> 
22                                 执行结果:<br/>
23                             </h3>
24                             <p>
25                                 {exception}
26                             </p> ";
27             return htmlbody;
28         }
邮件模板
 1  //使用redis
 2                         config.UseRedisStorage(Redis, new Hangfire.Redis.RedisStorageOptions()
 3                         {
 4                             FetchTimeout=TimeSpan.FromMinutes(5),
 5                             Prefix = "{hangfire}:",
 6                             //活动服务器超时时间
 7                             InvisibilityTimeout = TimeSpan.FromHours(1),
 8                             //任务过期检查频率
 9                             ExpiryCheckInterval = TimeSpan.FromHours(1),
10                             DeletedListSize = 10000,
11                             SucceededListSize = 10000
12                         })
13                         .UseHangfireHttpJob(new HangfireHttpJobOptions()
14                         {
15                             SendToMailList = HangfireSettings.Instance.SendMailList,
16                             SendMailAddress = HangfireSettings.Instance.SendMailAddress,
17                             SMTPServerAddress = HangfireSettings.Instance.SMTPServerAddress,
18                             SMTPPort = HangfireSettings.Instance.SMTPPort,
19                             SMTPPwd = HangfireSettings.Instance.SMTPPwd,
20                             SMTPSubject = HangfireSettings.Instance.SMTPSubject
21                         })
配置邮件参数

 

6,signalR 推送:宿主程序使用的 weapi,因此可以通过 webapi 推送,这样做的好处是可以将服务当作推送服务使用,第三方接口也可以利用此来推送,

 

 1  /// <summary>
 2        ///用户加入组处理
 3        /// </summary>
 4        /// <param name="userid">用户唯一标识</param>
 5        /// <param name="GroupName">组名称</param>
 6        /// <returns></returns>
 7         public Task InitUsers(string userid,string GroupName)
 8         {
 9             Console.WriteLine($"{userid}加入用户组");
10             Groups.AddToGroupAsync(Context.ConnectionId, GroupName);
11             SignalrGroups.UserGroups.Add(new SignalrGroups()
12             {
13                 ConnectionId = Context.ConnectionId,
14                 GroupName = GroupName,
15                 UserId = userid
16             });
17             return Clients.All.SendAsync("UserJoin", "用户组数据更新,新增id为:" + Context.ConnectionId + " pid:" + userid);
18         }
19         /// <summary>
20         /// 断线的时候处理
21         /// </summary>
22         /// <param name="exception"></param>
23         /// <returns></returns>
24         public override Task OnDisconnectedAsync(Exception exception)
25         {
26             //掉线移除用户,不给其推送
27             var user = SignalrGroups.UserGroups.FirstOrDefault(c => c.ConnectionId == Context.ConnectionId);
28 
29             if (user != null)
30             {
31                 Console.WriteLine($"用户:{user.UserId}已离线");
32                 SignalrGroups.UserGroups.Remove(user);
33                 Groups.RemoveFromGroupAsync(Context.ConnectionId, user.GroupName);
34             }
35             return base.OnDisconnectedAsync(exception);
36         }
Hub 定义
 1  /// <summary>
 2         /// 单个connectionid推送
 3         /// </summary>
 4         /// <param name="groups"></param>
 5         /// <returns></returns>
 6         [HttpPost, Route("AnyOne")]
 7         public IActionResult AnyOne([FromBody]IEnumerable<SignalrGroups> groups)
 8         {
 9             if (groups != null && groups.Any())
10             {
11                 var ids = groups.Select(c => c.UserId);
12                 var list = SignalrGroups.UserGroups.Where(c => ids.Contains(c.UserId));
13                 foreach (var item in list)
14                     hubContext.Clients.Client(item.ConnectionId).SendAsync("AnyOne", $"{item.ConnectionId}: {item.Content}");
15             }
16             return Ok();
17         }
18 
19         /// <summary>
20         /// 全部推送
21         /// </summary>
22         /// <param name="message"></param>
23         /// <returns></returns>
24         [HttpPost, Route("EveryOne")]
25         public IActionResult EveryOne([FromBody] MSG body)
26         {
27             var data = HttpContext.Response.Body;
28             hubContext.Clients.All.SendAsync("EveryOne", $"{body.message}");
29             return Ok();
30         }
31 
32         /// <summary>
33         /// 单个组推送
34         /// </summary>
35         /// <param name="group"></param>
36         /// <returns></returns>
37         [HttpPost, Route("AnyGroups")]
38         public IActionResult AnyGroups([FromBody]SignalrGroups group)
39         {
40             if (group != null)
41             {
42                 hubContext.Clients.Group(group.GroupName).SendAsync("AnyGroups", $"{group.Content}");
43             }
44             return Ok();
45         }
推送接口定义

 

7,接口健康检查:因为主要用来调用 api 接口,因此集成接口健康检查还是很有必要的,目前使用的方式是配置文件中添加需要检查的地址

 1 /*健康检查配置项*/
 2   "HealthChecks-UI": {
 3     /*检查地址,可以配置当前程序和外部程序*/
 4     "HealthChecks": [
 5       {
 6         "Name": "Hangfire Api 健康检查",
 7         "Uri": "http://localhost:9006/healthz"
 8       }
 9     ],
10     /*需要检查的Api地址*/
11     "CheckUrls": [
12       {
13         "Uri": "http://localhost:17600/CityService.svc/HealthyCheck",
14         "httpMethod": "Get"
15       },
16       {
17         "Uri": "http://localhost:9098/CheckHelath",
18         "httpMethod": "Post"
19       },
20       {
21         "Uri": "http://localhost:9067/GrtHelathCheck",
22         "httpMethod": "Get"
23       },
24       {
25         "Uri": "http://localhost:9043/GrtHelathCheck",
26         "httpMethod": "Get"
27       }
28     ],
29     "Webhooks": [], //钩子配置
30     "EvaluationTimeOnSeconds": 10, //检测频率
31     "MinimumSecondsBetweenFailureNotifications": 60, //推送间隔时间
32     "HealthCheckDatabaseConnectionString": "Data Source=\\healthchecksdb" //-> sqlite库存储检查配置及日志信息
33   }
健康检查相关配置

后台会根据配置的指定间隔去检查服务接口是否可以正常访问,(这个中间件可以实现很多检查功能,包括网络,数据库,mq 等,支持 webhook 推送等丰富功能,系统用不到因此没有添加)

健康检查的配置

1  //添加健康检查地址
2             HangfireSettings.Instance.HostServers.ForEach(s =>
3             {
4                 services.AddHealthChecks().AddUrlGroup(new Uri(s.Uri), s.httpMethod.ToLower() == "post" ? HttpMethod.Post : HttpMethod.Get, $"{s.Uri}");
5             });
健康检查地址添加
 1  app.UseHealthChecks("/healthz", new HealthCheckOptions()
 2             {
 3                 Predicate = _ => true,
 4                 ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
 5             });
 6             app.UseHealthChecks("/health", options);//获取自定义格式的json数据
 7             app.UseHealthChecksUI(setup =>
 8             {
 9                 setup.UIPath = "/hc"; // 健康检查的UI面板地址
10                 setup.ApiPath = "/hc-api"; // 用于api获取json的检查数据
11             });
健康检查中间件配置

其中,ui 配置路径是在面板中展示检查结果需要使用的地址

api 地址,可以通过接口的方式来调用检查结果,方便在第三方系统中使用,其数据格式可以自定义

 通过接口调用

 1 [{
 2     "id": 1,
 3     "status": "Unhealthy",
 4     "onStateFrom": "2019-04-07T18:00:09.6996751+08:00",
 5     "lastExecuted": "2019-04-07T18:05:03.4761739+08:00",
 6     "uri": "http://localhost:53583/healthz",
 7     "name": "Hangfire Api 健康检查",
 8     "discoveryService": null,
 9     "entries": [{
10         "id": 1,
11         "name": "http://localhost:17600/CityService.svc/HealthyCheck",
12         "status": "Unhealthy",
13         "description": "An error occurred while sending the request.",
14         "duration": "00:00:04.3907375"
15     }, {
16         "id": 2,
17         "name": "http://localhost:9098/CheckHelath",
18         "status": "Unhealthy",
19         "description": "An error occurred while sending the request.",
20         "duration": "00:00:04.4140310"
21     }, {
22         "id": 3,
23         "name": "http://localhost:9067/GrtHelathCheck",
24         "status": "Unhealthy",
25         "description": "An error occurred while sending the request.",
26         "duration": "00:00:04.3847367"
27     }, {
28         "id": 4,
29         "name": "http://localhost:9043/GrtHelathCheck",
30         "status": "Unhealthy",
31         "description": "An error occurred while sending the request.",
32         "duration": "00:00:04.4499007"
33     }],
34     "history": []
35 }]
接口返回数据原始格式
 1 {
 2     "status": "Unhealthy",
 3     "errors": [{
 4         "key": "http://localhost:17600/CityService.svc/HealthyCheck",
 5         "value": "Unhealthy"
 6     }, {
 7         "key": "http://localhost:9098/CheckHelath",
 8         "value": "Unhealthy"
 9     }, {
10         "key": "http://localhost:9067/GrtHelathCheck",
11         "value": "Unhealthy"
12     }, {
13         "key": "http://localhost:9043/GrtHelathCheck",
14         "value": "Unhealthy"
15     }]
16 }
接口返回数据处理后格式
 1  //重写json报告数据,可用于远程调用获取健康检查结果
 2             var options = new HealthCheckOptions
 3             {
 4                 ResponseWriter = async (c, r) =>
 5                 {
 6                     c.Response.ContentType = "application/json";
 7 
 8                     var result = JsonConvert.SerializeObject(new
 9                     {
10                         status = r.Status.ToString(),
11                         errors = r.Entries.Select(e => new { key = e.Key, value = e.Value.Status.ToString() })
12                     });
13                     await c.Response.WriteAsync(result);
14                 }
15             };
处理方式

 

8,通过接口添加任务:添加编辑周期任务,添加计划任务,触发周期任务,删除周期任务,多个任务连续一次执行的任务

  1  /// <summary>
  2         /// 添加一个队列任务立即被执行
  3         /// </summary>
  4         /// <param name="httpJob"></param>
  5         /// <returns></returns>
  6         [HttpPost, Route("AddBackGroundJob")]
  7         public JsonResult AddBackGroundJob([FromBody] Hangfire.HttpJob.Server.HttpJobItem httpJob)
  8         {
  9             var addreslut = string.Empty;
 10             try
 11             {
 12                 addreslut = BackgroundJob.Enqueue(() => Hangfire.HttpJob.Server.HttpJob.Excute(httpJob, httpJob.JobName, null));
 13             }
 14             catch (Exception ec)
 15             {
 16                 return Json(new Message() { Code = false, ErrorMessage = ec.ToString() });
 17             }
 18             return Json(new Message() { Code = true, ErrorMessage = "" });
 19         }
 20 
 21         /// <summary>
 22         /// 添加一个周期任务
 23         /// </summary>
 24         /// <param name="httpJob"></param>
 25         /// <returns></returns>
 26         [HttpPost, Route("AddOrUpdateRecurringJob")]
 27         public JsonResult AddOrUpdateRecurringJob([FromBody] Hangfire.HttpJob.Server.HttpJobItem httpJob)
 28         {
 29             try
 30             {
 31                 RecurringJob.AddOrUpdate(httpJob.JobName, () => Hangfire.HttpJob.Server.HttpJob.Excute(httpJob, httpJob.JobName, null), httpJob.Corn, TimeZoneInfo.Local);
 32             }
 33             catch (Exception ec)
 34             {
 35                 return Json(new Message() { Code = false, ErrorMessage = ec.ToString() });
 36             }
 37             return Json(new Message() { Code = true, ErrorMessage = "" });
 38         }
 39 
 40         /// <summary>
 41         /// 删除一个周期任务
 42         /// </summary>
 43         /// <param name="jobname"></param>
 44         /// <returns></returns>
 45         [HttpGet,Route("DeleteJob")]
 46         public JsonResult DeleteJob(string jobname)
 47         {
 48             try
 49             {
 50                 RecurringJob.RemoveIfExists(jobname);
 51             }
 52             catch (Exception ec)
 53             {
 54                 return Json(new Message() { Code = false, ErrorMessage = ec.ToString() });
 55             }
 56             return Json(new Message() { Code = true, ErrorMessage = "" });
 57         }
 58         /// <summary>
 59         /// 手动触发一个任务
 60         /// </summary>
 61         /// <param name="jobname"></param>
 62         /// <returns></returns>
 63         [HttpGet, Route("TriggerRecurringJob")]
 64         public JsonResult TriggerRecurringJob(string jobname)
 65         {
 66             try
 67             {
 68                 RecurringJob.Trigger(jobname);
 69             }
 70             catch (Exception ec)
 71             {
 72                 return Json(new Message() { Code = false, ErrorMessage = ec.ToString() });
 73             }
 74             return Json(new Message() { Code = true, ErrorMessage = "" });
 75         }
 76         /// <summary>
 77         /// 添加一个延迟任务
 78         /// </summary>
 79         /// <param name="httpJob">httpJob.DelayFromMinutes(延迟多少分钟执行)</param>
 80         /// <returns></returns>
 81         [HttpPost, Route("AddScheduleJob")]
 82         public JsonResult AddScheduleJob([FromBody] Hangfire.HttpJob.Server.HttpJobItem httpJob)
 83         {
 84             var reslut = string.Empty;
 85             try
 86             {
 87                 reslut = BackgroundJob.Schedule(() => Hangfire.HttpJob.Server.HttpJob.Excute(httpJob, httpJob.JobName, null), TimeSpan.FromMinutes(httpJob.DelayFromMinutes));
 88             }
 89             catch (Exception ec)
 90             {
 91                 return Json(new Message() { Code = false, ErrorMessage = ec.ToString() });
 92             }
 93             return Json(new Message() { Code = true, ErrorMessage = "" });
 94         }
 95         /// <summary>
 96         /// 添加连续任务,多个任务依次执行,只执行一次
 97         /// </summary>
 98         /// <param name="httpJob"></param>
 99         /// <returns></returns>
100         [HttpPost, Route("AddContinueJob")]
101         public JsonResult AddContinueJob([FromBody] List<Hangfire.HttpJob.Server.HttpJobItem> httpJobItems)
102         {
103             var reslut = string.Empty;
104             var jobid = string.Empty;
105             try
106             {
107                 httpJobItems.ForEach(k =>
108                 {
109                     if (!string.IsNullOrEmpty(jobid))
110                     {
111                         jobid = BackgroundJob.ContinueJobWith(jobid, () => RunContinueJob(k));
112                     }
113                     else
114                     {
115                         jobid = BackgroundJob.Enqueue(() => Hangfire.HttpJob.Server.HttpJob.Excute(k, k.JobName, null));
116                     }
117                 });
118                 reslut = "true";
119             }
120             catch (Exception ec)
121             {
122                 return Json(new Message() { Code = false, ErrorMessage = ec.ToString() });
123             }
124             return Json(new Message() { Code = true, ErrorMessage = "" });
125         }
通过接口添加任务

这样做的好处是有效利用了宿主的 webapi,而且无需登录控制面板操作就能实现任务管理,方便集成管理到其他系统中

 

防止多个实例的任务并行执行,即一个任务未执行完成,另一个相同的任务开始执行,可以使用分布式锁来解决

 通过特性来添加任务重试时间间隔 (hangfire 1.7 新增,单位 / 秒),重试次数,队列名称,任务名称,以及分布式锁超时时间

 1 /// <summary>
 2         /// 执行任务,DelaysInSeconds(重试时间间隔/单位秒)
 3         /// </summary>
 4         /// <param name="item"></param>
 5         /// <param name="jobName"></param>
 6         /// <param name="context"></param>
 7         [AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 30, 60, 90 }, LogEvents = true, OnAttemptsExceeded = AttemptsExceededAction.Fail)]
 8         [DisplayName("Api任务:{1}")]
 9         [Queue("apis")]
10         [JobFilter(timeoutInSeconds: 3600)]
配置分布式锁超时时间

 

 1 //设置分布式锁,分布式锁会阻止两个相同的任务并发执行,用任务名称和方法名称作为锁
 2             var jobresource = $"{filterContext.BackgroundJob.Job.Args[1]}.{filterContext.BackgroundJob.Job.Method.Name}";
 3             var locktimeout = TimeSpan.FromSeconds(_timeoutInSeconds);
 4             try
 5             {
 6                 //判断任务是否被暂停
 7                 using (var connection = JobStorage.Current.GetConnection())
 8                 {
 9                     var conts = connection.GetAllItemsFromSet($"JobPauseOf:{filterContext.BackgroundJob.Job.Args[1]}");
10                     if (conts.Contains("true"))
11                     {
12                         filterContext.Canceled = true;//任务被暂停不执行直接跳过
13                         return;
14                     }
15                 }
16                 //申请分布式锁
17                 var distributedLock = filterContext.Connection.AcquireDistributedLock(jobresource, locktimeout);
18                 filterContext.Items["DistributedLock"] = distributedLock;
19             }
20             catch (Exception ec)
21             {
22                 //获取锁超时,取消任务,任务会默认置为成功
23                 filterContext.Canceled = true;
24                 logger.Info($"任务{filterContext.BackgroundJob.Job.Args[1]}超时,任务id{filterContext.BackgroundJob.Id}");
25             }
过滤器添加分布式锁

 

1  if (!filterContext.Items.ContainsKey("DistributedLock"))
2             {
3                 throw new InvalidOperationException("找不到分布式锁,没有为该任务申请分布式锁.");
4             }
5             //释放分布式锁
6             var distributedLock = (IDisposable)filterContext.Items["DistributedLock"];
7             distributedLock.Dispose();
释放分布式锁

 

通过过滤器来设置任务过期时间,过期后自动在数据库删除历史记录

 

1 public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
2         {
3             //设置过期时间,任务将在三天后过期,过期的任务会自动被扫描并删除
4             context.JobExpirationTimeout = TimeSpan.FromDays(3);
5         }
设置任务过期时间

 

redis 集群下,测试秒级任务

集群为 windws 环境下,一个主节点四个从节点,(使用时需要在 redis 连接中配置全部集群连接,主节点和从节点),目前用不到 linux 环境,没有进行测试。

.net core 之 Hangfire 任务调度

.net core 之 Hangfire 任务调度

Hangfire 可用作任务调度,类似延迟任务、队列任务、批量任务和定时任务等。

一、nuget Hangfire 包

找到 Hangfire.AspNetCore 和 Hangfire.SqlServer 包,可以在 nuget 面板中找到或直接像如下方式安装:

Install Package Hangfire.AspNetCore

Install Package Hangfire.SqlServer

 

二、在 Startup.cs 文件中加入以下代码:

写一个 Hangfire 的扩展类,如下所示:

public static class HangFireModule
    {
        public static IApplicationBuilder UseHangfire(this IApplicationBuilder app)
        {
            app.UseHangfireServer();
            app.UseHangfireDashboard();

            return app;
        }

        public static IServiceCollection AddHangfire(this IServiceCollection services)
        {
            services.AddHangfire(x => x.UseSqlServerStorage(ConfigurationSetting.Configuration["ConnectionString:MyDb_Hangfire"]));
            return services;
        }
    }

在 Startup 的 ConfigureServices 方法中,加入 services.AddHangfire ();

在 Startup 的 Configure 方法中,加入 app.UseHangfire ();

 

三、自动加入任务

1. 在当前模块中,新创建一个名为 IHangfireTask 的接口,将其注册为瞬时生命周期服务。我们将要实现的是,从此以后,继承该接口的类型都会被自动加入到任务调度中。

/// <summary>
    ///  Hangfire任务接口,任何继承该接口的都会加入到Hangfire任务
    /// </summary>
    public interface IHangfireTask : ITransient {
        /// <summary>
        /// 运行hangfire任务
        /// </summary>
        void Run();
    }

ITransient 接口的作用就是,将所有继承 ITransient 的接口和类型注册为瞬时生命周期的服务。

2. 在 HangFireModule 中新增一个 UseHangfireTask 扩展方法,目的是在 Startup 的 Configure 方法中使用,在程序启动的时候就执行该方法。

//运行Hangfire任务
        public static void UseHangfireTask(this IApplicationBuilder app) {
            //找到继承了IHangfireTask接口的实例
            IList<IHangfireTask> list = ServiceLocator.Services.GetServices<IHangfireTask>()?.ToList();
            if (list?.Count > 0) {
                foreach (var item in list) {
                    item.Run();
                }
            }
        }

加入到 Startup 的 Configure 方法中:

public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider serviceProvider)
        {
            ServiceLocator.SetServices(serviceProvider);
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();             
            }
            else
            {
                app.UseExceptionHandler();
            }
        
             app.UseMvc();        

            app.UseSwagger();
            app.UseSwaggerUi();

            app.UseHangfire(); 
            app.UseHangfireTask();            
            
        }

如此这样,我们就做到了在程序刚启动时,程序就会自动去找到继承自 IHangfireTask 接口的所有类型并运行它们的 Run () 方法。

我们可以在 Run () 方法中实现诸如 Hangfire 的 BackgroundJob 或 RecurringJob 等任务。

接下来我们就用代码来实现一下!

我们现在想实现一个定时任务,则看如下代码:

public class RecurringTask:IHangfireTask
   {
       private log4net.ILog log => LogHelper.Log4NetInstance.LogFactory(typeof(RecurringTask));

       public void RunTask()
       {
            log.Info("每分钟执行一次定时任务");
       }
     
        public void Run()
        {
            RecurringJob.AddOrUpdate(()=> RunTask(), "* * * * *");
        }
    }

这里有一个地方需要注意,在使用 RecurringJob 或 BackgroundJob 的时候,就例如 ()=> RunTask() 这个,RunTask 方法的访问修饰符必须是 public如果有必要,尽量在 RunTask 方法内被调用的其他方法的访问修饰符也定义为 public。

在 Hangfire 中,Cron 表达式包含五个字段:分钟、小时、日期、月份、周,因此定时任务最低也只能是一分钟,没有秒数可供选择。

接下来我们看看程序运行的结果:

 

四、Hangfire 的基本任务

1. BackgroundJob.Enqueue

作用:将一个任务放入到某个持久化的队列中并挨个执行,以便程序可以继续执行其他代码。该任务只执行一次。

实现:BackgroundJob.Enqueue (() => RunTask ());

2. BackgroundJob.Schedule

作用:将一个任务延迟到某个时间点后执行。

实现:BackgroundJob.Schedule (() => RunTask (), TimeSpan.FromSeconds (30));

即在 30 秒后才执行 RunTask () 方法。

3. RecurringJob.AddOrUpdate

作用:重复执行一个任务,一般用作定时任务处理。

实现:RecurringJob.AddOrUpdate (()=> RunTask (), "* * * * *");

即每分钟都执行一次 RunTask () 方法。

4. BackgroundJob.ContinueWith

作用:类似工作流的形式,将多个任务连接起来按照顺序执行。

实现:

string jobId = BackgroundJob.Schedule (() => Console.WriteLine ("第一个任务"),TimeSpan.FromSeconds (10));
BackgroundJob.ContinueWith(jobId, () => RunTask());

注意:以上 RunTask () 方法的访问修饰符必须是 public

 

.NET Core 开源组件:后台任务利器之 Hangfire

.NET Core 开源组件:后台任务利器之 Hangfire

一。简述

Hangfire 作为一款高人气且容易上手的分布式后台执行服务,支持多种数据库。在.net core 的环境中,由 Core 自带的 DI 管理着生命周期,免去了在 NF4.X 环境中配置 always running 的麻烦,真正做到开箱即用。

二。安装

Hangfie 官方支持是 MsSql 和 redis,除此之外,可供选择的还有 PostgreSql 和 Mongo。
在应用入口项目需要引用 Hangfire.AspNetCore 和特定持久库,比如使用了 MsSql 数据库的 Hangfire.SqlServer。
而在其他项目(比如 bll 层的项目),只需引用基础的 Hangfire.Core 就可以了。

三.Startup 配置

1. 注入 Hnagfire 服务

services.AddHangfire(x => x.UseSqlServerStorage("<connection string>"));

2. 可选配置

启动 Hangfire 服务和对应的 web 面板如下:

 app.UseHangfireServer();//启动Hangfire服务 app.UseHangfireDashboard();//启动hangfire面板

细心的观众可能会发现,这两个方法都有可选参数,可以提供更多的配置。

2.1 配置任务属性

var jobOptions = new BackgroundJobServerOptions { Queues = new[] { "test","default" },//队列名称,只能为小写 WorkerCount = Environment.ProcessorCount * 5, //并发任务数 ServerName="hangfire1",//服务器名称 }; app.UseHangfireServer(jobOptions);

Queues 要处理的队列列表
对于有多个服务器同时连接到数据库,Hangfire 会认为他们是分布式中的一份子。现实中不同服务器往往存在着差异,这个时候就需要合理配置服务器 (应用) 的处理队列,举两个例子:
1. 对于服务器性能差异的处理,有 100 个 A 任务和 50 个 B 任务需要处理,假设 A 服务器的性能是 B 服务器的两倍,如果不配置队列,那么会平分任务给两个服务器。如果我们只让 B 服务器处理 B 任务,而 A 服务器同时处理两种任务,这样 B 就能减少一些压力。

2. 对于服务器能力差异的处理,假设 A 服务器能处理 A 和 B 两种任务,B 服务器只能处理 B 任务(没有处理 A 任务的方法或对象),如果不配置队列,默认会让 B 也执行 A 任务,从而产生错误。反面一想,如果 A 服务器和 B 服务器都有共同的接口,B 服务器不实现接口的方法,发起一个专属于 A 服务器队列的任务,而 A 服务器通过注入实现接口的方法,可以达到传递任务的效果。

WorkerCount 并发任务数,超出并发数将等待之前的任务完成
默认的并发任务数是线程(cpu)的 5 倍,如果 IO 密集型任务多而 CPU 密集型的任务少,可以考虑调高并发任务数。

以上是我用到的,当然还有其他配置参数等着你去开发。

2.2. 配置访问权限

在实际生产中,我们可能不希望任何人都可以访问面板,或暂停执行某些任务,这时就需要重写面板的权限了。默认情况下,只有本地访问权限才能使用 Hangfire 仪表板。所以需要重写控制面板,以便远程访问。

 var options = new DashboardOptions { Authorization = new[] { new HangfireAuthorizationFilter() } }; app.UseHangfireDashboard("/hangfire", options);
public class HangfireAuthorizationFilter : IDashboardAuthorizationFilter { //这里需要配置权限规则 public bool Authorize(DashboardContext context) { return true; } }

三。使用 (官方文档)


任务类型


Fire-and-forget 直接将任务加入到待执行任务队列
Delayed 在当前时间后的某个时间将任务加入到待执行任务队列
Recurring 周期性任务,每一个周期就将任务加入到待执行任务队列
Continuations 顾名思义,继续执行任务

1. 简单入门

using (var connection = JobStorage.Current.GetConnection())
 {
       var storageConnection = connection as JobStorageConnection; if (storageConnection != null) { //立即启动 var jobId = BackgroundJob.Enqueue(()=>Console.WriteLine("Fire-and-forget!")); } }

当然,不仅仅只有静态方法可以执行,Hangfire 的任务也是支持.net core 的依赖注入的,会构造一个对象并执行对应的方法。

BackgroundJob.Enqueue<SomeClass>(i => i.SomeMethod(someParams))

2. 进阶功能

2.1 设置任务队列

[Queue("test")]
public void TestQueue() { }

对于非周期任务,只需要在执行的方法添加 Queue 的特性就能指定该任务让特定的队列服务器处理。
而周期任务,则需要先声明:

RecurringJob.AddOrUpdate(() => Console.WriteLine("Recurring!"),Cron.Daily,queue:"test");

2.2 使用日志过滤器 (点我查看)

Hangfire 支持自定义过滤器,可以对任务在创建时、执行中、执行后等等状态执行特定特定的操作。

//特定方法过滤器
[LogEverything]
public static void Send() { } //全局过滤器 GlobalJobFilters.Filters.Add(new LogEverythingAttribute());

四. 中文文档

hangfire 是一个不错的开源后台任务组件,官方没有中文文档,所以简单地用谷歌机翻修改了一下。

文档在 github 的地址:https://github.com/jonechenug/Hangfire-Chinese-Doc

docker 运行并访问本地 8080 端口:

docker run --restart always  --name hangfire -d -p 8080:80 daocloud.io/koukouge/hangfirezhdoc


本文采用  知识共享署名 - 非商业性使用 - 相同方式共享 3.0 中国大陆许可协议 
转载请注明来源: 张蘅水
 
 
标签:  Hangfire,  后台任务 ,  .NET,  .NET Core

 

 

关于循序渐进学.Net Core Web Api 开发系列【17】:.Net core 自动作业之 Hangfire的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于.net Core HangFire 设置 Cron 时间、.NET Core 下开源任务调度框架 Hangfire 的 Api 任务拓展 (支持秒级任务)、.net core 之 Hangfire 任务调度、.NET Core 开源组件:后台任务利器之 Hangfire等相关内容,可以在本站寻找。

本文标签: