特来电Hangfire智能监控实践

微信扫一扫,分享到朋友圈

特来电Hangfire智能监控实践

Hangfire 是一款强大的.NET开源后台任务利器,无需Windows服务/任务计划程序。

以使用于ASP.NET 应用也可以使用于控制台。
Hangfire 只需简单几句代码即可创建新的不同种类的任务。

Quartz.NET
相比,其集成了可视化的UI管理与监视工具,不但可以查看任务的运行频率 ,还可以手动启动或删除任务。

在特来电系统中,Hangfire作为定时任务调度的引擎,主要用于实现定时充电调度,Plus会员过期提醒,各类状态信息的定时上报和各类定时处理调度(比如各类补偿任务和检查任务)等。

Hangfire UI管理界面

由于Hangfire的的任务执行依赖于轮询redis或者sqlserver数据库,如果它们发生宕机或者连接出现问题,Hangfire的工作线程往往会挂掉,出现任务无法按时执行的问题,当然可以通过Hangfire的UI界面发现问题,但是对于可用性要求高的互联网应用,通过人工观察是无法保证系统的可靠性。

所以为了及时了解Hangfire的运行状态,我们对 Hangfire
的系统有如下的监控需求:

  • 系统是否正常运行,即任务能够正常创建与按时执行。

  • 系统是否有异常,主要指需要执行的任务是否发生积压和任务调度是否有异常。

    因此我们需要对Hangfire进行如下维度的监控:

  • Hangfire是否正常工作

  • Hangfire
    积压任务数

  • Hangfire是否有内部异常





 






Hangfire
工作线程是否正常


Hangfire工作原理如图

  • 客户端将任务压入存储介质(比如sqlserver)

  • 存储介质,负责存储任务数据

  • 服务端轮询任务,取出合适的任务并执行。

    服务端的功能主要是2块

  • 任务轮询功能:取出当前时间需要执行的任务,存入 存储介质
    的执行队列中。

  • 任务执行功能:访问 存储介质
    的执行队列,如果有任务取出并执行。

特来电使用的Hangfire 任务主要是下面两类:

  • Delayed Job 一次性定时执行的任务,比如7天后执行的任务。

  • Recurring Job 按照固定频率循环执行的任务,比如10分钟一次。频率设定使用cron表达式(cron表达式是一种定义定时任务执行频率的语言)

上述两种任务的分别有各自的轮询功能。


  • Delayed
     Job
    :当Delayed


     Job存入 存储介质
      轮询线程会根据

    执行时间算一个得分( H
    angfire的内部得分即距离1970年1月1日零时的秒数,下面提到的得分计算规则是一样的
    ), Delayed
     Job
    的轮询线程,每次执行时会计算当前时间的得分,然后根据这个数字与任务的得分比较,如果当前日期的得分大于任务得分,将任务迁移到 存储介质
    的执行队列中。

  • Recurring 

    Job:当 Recurring 
    Job
    存入 存储介质,
    得分为空。轮询线程每次运行时,解析cron表达式,计算下一次的执行时间的得分,如果得分小于当前时间则 将任务迁移到
    存储介质
    的执行队列中
    。同时更新的 Recurring 
    Job
    的得分(无论是否达到执行时间)。

Hangfire执行逻辑

因此如果想确保 Delayed
 Job
Recurring 
Job功能都是正常,可以通过创建心跳任务来确认,如果心跳任务正常,那么就认为从创建任务到任务执行的通路是畅通的。

所以在除了业务任务外,在Hangfire的任务队列中新增了Recurring job Heartbeat和Delayed Job Heartbeat两类任务。这 两个任务都是用来

上报监控信息。 R
ecurring job Heartbeat
是非常简单的 R
ecurring job,频率为
每分钟执行一次。

Delayed Job Heartbeat稍微复杂,创建一个每小时执行的Recurring job,这个job处理为新增60个Delayed Job Heartbeat监控任务,在下一个小时中每分钟执行一个。  

码实现如下:

//Recurring job 每分钟执行,上报一次心跳信息,RecurringJobID为任务ID
RecurringJob.AddOrUpdate(RecurringJobID, () => pro.ReportMonitorData(RecurringMonitoMetaData), "* * * * *");
//Delayed Job 每小时执行一次,ReportBackgroundDataID为任务ID
RecurringJob.AddOrUpdate(ReportBackgroundDataID, () => pro.ReportSchduleProcess(), "0 * * * *");

ReportSchduleProcess
即为创建 Delayed Job 
Heartbeat的任务

 for (int i = 1; i <= 60; i++)
{
BackgroundJob.Schedule(() => ReportMonitorData(ScheduleMonitoMetaData), new TimeSpan(0, i, 0));
}

其中, ReportMonitorData
方法为上报监控的方法,参数为监控维度信息。

这样如果任务轮询或者任务执行出现问题,那么必然出现Hangfire监控上报的空白。

正常情况监控










Hangfire
积压
任务数

Hangfire的积压任务数是指在 执行队列
中任务数量,即已经到时间执行但是尚未执行的任务数,通常可以使用UI管理界面直接查看,但是对于互联网应用来说,这是无法满足需求的,同样还是需要对接监控系统。




上图的Enqueued即积压任务数

对于积压任务的数目,直接使用Hangfire自己的监控API即可以获得

JobStorage.Current.GetMonitoringApi().EnqueuedCount("default")

其中“default”这个参数为执行队列名,如果没有特别指定,默认是“default”。

同时为了能够定时上报,也把上报处理当成一个每分钟执行的Job来处理。

RecurringJob.AddOrUpdate(ReportMonitorExtrusionDataID, () => pro.ReportMonitorExtrusionData(), "* * * * *");

这样的如果通过监控图表可以实时的看到各个Hangfire站点的任务积压数

三 

Hangfire
是否有内部异常

Hangfire内部有完善的日志机制,提供了trace,debug,info,warn,error,fatal等级的日志。我们可以自己实现Hangfire的日志的处理实时监控Hangfire的内部异常。

实现Hangfire的日志自定义处理,需要实现Hangfire.Logging.ILog和Hangfire.Logging.ILogProvider 接口。其中ILog接口是日志的具体实现,ILogProvider负责提供日志实现。

   //
// 摘要:
// Represents a way to get a Hangfire.Logging.ILog
public interface ILogProvider
{
ILog GetLogger(string name);
}

ILogProvider
定义

我们可以在自己实现的

ILogProvider


返回自己定义的 ILog
,然后在程序启动时,设定
LogProvider

 GlobalConfiguration.Configuration.UseLogProvider(new CustomerLogProvider())

在实现了ILog之后,当Hangfire的内部处理调用 Log处理时, 如果传入的

 bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception = null);

Log方法定义

Exception不为空
,或者LogLevel是Error或者Fatal时,就可以认为出现内部异常,需要上报异常监控信息。





规划和展望

   
   
Hangfire调度框架是一个优秀的计划任务调度框架,其本身的监控非常重要,通过对其深入的监控埋点改造,特来电已经实现全面的智能监控和自动化运维,全面保障业务的连续性。未来,我们将继续改造Hangfire,根据业务的需求进行深度定制,将更多更好的特性贡献到技术社区中,分享给大家。

微信扫一扫,分享到朋友圈

特来电Hangfire智能监控实践

科学家用胶原蛋白与合成纤维织造动脉移植物

上一篇

入门用Python进行Web爬取数据:为数据科学项目提取数据的有效方法

下一篇

你也可能喜欢

特来电Hangfire智能监控实践

长按储存图像,分享给朋友