Abp后台工作者类使用hangfire
一、Abp中的后台工作及后台工作者类
请阅读这篇文章
二 、Abp官方实现的缺点
Abp官方实现方式很简单,也很容易上手,但缺点是工作者类依赖了具体的基类(PeriodicBackgroundWorkerBase),就会存在应用程序耦合。
为什么会耦合呢,假设以后想采用HangFire或Quartz.NET来调度工作者,我们就需要把所有工作类的基类进行修改,这不利于系统的维护和可扩展,而且采用官方实现无法监测和管控工作者。
三、开始改造
1、核心库
要消除工作者类对具体调度类的依赖,则只能让后台工作者类继承自不含调度实现的基类(BackgroundWorkerBase)或直接实现接口(IBackgroundWorker)。我定义了一个泛型基类(BackgroundWorker
BackgroundWorker
IBackgroundWorkerDo: 约束所有后台工作者类必须实现DoWork,配合泛型参数,Hangfire的轮询任务便可以知道T类型一定会有一个DoWork方法,然后在RecurringJob.AddOrUpdate
WorkerConfig类: 每个后台工作者都应该有一个唯一的标识,执行间隔时间,这样轮询代理类才知道如何处理
IBackgroudWorkerProxy: 代替后台工作者类执行其DoWork方法,所有轮询调度类都应该实现该接口
////// 所有的后台工作者类都应实现该接口/// public interface IBackgroundWorkerDo{////// 执行具体的任务/// void DoWork();}
////// 所有后台工作者类都应继承该类/// public abstract class BackgroundWorker: BackgroundWorkerBase, IBackgroundWorkerDo where T : IBackgroundWorkerDo{protected readonly IBackgroudWorkerProxy _workProxy;protected readonly WorkerConfig _config;protected BackgroundWorker(IBackgroudWorkerProxy workProxy, WorkerConfig config){_workProxy = workProxy;_config = config;}/// /// 任务启动/// public override void Start(){Logger.Debug("轮询任务启动");_workProxy.Excete(DoWork, _config); //主要指定当前任务类,不然hangfire无法调用,不然可以移到父类去 }/// /// 具体的任务执行/// public abstract void DoWork();}
////// 工作任务配置/// public class WorkerConfig{////// 轮询秒数/// public int IntervalSecond { get; set; }////// 工作唯一编号/// public string WorkerId { get; set; }}
public interface IBackgroudWorkerProxy{////// 执行/// /// void Excete(Action method, WorkerConfig config) where T : IBackgroundWorkerDo;}
以上便是解耦的核心代码,在核心代码中,仿照Abp官方的PeriodicBackgroundWorkerBase类提供了一个基于Timer的轮询调度实现:
public class PeriodicWorkerPxoxy : IBackgroudWorkerProxy{private Action ExetuteMethod { get; set; }protected readonly AbpTimer Timer;public PeriodicWorkerPxoxy(AbpTimer timer){Timer = timer;Timer.Elapsed += Timer_Elapsed;}private void Timer_Elapsed(object sender, EventArgs e){try{DoWork();}catch (Exception ex){}}public void Excete(Action method, WorkerConfig config) where T: IBackgroundWorkerDo{ExetuteMethod = method;Timer.Period = config.IntervalSecond*1000;//将传入的秒数转化为毫秒 Timer.Start();}protected void DoWork(){ExetuteMethod();}}
作为一个核心模块,所以还需要定义一个模块启动配置文件
public class FastWorkWorkerPxoxyModule : AbpModule{public override void Initialize(){IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());}public override void PreInitialize(){IocManager.RegisterIfNot();}}
核心库解决方案图如下,(记住要引用Abp核心库)
在需要的项目中引入该Dll,然后按照模块启动配置依赖进行配置
[DependsOn(typeof(AbpZeroCoreModule), typeof(AbpZeroLdapModule), typeof(AbpAutoMapperModule), typeof(FastWorkWorkerPxoxyModule))]public class FastWorkCoreModule : AbpModule{...}
后台工作者类示例:
namespace ORS.FastWork.Core.Sms {////// 清理短信日志/// public class SmsWorker : BackgroundWorker, ISingletonDependency{private readonly IRepository _smsLogRepository;public SmsWorker(IRepository smsLogRepository,IBackgroudWorkerProxy workMiddleware) : base(workMiddleware, new WorkerConfig { IntervalSecond=60,WorkerId="smsworker"}){_smsLogRepository = smsLogRepository;}public override void DoWork(){//_smsLogRepository.Insert(new SmsSendLog { IsOk = true, Content = "轮询任务创建的", CreationTime = DateTime.Now }); }} }
2、HangFire实现
主要的类有两个,一个是启动配置,一个实现了IBackgroudWorkerProxy接口,解决方案目录如下:
解决方案记得引用上面定义好的核心库,Hangfire实现轮询的代码如下:
public class HangfireWorkerPxoxy : IBackgroudWorkerProxy{public HangfireWorkerPxoxy(){}private WorkerConfig Config { get; set; }public void Excete(Action method, WorkerConfig config) where T: IBackgroundWorkerDo{Config = config;string workerId = config.WorkerId;string cron = Cron.MinuteInterval(config.IntervalSecond/60);RecurringJob.AddOrUpdate (config.WorkerId, (t)=>t.DoWork(), cron,TimeZoneInfo.Local);RecurringJob.Trigger(config.WorkerId);}}
模块启动文件中的代码很关键,当后台工作采用了Hangfire来调度时(即在web模块的启动文件中使用了 Configuration.BackgroundJobs.UseHangfire(...)),则后台工作者类的调度也将由我们核心库中的PeriodicWorkerPxoxy变更为Hangfire来接管
public class HangFireWorkerModule : AbpModule{public override void Initialize(){IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());}public override void PreInitialize(){IocManager.RegisterIfNot();}public override void PostInitialize(){//判断是否启用了hangfire,如果启用了,则将IBackgroudWorkerProxy的实例改为hangfirevar hangfireConfig = IocManager.Resolve ();if (hangfireConfig?.Server!= null) {IocManager.IocContainer.Register(Component.For ().ImplementedBy ().IsDefault());}} }
在Web项目中引用该项目,然后在模块启动中加入对该模块的依赖
在PostInitialize方法中向后台工作管理类加入具体的工作
最终效果如下:
3.进一步优化
该方案目前已在我们公司的项目中投入使用,由于时间和精力关系,我个人没有对该方案进行进一步优化。在web模块启动文件中,还是需要做两步工作:1.引用了dll 2.启动文件上标注依赖关系,每增加一种轮询调度方式我们都需要重复这两步,如果想做得更灵活的话,可以弄成插件模块(拷入dll到站点PlugIns目录,然后再后台设置一下即可),下一篇文章我会以短信网关插件实战来演示Abp插件模块的妙用。
转载于:https://www.cnblogs.com/94pm/p/6803829.html
标签:
相关文章
-
无相关信息