hangfire cron循环任务20分钟后停止,是什么原因

同类项目推荐
相似项目推荐Hangfire项目实践分享
项目中使用Hangfire已经快一年了,期间经历过很多次的试错及升级优化,才达到现在的稳定效果。趁最近不是太忙,自己在github上做了个案列,也是拿来跟大家分享下,案例是从项目里剥离出来的,有兴趣的可以访问 .
什么是Hangfire
是一个开源的.NET任务调度框架,目前1.6+版本已支持.NET Core。个人认为它最大特点在于内置提供集成化的控制台,方便后台查看及监控:
另外,Hangfire包含三大核心组件:客户端、持久化存储、服务端,官方的流程介绍图如下:
从图中可以看出,这三个核心组件是可以分离出来单独部署的,例如可以部署多台Hangfire服务,提高处理后台任务的吞吐量。关于任务持久化存储,支持Sqlserver,MongoDb,Mysql或是Redis等等。
Hangfire基础 基于队列的任务处理(Fire-and-forget jobs)
基于队列的任务处理是Hangfire中最常用的,客户端使用BackgroundJob类的静态方法Enqueue来调用,传入指定的方法(或是匿名函数),Job Queue等参数.
varjobId = BackgroundJob.Enqueue( () =& Console.WriteLine( "Fire-and-forget!"));
在任务被持久化到数据库之后,Hangfire服务端立即从数据库获取相关任务并装载到相应的Job Queue下,在没有异常的情况下仅处理一次,若发生异常,提供重试机制,异常及重试信息都会被记录到数据库中,通过Hangfire控制面板可以查看到这些信息。
延迟任务执行(Delayed jobs)
延迟(计划)任务跟队列任务相似,客户端调用时需要指定在一定时间间隔后调用:
varjobId = BackgroundJob.Schedule( () =& Console.WriteLine( "Delayed!"), TimeSpan.FromDays( 7)); 定时任务执行(Recurring jobs)
定时(循环)任务代表可以重复性执行多次,支持CRON表达式:
RecurringJob.AddOrUpdate( () =& Console.WriteLine( "Recurring!"), Cron.Daily); 延续性任务执行(Continuations)
延续性任务类似于.NET中的Task,可以在第一个任务执行完之后紧接着再次执行另外的任务:
BackgroundJob.ContinueWith( jobId, () =& Console.WriteLine( "Continuation!"));
其实还有批量任务处理,批量任务延续性处理(Batch Continuations),但这个需要商业授权及收费。在我看来,官方提供的开源版本已经基本够用。
与quartz.net对比
在项目没有引入Hangfire之前,一直使用的是Quartz.net。个人认为Quartz.net在定时任务处理方面优势如下:
支持秒级单位的定时任务处理,但是Hangfire只能支持分钟及以上的定时任务处理
原因在于Hangfire用的是开源的组件,跟linux上的crontab指令相似。
更加复杂的触发器,日历以及任务调度处理
可配置的定时任务
但是为什么要换Hangfire? 很大的原因在于项目需要一个后台可监控的应用,不用每次都要从服务器拉取日志查看,在没有ELK的时候相当不方便。Hangfire控制面板不仅提供监控,也可以手动的触发执行定时任务。如果在定时任务处理方面没有很高的要求,比如一定要5s定时执行,Hangfire值得拥有。抛开这些,Hangfire优势太明显了:
持久化保存任务、队列、统计信息
多语言支持
支持任务取消
支持按指定Job Queue处理任务
服务器端工作线程可控,即job执行并发数控制
分布式部署,支持高可用
良好的扩展性,如支持IOC、Hangfire Dashboard授权控制、Asp.net Core、持久化存储等
说了这么多的优点,我们可以有个案例,例如秒杀场景:用户下单-&订单生成-&扣减库存,Hangfire对于这种分布式的应用处理也是适用的,最后会给出实现。
Hangfire扩展
重点说一下上面提到的第8点,Hangfire扩展性,大家可以参考 ,有几个扩展是很实用的.
Hangfire Dashborad日志查看
提供类似于console-like的日志体验,与Hangfire dashboard集成:
用法如下:
publicvoidSimpleJob(PerformContext context){ context.WriteLine($ "{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss ")} SimpleJob Running ..."); varprogressBar = context.WriteProgressBar(); foreach( vari inEnumerable.Range( 1, 50).ToList().WithProgress(progressBar)) { System.Threading.Thread.Sleep( 1000); }}
不仅支持日志输入到控制面板,也支持在线进度条展示.
Hangfire Dashborad授权
这个扩展应该都能理解,给Hangfire Dashboard
提供授权机制,仅授权的用户才能访问。其中提供两种授权机制:
OWIN-based authentication
Basic authentication
可以参考提供,我实现的是基本认证授权:
varoptions = newDashboardOptions{ AppPath = HangfireSettings.Instance.AppWebSite, AuthorizationFilters = new[] { newBasicAuthAuthorizationFilter ( newBasicAuthAuthorizationFilterOptions { SslRedirect = false, RequireSsl = false, LoginCaseSensitive = true, Users = new[] { newBasicAuthAuthorizationUser { Login = HangfireSettings.Instance.LoginUser, // Password as plain textPasswordClear = HangfireSettings.Instance.LoginPwd } } } ) }};app.UseHangfireDashboard( "", options); IOC容器之Autofac
Hangfire对于每一个任务(Job)假如都写在一个类里,然后使用BackgroundJob/RecurringJob对方法(实例或静态)进行调用,这样会导致模块间太多耦合。实际项目中,依赖倒置原则可以降低模块之间的耦合性,Hangfire也提供了IOC扩展,其本质是重写JobActivator类。
是官方提供的开源扩展,用法参考如下:
GlobalConfiguration.Configuration.UseAutofacActivator(container); RecurringJob扩展
关于RecurringJob定时任务,我写了一个扩展 ,在使用上做了一下增强,具体有两点:
使用特性RecurringJobAttribute发现定时任务 publicclassRecurringJobService{ [RecurringJob( "*/1 * * * *")] [DisplayName( "InstanceTestJob")] [Queue( "jobs")] publicvoidInstanceTestJob(PerformContext context) { context.WriteLine($ "{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss ")} InstanceTestJob Running ..."); } [RecurringJob( "*/5 * * * *")] [DisplayName( "JobStaticTest")] [Queue( "jobs")] publicstaticvoidStaticTestJob(PerformContext context) { context.WriteLine($ "{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss ")} StaticTestJob Running ..."); }} 使用json配置文件注册定时任务 [AutomaticRetry(Attempts = 0)][DisableConcurrentExecution( 90)]
publicclassLongRunningJob: IRecurringJob{
publicvoidExecute(PerformContext context) { context.WriteLine($ "{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss ")} LongRunningJob Running ...");
varrunningTimes = context.GetJobData& int&( "RunningTimes"); context.WriteLine($ "get job data parameter-& RunningTimes: {runningTimes}");
varprogressBar = context.WriteProgressBar();
foreach( vari inEnumerable.Range( 1, runningTimes).ToList().WithProgress(progressBar)) { Thread.Sleep( 1000); } }}
Json配置文件如下:
[{"job-name":"Long Running Job","job-type":"Hangfire.Samples.LongRunningJob, Hangfire.Samples","cron-expression":"*/2 * * * *","job-data":{"RunningTimes":300}}]
实现接口IRecurringJob来定义具体的定时任务,这样的写法与Quartz.net相似,可以很方便的实现Quartz.net到Hangfire的迁移。类似地,参考了quartz.net,
使用job-data-map这样的方式来定义整个任务执行期间的上下文有状态的job.
varrunningTimes = context.GetJobData& int&( "RunningTimes");
详细用法可以直接参考项目。
与MSMQ集成
Hangfire server在处理每个job时,会将job先装载到事先定义好的job queue中,比如一次性加载1000个job,在默认的sqlsever实现中是直接将这些job queue中的
job id储存到数据库中,然后再取出执行。大量的job会造成任务的延迟性执行,所以更有效的方式是将任务直接加载到MSMQ中。
实际应用中,MSMQ队列不存在时一定要手工创建,而且必须是事务性的队列,权限也要设置,用法如下:
publicstaticIGlobalConfiguration&SqlServerStorage&
UseMsmq(thisIGlobalConfiguration&SqlServerStorage& configuration, stringpathPattern, paramsstring[] queues){
if( string.IsNullOrEmpty(pathPattern)) thrownewArgumentNullException(nameof(pathPattern));
if(queues == null)
thrownewArgumentNullException(nameof(queues));
foreach( varqueueName inqueues) {
varpath = string.Format(pathPattern, queueName); if(!MessageQueue.Exists(path))
using( varqueue = MessageQueue.Create(path, transactional: true)) queue.SetPermissions( "Everyone", MessageQueueAccessRights.FullControl); } returnconfiguration.UseMsmqQueues(pathPattern, queues);} 持久化存储之Redis
Hangfire中定义的job存储到sqlserver不是性能最好的选择,使用Redis存储,性能将会是巨大提升(下图来源于).
Hangfire.Pro提供了基于servicestack.redis的redis扩展组件,然而商业收费,不开源。
但是,有另外的基于StackExchange.Redis的开源实现 ,
github上一直在维护,支持.NET Core,项目实测稳定可用. 该扩展相当简单:
services.AddHangfire(x =&{ varconnectionString = Configuration.GetConnectionString( "hangfire.redis"); x.UseRedisStorage(connectionString);}); Hangfire最佳实践
配置最大job并发处理数
Hangfire server在启动时会初始化一个最大Job处理并发数量的阈值,系统默认为20,可以根据服务器配置设置并发处理数。最大阈值的定义除了考虑服务器配置以外,
也需要考虑数据库的最大连接数,定义太多的并发处理数量可能会在同一时间耗尽数据连接池。
app.UseHangfireServer( newBackgroundJobServerOptions{ //wait all jobs performed when BackgroundJobServer shutdown.ShutdownTimeout = TimeSpan.FromMinutes( 30), Queues = queues, WorkerCount = Math.Max(Environment.ProcessorCount, 20)}); 使用 DisplayNameAttribute特性构造缺省的JobName publicinterfaceIOrderService: IAppService{ ///&summary&///Creating order from product.///&/summary&///&param name="productId"&&/param&[AutomaticRetry(Attempts = 3)] [DisplayName( "Creating order from product, productId:{0}")] [Queue( "apis")] voidCreateOrder(intproductId);}
目前netstandard暂不支持缺省的jobname,因为需要单独引用组件ponentModel.Primitives,hangfire官方给出的答复是尽量保证少的Hangfire.Core组件的依赖。
Hangfire在调用Background/RecurringJob创建job时应尽量使传入的参数简单.
Hangfire job中参数(包括参数值)及方法名都序列化为json持久化到数据库中,所以参数应尽量简单,如传入单据ID,这样才不会使Job Storage呈爆炸性增长。
为Hangfire客户端调用定义统一的REST APIs
定义统一的REST APIs可以规范并集中管理整个项目的hangfire客户端调用,同时避免到处引用hangfire组件。使用例如Swagger这样的组件来给不同的应用方(Co
///&summary&///Creating order from product.
///&/summary&///&param name="productId"&&/param&
///&returns&&/returns&[Route( "create")][HttpPost]
publicIActionResult Create([FromBody]stringproductId){
if( string.IsNullOrEmpty(productId))
returnBadRequest(); varjobId = BackgroundJob.Enqueue&IOrderService&(x =& x.CreateOrder(productId)); BackgroundJob.ContinueWith&IInventoryService&(jobId, x =& x.Reduce(productId)); returnOk( new{ Status = 1, Message = $ "Enqueued successfully, ProductId-&{productId}"});} 利用Topshelf + Owin Host将hangfire server 宿主到Windows Service.
不推荐将hangfire server 宿主到如ASP.NET appl署到windows service, 利用Topshelf+Owin Host:
///&summary&
///OWIN host
///&/summary&publicclassBootstrap: ServiceControl{
privatestaticreadonlyILog _logger = LogProvider.For&Bootstrap&(); privateIDisposable webA
publicstringAddress { get; set; }
publicboolStart(HostControl hostControl) {
try{ webApp = WebApp.Start&Startup&(Address);
returntrue; }
catch(Exception ex) { _logger.ErrorException( "Topshelf starting occured errors.", ex); returnfalse; } }
publicboolStop(HostControl hostControl)
{ try{ webApp?.Dispose();
returntrue; } catch(Exception ex) { _logger.ErrorException($ "Topshelf stopping occured errors.", ex); returnfalse; } }} 日志配置
从Hangfire 1.3.0开始,Hangfire引入了日志组件,所以应用不需要做任何改动就可以兼容如下日志组件:
EntLib Logging
例如,配置 如下,LibLog组件会自动发现并使用serilog
Log.Logger = newLoggerConfiguration() .MinimumLevel.Verbose() .WriteTo.LiterateConsole() .WriteTo.RollingFile( "logslog-{Date}.txt") .CreateLogger(); Hangfire多实例部署(高可用)
下图是一个多实例Hangfire服务部署:
其中,关于Hangfire Server Node 节点可以根据实际需要水平扩展.
上述提到过一个秒杀场景:用户下单-&订单生成-&扣减库存,实现参考github项目.
HF.Samples.Consumer
服务应用消费方(App/Webservice/Microservices等。)
HF.Samples.APIs
统一的REST APIs管理
HF.Samples.Console
Hangfire 控制面板
HF.Samples.ServerNode
Hangfire server node cli 工具,使用如下:
@echo off setdir= "cluster"dotnetrun - p%dir%HF.Samples.ServerNode nodeA -q order-w 100dotnetrun - p%dir%HF.Samples.ServerNode nodeB -q storage-w 100
上述脚本为创建两个Hangfire server nodeA, nodeB分别用来处理订单、仓储服务。
-q 指定hangfire server 需要处理的队列,-w表示Hangfire server 并发处理job数量。
可以为每个job queue创建一个hangfire实例来处理更多的job.
原文地址: /ecin/p/6201262.html
.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注
责任编辑:
声明:本文由入驻搜狐号的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
今日搜狐热点今年1月31日,在微软的MVP 2015社区大讲堂上,我给大家分享了一个演讲:在ASP.NET应用中执行后台任务。其中介绍了三种技术的应用:QueueBackgroundWorkItem、Hangfire和Azure WebJobs。当时由于时间关系,对三者都是简要介绍。今天想和大家着重介绍下Hangfire。 Hangfire是一个开源且商业免费使用的工具函数库。可以让你非常容易地在ASP.NET应用(也可以不在ASP.NET应用)中执行多种类型的后台任务,而无需自行定制开发和管理基于Windows Service后台任务执行器。且任务信息可以被持久保存。内置提供集成化的控制台。 通过Nuget就可以在你的应用程序中安装Hangfire:Install-Package Hangfire Hangfire的具有如下特性和有点:
支持基于队列的任务处理:任务执行不是同步的,而是放到一个持久化队列中,以便马上把请求控制权返回给调用者。使用方法:BackgroundJob.Enqueue(() =& Console.WriteLine("Simple!")); 延迟任务执行:不是马上调用方法,而是设定一个未来时间点再来执行。使用方法:BackgroundJob.Schedule(() =& Console.WriteLine("Reliable!"), TimeSpan.FromDays(7)); 循环任务执行:只需要简单的一行代码就可以添加重复执行的任务,其内置了常见的时间循环模式,也可以基于CRON表达式来设定复杂的模式。使用方法:RecurringJob.AddOrUpdate(() =& Console.WriteLine("Transparent!"), Cron.Daily); 持久化保存任务、队列、统计信息:默认使用SQL Server,也可以配合消息队列来降低队列处理延迟,或配置使用Redis来获得更好的性能表现 内置自动重试机制:可以设定重试次数,还可以手动在控制台重启任务 除了调用静态方法外还支持实例方法 能够捕获多语言状态:即可以把调用者的Thread.CurrentCulture和Thread.CurrentUICulture信息同任务持久保存在一起,以便任务执行的时候多语言信息是一致的 支持任务取消:使用CancellationToken这样的机制来处理任务取消逻辑 支持IoC容器:目前支持Ninject和Autofac比较常用的开源IoC容器 支持Web集群:可以在一台或多台机器上运行多个Hangfire实例以便实现冗余备份 支持多队列:同一个Hangfire实例可以支持多个队列,以便更好的控制任务的执行方式 并发级别的控制:默认是处理器数量的5倍工作行程,当然也可以自己设定 具备很好的扩展性:有很多扩展点来控制持久存储方式、IoC容器支持等 为什么要使用Hangfire这样的函数库呢?我觉得好处有如下几个方面:
开发简单:无需自己额外做开发,就可以实现任务的队列执行、延迟执行和重复执行 部署简单:可以同主ASP.NET应用部署在一起,测试和维护都相对简单 迁移简单:由于宿主不仅限于ASP.NET,那么未来可以非常容易的把任务执行器放到其他地方(需要改变的就是在其他宿主中启动Hangfire服务器) 扩展简单:由于开源且有很多扩展点,在现有插件都不满足自己需要的情况下能够容易的进行扩展 之前我把Hangfire运用到两种情况下:
后台长时间的科学计算:这样就可以让请求马上返回给客户端,后台完成长时间计算后,用SignalR实时提醒用户 后台群发邮件:通过延迟和循环任务分批通过SendCloud这样的服务发送群发邮件 当然,Hangfire的应用场景还很多,比如在后台处理电商卖家的订单。 MVP 2015社区大讲堂演讲的PPT在此:MVP 2015社区大讲堂之:在ASP.NET应用中执行后台任务
、 、 、 、 、 、执行后台任务的利器&&Hangfire
时间: 23:20:01
&&&& 阅读:134
&&&& 评论:
&&&& 收藏:0
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&今年1月31日,在微软的MVP 2015社区大讲堂上,我给大家分享了一个演讲:在ASP.NET应用中执行后台任务。其中介绍了三种技术的应用:QueueBackgroundWorkItem、Hangfire和Azure WebJobs。当时由于时间关系,对三者都是简要介绍。今天想和大家着重介绍下Hangfire。
Hangfire是一个开源且商业免费使用的工具函数库。可以让你非常容易地在ASP.NET应用(也可以不在ASP.NET应用)中执行多种类型的后台任务,而无需自行定制开发和管理基于Windows Service后台任务执行器。且任务信息可以被持久保存。内置提供集成化的控制台。
通过Nuget就可以在你的应用程序中安装Hangfire:Install-Package Hangfire
Hangfire的具有如下特性和有点:
支持基于队列的任务处理:任务执行不是同步的,而是放到一个持久化队列中,以便马上把请求控制权返回给调用者。使用方法:BackgroundJob.Enqueue(() =& Console.WriteLine("Simple!"));
延迟任务执行:不是马上调用方法,而是设定一个未来时间点再来执行。使用方法:BackgroundJob.Schedule(() =& Console.WriteLine("Reliable!"), TimeSpan.FromDays(7));
循环任务执行:只需要简单的一行代码就可以添加重复执行的任务,其内置了常见的时间循环模式,也可以基于CRON表达式来设定复杂的模式。使用方法:RecurringJob.AddOrUpdate(() =& Console.WriteLine("Transparent!"), Cron.Daily);
持久化保存任务、队列、统计信息:默认使用SQL Server,也可以配合消息队列来降低队列处理延迟,或配置使用Redis来获得更好的性能表现
内置自动重试机制:可以设定重试次数,还可以手动在控制台重启任务
除了调用静态方法外还支持实例方法
能够捕获多语言状态:即可以把调用者的Thread.CurrentCulture和Thread.CurrentUICulture信息同任务持久保存在一起,以便任务执行的时候多语言信息是一致的
支持任务取消:使用CancellationToken这样的机制来处理任务取消逻辑
支持IoC容器:目前支持Ninject和Autofac比较常用的开源IoC容器
支持Web集群:可以在一台或多台机器上运行多个Hangfire实例以便实现冗余备份
支持多队列:同一个Hangfire实例可以支持多个队列,以便更好的控制任务的执行方式
并发级别的控制:默认是处理器数量的5倍工作行程,当然也可以自己设定
具备很好的扩展性:有很多扩展点来控制持久存储方式、IoC容器支持等 为什么要使用Hangfire这样的函数库呢?我觉得好处有如下几个方面:
开发简单:无需自己额外做开发,就可以实现任务的队列执行、延迟执行和重复执行
部署简单:可以同主ASP.NET应用部署在一起,测试和维护都相对简单
迁移简单:由于宿主不仅限于ASP.NET,那么未来可以非常容易的把任务执行器放到其他地方(需要改变的就是在其他宿主中启动Hangfire服务器)
扩展简单:由于开源且有很多扩展点,在现有插件都不满足自己需要的情况下能够容易的进行扩展 之前我把Hangfire运用到两种情况下:
后台长时间的科学计算:这样就可以让请求马上返回给客户端,后台完成长时间计算后,用SignalR实时提醒用户
后台群发邮件:通过延迟和循环任务分批通过SendCloud这样的服务发送群发邮件 当然,Hangfire的应用场景还很多,比如在后台处理电商卖家的订单。
MVP 2015社区大讲堂演讲的PPT在此:标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&原文:/redmoon/p/4394962.html
教程昨日排行
&&国之画&&&& &&&&&&
&& &&&&&&&&&&&&&&
鲁ICP备号-4
打开技术之扣,分享程序人生!

我要回帖

更多关于 abp hangfire 的文章

 

随机推荐