ABP官方文档翻译 7.1 后台Jobs和Workers
后台Jobs和Workers
介绍
ABP提供了后台jobs和workers,他们用于在应用的后台线程中执行一些任务。
后台Jobs
后台Jobs用来排队一些需要在后台队列或持久化的方式执行一些任务。以下几种情况可以使用后台jobs:
- 执行一些长时运行的任务而不需要用户等待。例如,用户按了一个'report'按钮开始一个长时运行的报表job。可以将这个job添加到队列,当执行完成的时候通过邮件发送给用户报表结果。
- 创建一个重试且持久化的任务来保证一段代码的成功运行。例如:可以在后台job中发送邮件来克服临时错误并保证它最终被发送。这样,当发送邮件时,用户不必等待。
关于Job持久化
参见后台Job存储了解更多关于job持久化的信息。
创建后台Job
我们可以通过从BackgroundJob<TArgs>类或直接实现IBackgroundJob<TArgs>接口来创建一个后台job类。
这是最简单的后台job:
public class TestJob : BackgroundJob<int>, ITransientDependency
{
public override void Execute(int number)
{
Logger.Debug(number.ToString());
}
}
后台job定义了Excute方法,它有一个输入参数。参数类型为泛型类参数。
后台job必须注册到依赖注入系统。实现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);
}
}
我们注入了user仓储(获得用户电子邮件)和电子邮件sender(发送邮件的服务)简单的发送邮件。SimpleSendEmailJobArgs为job的参数,定义如下:
[Serializable]
public class SimpleSendEmailJobArgs
{
public long SenderUserId { get; set; } public long TargetUserId { get; set; } public string Subject { get; set; } public string Body { get; set; }
}
job参数应该是可以序列化的,因为它被序列化并存储在数据库中。ABP默认后台job管理器使用JSON序列化(它不需要[Serilizable]特性),但是最好定义[Serializable]特性,因为我们可能在将来切换到其他job管理器,它可能使用.NET的内置二进制序列化。
保持参数简单(如DTOs),不要包含实体或其他非序列化的对象。如在SimpleSendEmailJob示例中所示,我们可以存储实体的Id并从实体的仓储中获得实体。
在队列中添加一个新Job
定义后台job之后,我们可以注入并使用IBackgroundJobManager来添加job到队列。参见TestJob定义示例:
public class MyService
{
private readonly IBackgroundJobManager _backgroundJobManager; public MyService(IBackgroundJobManager backgroundJobManager)
{
_backgroundJobManager = backgroundJobManager;
} public void Test()
{
_backgroundJobManager.Enqueue<TestJob, int>();
}
}
当入队时,我们使用42作为参数。IBackgroundJobManager将实例化并执行TestJob,参数为42。
让我们添加一个上面定义的新的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
});
}
}
Enque(或EnqueueAsync)方法还有其他参数如priority和delay。
默认的后台Job管理器
IBackgroundJobManager默认由BackgroundJobManager实现。它可以被其他后台job提供者(参见hangfire集成)取代。BackgroundJobManager的一些参考信息:
- 它是一个单线程的FIFO的简单job队列。它使用IBackgroundJobStore来持久化jobs(参见下部分)。
- 在job成功运行(不抛出任何异常但是会记录)或超时前会一直重试。默认的重试时间为2天。
- 当成功执行后,它从仓储(数据库)中删除job。如果它超时了,就设置为abandoned并保留在数据库中。
- 每次重试前它会递增时间。第一次尝试等待1分钟,第二次等待2分钟,第三次等待4分钟...
- 它以固定间隔轮训jobs仓储。按优先级(升序)、重试次数(升序)排列查询jobs。
后台Job存储
默认的BackgroundJobManager需要一个数据仓储来保存和获取jobs。如果你没实现IBackgroundJobStore那么它使用InMemoryBackgroundJobStore,它不在持久化的数据库中存储jobs。你可以简单的实现它来在数据库中存储jobs或者可以使用已经实现了它的module-zero。
如果你使用了第三方的job管理器(如Hangfire),就不需要实现IBackgroundJobStore了。
配置
你可以在模块的PreInitialize方法中使用Configuration.BackgroundJobs来配置后台job系统。
禁用Job执行
你可以禁用应用程序的后台job执行:
public class MyProjectWebModule : AbpModule
{
public override void PreInitialize()
{
Configuration.BackgroundJobs.IsJobExecutionEnabled = false;
} //...
}
几乎不需要这样做。但是,假如你的应用运行了多个实例并使用同样的数据库(在web farm中)。在这种情况下,每个应用将使用同样的数据库查询job并执行。这会导致同样job的多次执行及其他的问题。为了阻止这种情况,有两种选择:
- 你可以只为应用程序的一个实例启用job执行。
- 你可以为应用程序的所有实例禁用job执行,然后创建一个单独的执行后台jobs的应用程序(例如:windows service)。
异常处理
因为默认的后台管理器会自动重试失败的jobs,它处理(并记录)所有的异常。如果当异常发生时你想要得到通知,可以创建一个事件处理者来处理AbpHandledExceptionData。后台管理器触发这个事件,它有一个BackgroundJobException异常对象参数,这个参数包装了真正的异常(获取InnerException获取真正的异常)。
Hangfire集成
后台管理器设计为可以被其他后台job管理器代替。参见hangfire集成文档,使用Hangfire替换它。
后台Workers
后台Workers与后台Jobs不同。他们为在后台运行的简单独立的线程。通常,他们定期执行一些任务。例如:
- 后台worker可以定期的删除老的日志。
- 后台worker可以定期的判定不活动的用户并发送邮件来通知用户。
创建后台Workers
创建一个后台worker,我们需要实现IBackgroundWorker接口。作为另一个选择,我们可以基于我们的需求继承BackgroundWorkerBase或PeriodicBackgroundWorkerBase。
假如我们想使用户失活,如果他30天内没有登录应用的话。参见下面的代码:
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 = ; //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()); 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();
}
}
}
这是一段直接在ABP moudle-zero中使用的真实代码。
- 如果你从PeriodicBackgroundWokerBase(如在本例中)继承,需要实现DoWork方法来执行你的定期执行代码。
- 如果你从BackgroundWorkerBase继承或直接实现IBackgroundWorker接口,你需要重写/实现Start,Stop和WaitToStop方法。Start和Stop方法需要为非阻塞的,WaitToStop方法需要等待worker完成它当前关键的job。
注册后台Workers
创建后台worker后,我们需要把它添加到IBackgroundWorkerManger。最常见的地方为模块的PostInitialize方法:
public class MyProjectWebModule : AbpModule
{
//... public override void PostInitialize()
{
var workManager = IocManager.Resolve<IBackgroundWorkerManager>();
workManager.Add(IocManager.Resolve<MakeInactiveUsersPassiveWorker>());
}
}
我们通常在PostInitialize方法中添加worker,但并不限制。你可以在任何地方注入IBackgroundWorkerManager并添加worker。当应用程序关闭时,IBackgroundWorkerManager将停止并释放所有注册的worker。
后台Worker生命周期
后台workder一般实现为单例模式。但是并没有限制。如果你需要同个worker类的多个实例,你可以使它为瞬时的并添加多个实例到IBackgroundWorkerManager。在这种情况下,你的worker将为参数化的(也就是说你有一个单独的LogCleaner类但是两个LogCleaner worker实例他们监视并清理不同的日志文件夹)。
高级计划安排
ABP后台worker系统比较简单。除了上面展示的定期执行worker,它没有计划安排系统。如果你需要更多计划安排的特征,我们建议你使用Quartz或其他类库。
让你的应用一直运行
后台jobs和workers只有当你应用程序运行时才会工作。如果长时间web应用没有请求执行,ABP应用默认会关闭。所以,如果你在web应用中(这是默认行为)寄宿后台job的执行,你需要保证你的web应用配置为一直执行。否则,后台job只有应用在使用时才能工作。
有一些技术可以实现。最简单的方式是使用外部应用定期请求你的web应用。这样,你可以检查文本应用是否已启动并运行。Hangfire文档解释了一些其他的方式。
ABP官方文档翻译 7.1 后台Jobs和Workers的更多相关文章
- ABP官方文档翻译 0.0 ABP官方文档翻译目录
一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...
- ABP官方文档翻译 10.1 ABP Nuget包
ABP Nuget包 Packages Abp Abp.AspNetCore Abp.Web.Common Abp.Web Abp.Web.Mvc Abp.Web.Api Abp.Web.Api.OD ...
- ABP官方文档翻译 7.3 Quartz集成
Quartz集成 介绍 安装 创建Jobs 计划安排Jobs 更多 介绍 Quartz是一个全功能的.开源的job计划安排系统,可以用在小的apps也可以用于大型的企业系统.Abp.Quartz包简化 ...
- ABP官方文档翻译 8.1 通知系统
通知系统 介绍 发送模型 通知类型 通知数据 通知严重性 关于通知持久化 订阅通知 发布通知 用户通知管理 实时通知 客户端 通知存储 通知定义 介绍 在系统中通知用来基于特定的事件告知用户.ABP提 ...
- ABP官方文档翻译 7.2 Hangfire集成
Hangfire集成 介绍 ASP.NET Core集成 ASP.NET MVC 5.x集成 面板授权 介绍 Hangfire是一个综合的后台job管理器.你可以 把它集成到ABP,用来取代默认的后台 ...
- ABP官方文档翻译 1.1 介绍
介绍 介绍 快速示例 其他 启动模板 如何使用 介绍 我们通常会根据不同的需求来创建不同的应用程序.但是对于一些通用相似的结构总是一遍又一遍的实现,至少在某种程度上是这样的.常见的通用模块如授权.验证 ...
- 0.0 ABP官方文档翻译目录
一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...
- ABP官方文档翻译 2.5 设置管理
设置管理 介绍 关于 ISettingStore 定义设置 设置范围 重写设置定义 获取设置值 服务端 客户端 更改设置 关于缓存 介绍 每个应用都需要存储设置,并且在应用的某些地方需要使用这些设置. ...
- ABP官方文档翻译 9.3 NHibernate集成
NHibernate集成 Nuget包 配置 实体映射 仓储 默认实现 自定义仓储 应用程序特定基础仓储类 ABP可以使用任何ORM框架,它内置集成NHibernate.此文档将讲解ABP如何使用NH ...
随机推荐
- bzoj usaco 金组水题题解(1)
UPD:我真不是想骗访问量TAT..一开始没注意总长度写着写着网页崩了王仓(其实中午的时候就时常开始卡了= =)....损失了2h(幸好长一点的都单独开了一篇)....吓得赶紧分成两坨....TAT. ...
- HDU_5504 GT and sequence
GT and sequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) ...
- Netty5序章之BIO NIO AIO演变
Netty5序章之BIO NIO AIO演变 Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠的网络服务器和客户端程序.Netty简化了网络程序的开发,是很多框架和公司都在使 ...
- 算法--链表的K逆序问题
转载请标明出处http://www.cnblogs.com/haozhengfei/p/9e6f4dda3138cf9fab17f996ec85b624.html 链表的K逆序问题 链表的k逆 ...
- 配置国内PIP源方法
python开发者都知道,当我们pip install安装扩展库的时候,经常遇到安装失败(超时)等,有时候是因为国外镜像被屏蔽了,带来不少麻烦, 随着国内python开发的增多,越来越多企业都开放了自 ...
- PhpStorm (强大的PHP开发环境)2017.2.4 附注册方法
http://www.oyksoft.com/soft/40722.html?pc=1 最新版PhpStorm 2017正式版改进了PHP 7支持,改进代码完成功能. PhpStorm 是最好的PHP ...
- 织梦dede在首页调用留言本
织梦dedecms在首页调用留言本 . {dede:loop table=dede_guestbook sort=dtime row=10 titlelen=36 typeid=40 if=ische ...
- shareInstance
2.+(id)shareInstance; 外界初始化得到单例类对象的唯一借口,这个类方法返回的就是instance,即类的一个对象, 如果instance为空,则实例化一个对象,如果不为空,则直接返 ...
- 关于富文本在Android中的应用以及遇到的坑
富文本可以为用户提供更加多样化的文本展示形式,但由于其使用了H5标签的特殊性,一般都需要第三方框架的支持.这里推荐一款合适的第三方富文本框架,richeditor. 首先我们要使用该功能需要引入相关j ...
- 如何修改nexus的端口号
1. Maven仓库:放置所有JAR文件(WAR,ZIP,POM等等)的地方,所有Maven项目可以从同一个Maven仓库中获取自己所需要的依赖JAR,这节省了磁盘资源. 简言之,Maven仓库能帮助 ...