同步博客园地址:www.cnblogs.com/anyushengcm…
同步简书地址:www.jianshu.com/p/ebe390e48…html
HangFire与Quartz.NET相比主要是HangFire的内置提供集成化的控制台,方便后台查看及监控,对于你们来讲,比较方便。git
Hangfire是一个开源框架(.NET任务调度框架),能够帮助您建立,处理和管理您的后台做业,处理你不但愿放入请求处理管道的操做:github
Hangfire支持全部类型的后台任务 - 短期运行和长时间运行, CPU intensive
和I/O intensive
,一次性的和常常性的。你不须要从新发明轮子 ,能够直接使用。
Hangfire包含三大核心组件:客户端、持久化存储、服务端。看看官方的这张图:web
var jobId = BackgroundJob.Schedule(
() => Console.WriteLine("Delayed!"),
TimeSpan.FromDays(7));复制代码
RecurringJob.AddOrUpdate(
() => Console.WriteLine("Recurring!"),
Cron.Daily);复制代码
Task
,能够在第一个任务执行完以后紧接着再次执行另外的任务:BackgroundJob.ContinueWith(
jobId,
() => Console.WriteLine("Continuation!"));复制代码
var jobId = BackgroundJob.Schedule(
() => Console.WriteLine("Delayed!"),
TimeSpan.FromDays(7));复制代码
var batchId = Batch.StartNew(x =>
{
x.Enqueue(() => Console.WriteLine("Job 1"));
x.Enqueue(() => Console.WriteLine("Job 2"));
});复制代码
Batch.ContinueWith(batchId, x =>
{
x.Enqueue(() => Console.WriteLine("Last Job"));
});复制代码
public class CleanTempDirectoryProcess : IBackgroundProcess
{
public void Execute(BackgroundProcessContext context)
{
Directory.CleanUp(Directory.GetTempDirectory());
context.Wait(TimeSpan.FromHours(1));
}
}复制代码
后台做业是应用程序中很是重要的部分,Hangfire
确保至少执行一次任务。要在应用程序从新启动之间保留后台做业信息,全部信息都将保存在您最喜欢的持久性存储中。Hangfire Worder
的线程,从新加载应用程序域,或者终止程序,即便这样您的任务仍会被处理。只有在你代码的最后一行执行完成,Hangfire
才会标记这个任务完成。而且知道任务可能在最后一行代码执行以前失败。它包含多种 自动-重试机制,它能够自动处理在存储或代码执行过程当中发生的错误。光说不练假把式,下面咱们新建一个web项目,而后NuGet引入这几个程序集
数据库
而后在App_Start文件夹下Startup类配置下。
首先指定数据库,指定Hangfire使用内存存储后台任务信息.Hangfire.GlobalConfiguration.Configuration.UseSqlServerStorage("Default");
而后启用HangfireServer这个中间件(它会自动释放)app.UseHangfireServer();
而后启用Hangfire的仪表盘(能够看到任务的状态,进度等信息)app.UseHangfireDashboard();
而后配置下前台路由json
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new AbpHangfireAuthorizationFilter() }
});复制代码
而后就是加入上面已经列出的几个例子。bash
var jobId = BackgroundJob.Schedule(
() => Console.WriteLine("Delayed!"),
TimeSpan.FromDays(7));
RecurringJob.AddOrUpdate(
() => Console.WriteLine("Recurring!"),
Cron.Daily);
BackgroundJob.ContinueWith(
jobId,
() => Console.WriteLine("Continuation!"));
var jobId2 = BackgroundJob.Schedule(
() => Console.WriteLine("Delayed!"),
TimeSpan.FromDays(7));复制代码
运行项目,输入路径http://<your-site>/hangfire
而后就能够看到界面了。
服务器
界面看起来很清爽,并且一目了然。这就是可视化界面的好处。并发
ASP.NET Boilerplate提供后台做业和后台工做者,用于在应用程序的后台线程中执行某些任务。
后台做业用于排队某些任务,以队列和持续的方式在后台执行。
咱们能够经过从BackgroundJob <TArgs>
类继承或直接实现IBackgroundJob <TArgs>
接口来建立后台做业类。
这是最简单的后台工做:app
public class TestJob : BackgroundJob<int>, ITransientDependency
{
public override void Execute(int number)
{
Logger.Debug(number.ToString());
}
}复制代码
后台做业定义了一个Execute方法获取输入参数。参数类型被定义为泛型 类参数,如示例中所示。后台工做必须注册到依赖注入系统中,实现ITransientDependency是最简单的方式。下面定义一个更实际的工做,在后台队列中发送电子邮件:
public class SimpleSendEmailJob : BackgroundJob<SimpleSendEmailJobArgs>, ITransientDependency
{
private readonly IRepository<User, long> _userRepository;
private readonly IEmailSender _emailSender;
public SimpleSendEmailJob(IRepository<User, long> userRepository, IEmailSender emailSender)
{
_userRepository = userRepository;
_emailSender = emailSender;
}
public override void Execute(SimpleSendEmailJobArgs args)
{
var senderUser = _userRepository.Get(args.SenderUserId);
var targetUser = _userRepository.Get(args.TargetUserId);
_emailSender.Send(senderUser.EmailAddress, targetUser.EmailAddress, args.Subject, args.Body);
}
}复制代码
咱们注入了用户仓储(为了得到用户信息)和email发送者(发送邮件的服务),而后简单地发送了该邮件。SimpleSendEmailJobArgs是该工做的参数,它定义以下:
[Serializable]
public class SimpleSendEmailJobArgs
{
public long SenderUserId { get; set; }
public long TargetUserId { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}复制代码
做业参数应该是可序列化的,由于它 被序列化并存储在数据库中。虽然ASP.NET Boilerplate默认后台做业管理器使用JSON 序列化(不须要[Serializable]
属性),但最好定义[Serializable]
属性,由于未来可能会切换到另外一个做业管理器,在二进制序列化。保持你的参数简单(如 DTO),不要包含 实体或其余不可序列化的对象。如SimpleSendEmailJob
示例所示,咱们只能存储 一个实体的Id,并从做业中的存储库获取该实体。将新做业添加到队列中定义后台做业后,咱们能够注入并使用IBackgroundJobManager
将做业添加到队列中。查看上面定义的TestJob
示例:
public class MyService
{
private readonly IBackgroundJobManager _backgroundJobManager;
public MyService(IBackgroundJobManager backgroundJobManager)
{
_backgroundJobManager = backgroundJobManager;
}
public void Test()
{
_backgroundJobManager.Enqueue<TestJob, int>(42);
}
}复制代码
当入队(Enqueue)时,咱们将42做为参数传递。IBackgroundJobManager
将会实例化并使用42做为参数执行TestJob
。
让咱们看一下如何为上面定义的SimpleSendEmailJob
添加一个新的工做:
[AbpAuthorize]
public class MyEmailAppService : ApplicationService, IMyEmailAppService
{
private readonly IBackgroundJobManager _backgroundJobManager;
public MyEmailAppService(IBackgroundJobManager backgroundJobManager)
{
_backgroundJobManager = backgroundJobManager;
}
public async Task SendEmail(SendEmailInput input)
{
await _backgroundJobManager.EnqueueAsync<SimpleSendEmailJob, SimpleSendEmailJobArgs>(
new SimpleSendEmailJobArgs
{
Subject = input.Subject,
Body = input.Body,
SenderUserId = AbpSession.GetUserId(),
TargetUserId = input.TargetUserId
});
}
}复制代码
Enqueu
(或EnqueueAsync
)方法具备其余参数,如优先级 和延迟。
IBackgroundJobManager
由BackgroundJobManager
默认实现。它能够被另外一个后台做业提供者替代(参见 hangfire
集成)。有关默认BackgroundJobManager
的一些信息:
这是一个简单的做业队列在 单线程中做为FIFO使用。它使用IBackgroundJobStore
来坚持做业。
默认的BackgroundJobManager
须要一个数据存储来保存和获取做业。若是您没有实现IBackgroundJobStore
,那么它使用 InMemoryBackgroundJobStore
,它不会将做业保存在持久数据库中。您能够简单地实现它来将做业存储在数据库中,或者可使用 已经实现它的module-zero
。
若是您使用第三方工做经理(如 Hanfgire
),则无需实施IBackgroundJobStore
。
您能够在 模块的PreInitialize
方法中使用Configuration.BackgroundJobs
来配置后台做业系统。
禁用做业执行
您可能须要为应用程序禁用后台做业执行:
public class MyProjectWebModule : AbpModule
{
public override void PreInitialize()
{
Configuration.BackgroundJobs.IsJobExecutionEnabled = false;
}
//...
}复制代码
这种状况不多见。可是,认为您正在同一个数据库上运行应用程序的多个实例(在Web场中)。在这种状况下,每一个应用程序将查询做业的相同数据库并执行它们。这致使同一个工做的多个执行和一些其余问题。为了防止它,你有两个选择:
因为默认的后台做业管理器应该从新尝试失败的做业,它会处理(并记录)全部异常。若是你想在发生异常时获得通知,你能够建立一个事件处理程序来处理AbpHandledExceptionData
。后台管理器用一个包装了真正异常的BackgroundJobException
异常对象触发这个事件(对于实际的异常,获得InnerException
)。
####Hangfire集成
要建立一个后台工做者,咱们应该实现 IBackgroundWorker
接口。或者,咱们能够根据咱们的须要从BackgroundWorkerBase
或PeriodicBackgroundWorkerBase
继承 。
假设咱们想在最近30天内没有登陆到应用程序,使用户状态passive。看代码:
public class MakeInactiveUsersPassiveWorker : PeriodicBackgroundWorkerBase, ISingletonDependency
{
private readonly IRepository<User, long> _userRepository;
public MakeInactiveUsersPassiveWorker(AbpTimer timer, IRepository<User, long> userRepository)
: base(timer)
{
_userRepository = userRepository;
Timer.Period = 5000; //5 seconds (good for tests, but normally will be more)
}
[UnitOfWork]
protected override void DoWork()
{
using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.MayHaveTenant))
{
var oneMonthAgo = Clock.Now.Subtract(TimeSpan.FromDays(30));
var inactiveUsers = _userRepository.GetAllList(u =>
u.IsActive &&
((u.LastLoginTime < oneMonthAgo && u.LastLoginTime != null) || (u.CreationTime < oneMonthAgo && u.LastLoginTime == null))
);
foreach (var inactiveUser in inactiveUsers)
{
inactiveUser.IsActive = false;
Logger.Info(inactiveUser + " made passive since he/she did not login in last 30 days.");
}
CurrentUnitOfWork.SaveChanges();
}
}
}复制代码
这是现实的代码,能够直接在module-zero
的 ASP.NET Boilerplate中运行 。
PeriodicBackgroundWorkerBase
派生(如本示例中所示),则应该实施DoWork
方法来执行您的按期工做代码。BackgroundWorkerBase
派生或直接实现IBackgroundWorker
,则将覆盖/实现Start
,Stop
和WaitToStop
方法。Start
和Stop
方法应该是非阻塞的,WaitToStop
方法应该等待 worker
完成当前的关键任务。建立后台工做者后,咱们应该将其添加到 IBackgroundWorkerManager
。最多见的地方是你的模块的PostInitialize
方法:
public class MyProjectWebModule : AbpModule
{
//...
public override void PostInitialize()
{
var workManager = IocManager.Resolve<IBackgroundWorkerManager>();
workManager.Add(IocManager.Resolve<MakeInactiveUsersPassiveWorker>());
}
}复制代码
虽然咱们一般在PostInitialize
中加入工做人员,但对此没有限制。您能够在任何地方注入IBackgroundWorkerManager
,并在运行时添加工做人员。当您的应用程序正在关闭时,IBackgroundWorkerManager
将中止并释放全部注册的工做人员。
后台工做人员一般以单例的。可是没有限制。若是您须要同一工人类的多个实例,则能够将其设置为暂时的,并向IBackgroundWorkerManager
添加多个实例。在这种状况下,您的工做人员多是参数化的(例如,您有一个LogCleaner
类,可是他们监视的两个LogCleaner
工做者实例并清除不一样的日志文件夹)。
ASP.NET Boilerplate的后台工做系统很简单。除了按期运行的工人以外,它没有一个时间表系统。若是您须要更高级的计划功能,咱们建议您检查Quartz
或其余库。
后台做业和工做人员只有在您的应用程序正在运行时才有效 若是很长一段时间没有对Web应用程序执行任何请求,ASP.NET应用程序将默认关闭。所以,若是您在Web应用程序中托管后台做业(这是默认行为),则应确保您的Web应用程序配置为始终运行。不然,后台做业只在您的应用程序正在使用时才起做用。
有一些技术来完成这一点。最简单的方法是按期从外部应用程序请求您的Web应用程序。所以,您也能够检查您的Web应用程序是否已启动并正在运行。 Hangfire文档解释了其余一些方法。
经过以上官方文档,咱们在程序里配置一下。
运行一下效果是同样的。
其实Hangfire仍是蛮简单的。若是你须要了解更多关于Abp.Hangfire的内容,建议你去看一下github上一个专门关于Abp.Hangfire的demo,
讲解abp Hangfire 缺点是工做者类依赖了具体的基类(PeriodicBackgroundWorkerBase),就会存在应用程序耦合。以及解决耦合的办法,算是对abp Hangfire的扩展,我不太认同,各有见解吧。
Hangfire
是一个后台可监控的应用,不用每次都要从服务器拉取日志查看,在没有ELK的时候至关不方便。Hangfire
控制面板不只提供监控,也能够手动的触发执行定时任务。若是在定时任务处理方面没有很高的要求,好比必定要5s定时执行,Hangfire
值得拥有。抛开这些,Hangfire
优点太明显了:
Job Queue
处理任务job
执行并发数控制IOC
、Hangfire Dashboard
受权控制、Asp.net Core
、持久化存储等Hangfire扩展性你们能够参考这里,有几个扩展是很实用的.下面这些关于Hangfire扩展你们能够本身查资料。后面若是有机会的话,我再补上。
本文githubd地址:github.com/Jimmey-Jian…