一、Abp中的后台工作及后台工作者类

请阅读这篇文章

二 、Abp官方实现的缺点

Abp官方实现方式很简单,也很容易上手,但缺点是工作者类依赖了具体的基类(PeriodicBackgroundWorkerBase),就会存在应用程序耦合。

为什么会耦合呢,假设以后想采用HangFire或Quartz.NET来调度工作者,我们就需要把所有工作类的基类进行修改,这不利于系统的维护和可扩展,而且采用官方实现无法监测和管控工作者。

三、开始改造

1、核心库

要消除工作者类对具体调度类的依赖,则只能让后台工作者类继承自不含调度实现的基类(BackgroundWorkerBase)或直接实现接口(IBackgroundWorker)。我定义了一个泛型基类(BackgroundWorker<T>),该基类继承ABP核心库的BackgroundWorkerBase,同时该基类必须实现我自定定义的IBackgroundWorkerDo接口。

BackgroundWorker<T>:所有后台工作者类都该继承的基类,加泛型参数的目的是Hangfire的RecurringJob.AddOrUpdate<T>方法在创建轮询任务时必须知道它该调用哪个类的哪个方法

IBackgroundWorkerDo:  约束所有后台工作者类必须实现DoWork,配合泛型参数,Hangfire的轮询任务便可以知道T类型一定会有一个DoWork方法,然后在RecurringJob.AddOrUpdate<T>的方法体中便可以调用T类型实的DoWork方法

WorkerConfig类: 每个后台工作者都应该有一个唯一的标识,执行间隔时间,这样轮询代理类才知道如何处理

IBackgroudWorkerProxy: 代替后台工作者类执行其DoWork方法,所有轮询调度类都应该实现该接口

    /// <summary>
/// 所有的后台工作者类都应实现该接口
/// </summary>
public interface IBackgroundWorkerDo
{
/// <summary>
/// 执行具体的任务
/// </summary>
void DoWork();
}
    /// <summary>
/// 所有后台工作者类都应继承该类
/// </summary>
public abstract class BackgroundWorker<T> : BackgroundWorkerBase, IBackgroundWorkerDo where T : IBackgroundWorkerDo
{
protected readonly IBackgroudWorkerProxy _workProxy;
protected readonly WorkerConfig _config;
protected BackgroundWorker(IBackgroudWorkerProxy workProxy, WorkerConfig config)
{
_workProxy = workProxy;
_config = config;
}
/// <summary>
/// 任务启动
/// </summary>
public override void Start()
{
Logger.Debug("轮询任务启动");
_workProxy.Excete<T>(DoWork, _config); //主要指定当前任务类,不然hangfire无法调用,不然可以移到父类去
}
/// <summary>
/// 具体的任务执行
/// </summary>
public abstract void DoWork();
}
    /// <summary>
/// 工作任务配置
/// </summary>
public class WorkerConfig
{
/// <summary>
/// 轮询秒数
/// </summary>
public int IntervalSecond { get; set; }
/// <summary>
/// 工作唯一编号
/// </summary>
public string WorkerId { get; set; }
}
    public interface IBackgroudWorkerProxy
{
/// <summary>
/// 执行
/// </summary>
/// <param name="method"></param>
void Excete<T>(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<T>(Action method, WorkerConfig config) where T: IBackgroundWorkerDo
{
ExetuteMethod = method;
Timer.Period = config.IntervalSecond*;//将传入的秒数转化为毫秒
Timer.Start();
} protected void DoWork()
{
ExetuteMethod();
}
}

作为一个核心模块,所以还需要定义一个模块启动配置文件

public class FastWorkWorkerPxoxyModule : AbpModule
{
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
public override void PreInitialize()
{
IocManager.RegisterIfNot<IBackgroudWorkerProxy, PeriodicWorkerPxoxy>();
}
}

核心库解决方案图如下,(记住要引用Abp核心库)

在需要的项目中引入该Dll,然后按照模块启动配置依赖进行配置

    [DependsOn(typeof(AbpZeroCoreModule), typeof(AbpZeroLdapModule), typeof(AbpAutoMapperModule), typeof(FastWorkWorkerPxoxyModule))]
public class FastWorkCoreModule : AbpModule
{
...
}

后台工作者类示例:

namespace ORS.FastWork.Core.Sms
{
/// <summary>
/// 清理短信日志
/// </summary>
public class SmsWorker : BackgroundWorker<SmsWorker>, ISingletonDependency
{
private readonly IRepository<SmsSendLog, long> _smsLogRepository;
public SmsWorker(IRepository<SmsSendLog, long> smsLogRepository,IBackgroudWorkerProxy workMiddleware) : base(workMiddleware, new WorkerConfig { IntervalSecond=,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<T>(Action method, WorkerConfig config) where T: IBackgroundWorkerDo
{
Config = config;
string workerId = config.WorkerId;
string cron = Cron.MinuteInterval(config.IntervalSecond/);
RecurringJob.AddOrUpdate<T>(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<IBackgroudWorkerProxy, HangfireWorkerPxoxy>();
}
public override void PostInitialize()
{
//判断是否启用了hangfire,如果启用了,则将IBackgroudWorkerProxy的实例改为hangfire
var hangfireConfig = IocManager.Resolve<IAbpHangfireConfiguration>();
if (hangfireConfig?.Server!= null) {
IocManager.IocContainer.Register(Component.For<IBackgroudWorkerProxy>().ImplementedBy<HangfireWorkerPxoxy>().IsDefault());
}
}
}

在Web项目中引用该项目,然后在模块启动中加入对该模块的依赖

在PostInitialize方法中向后台工作管理类加入具体的工作

最终效果如下:

3.进一步优化

该方案目前已在我们公司的项目中投入使用,由于时间和精力关系,我个人没有对该方案进行进一步优化。在web模块启动文件中,还是需要做两步工作:1.引用了dll 2.启动文件上标注依赖关系,每增加一种轮询调度方式我们都需要重复这两步,如果想做得更灵活的话,可以弄成插件模块(拷入dll到站点PlugIns目录,然后再后台设置一下即可),下一篇文章我会以短信网关插件实战来演示Abp插件模块的妙用。

Abp后台工作者类使用hangfire的更多相关文章

  1. 后台工作者HangFire与ABP框架Abp.Hangfire及扩展

    HangFire与Quartz.NET相比主要是HangFire的内置提供集成化的控制台,方便后台查看及监控,对于大家来说,比较方便. HangFire是什么 Hangfire是一个开源框架(.NET ...

  2. ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十四节--后台工作者HangFire与ABP框架Abp.Hangfire及扩展

    返回总目录:ABP+AdminLTE+Bootstrap Table权限管理系统一期 HangFire与Quartz.NET相比主要是HangFire的内置提供集成化的控制台,方便后台查看及监控,对于 ...

  3. [Abp 源码分析]十六、后台作业与后台工作者

    0. 简介 在某些时候我们可能会需要执行后台任务,或者是执行一些周期性的任务.比如说可能每隔 1 个小时要清除某个临时文件夹内的数据,可能用户会要针对某一个用户群来群发一组短信.前面这些就是典型的应用 ...

  4. ABP框架理论学习之后台工作(Jobs)和后台工作者(Workers)

    返回总目录 本篇目录 介绍 后台工作 后台工作者 让你的应用程序一直运行 介绍 ABP提供了后台工作和后台工作者,它们会在应用程序的后台线程中执行一些任务. 后台工作 后台工作以队列和持续的方式在后台 ...

  5. [Abp vNext 源码分析] - 12. 后台作业与后台工作者

    一.简要说明 文章信息: 基于的 ABP vNext 版本:1.0.0 创作日期:2019 年 10 月 24 日晚 更新日期:暂无 ABP vNext 提供了后台工作者和后台作业的支持,基本实现与原 ...

  6. ABP后台服务之作业调度Quartz.NET

    一.简介 Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活 ...

  7. ABP官方文档翻译 7.2 Hangfire集成

    Hangfire集成 介绍 ASP.NET Core集成 ASP.NET MVC 5.x集成 面板授权 介绍 Hangfire是一个综合的后台job管理器.你可以 把它集成到ABP,用来取代默认的后台 ...

  8. 实现ABP中Person类的权限功能

    菜单项的显示功能已经完全OK了.那么我们就开始制作视图功能吧. 首先测试接口是否正常 我们通过代码生成器将权限和application中大部分功能已经实现了.那么我们来测试下这些接口ok不. 浏览/a ...

  9. ECshop网点程序优化-后台添加类目自动选择上次父类目并计算Sort Order

    如果在ECshop后台批量添加过大量类目的人都能体会到是多么的不方便(这点还是要说一下ECshop的产品经理,细节上还是要多注意),每次添加都需要在几百个类目里面找到要添加的父类目也是一个麻烦事,比如 ...

随机推荐

  1. 转:C++学习之Pair

    Pair类型概述 pair是一种模板类型,其中包含两个数据值,两个数据的类型可以不同,基本的定义如下: pair<int, string> a; 表示a中有两个类型,第一个元素是int型的 ...

  2. JFinal快速上手及注意事项

    官方手册虽然写的很详细但是忽略的很多小的细节方面,不看源码,网络资料又少,很多新手找不到解决办法.所以养成出了问题,多看源码的习惯 部署helloJFinal 项目结构 - 相关代码 `package ...

  3. LeetCode 66. Plus One(加1)

    Given a non-negative integer represented as a non-empty array of digits, plus one to the integer. Yo ...

  4. 顺序线性表 ---- ArrayList 源码解析及实现原理分析

    原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7738888.html ------------------------------------ ...

  5. CMake vs Make对比

    程序员现在已经使用了CMake和Make了很久.当您加入大公司或开始使用大型代码库开发项目时,您需要处理所有这些构建.你必须看到这些“CMakeLists.txt”文件浮动.你应该在终端上运行“cma ...

  6. 一段批处理脚本(for 嵌套)

    需求: 1.服务器上有一堆按日期生成的目录,已经有N个月了,需要只取当前月份的目录. 2.目录中有一系列文件,文件名字不一样,但存在一定的重复规律. 3.需要从服务器上拷贝文件到本地,自动去重,拷贝到 ...

  7. C# Request.InputStream 读取输入流为空的原因处理

    今天在手机App测试接口的时候发现一个通过POST方式的接口 获取body中的参数一直为空,但是在数据量小的时候却可以获取到数据,开始怀疑是不是POST的长度有限制,然后在web.config中修改了 ...

  8. linux学习(六)绝对路径、相对路径、cd、mkdir、rmdir、rm

    一.绝对路径 就是从根开始的,如:/root./usr/local. 二.相对路径 相对于当前路径的,比如我们在当前路径下建立了一个a.txt. [root@iZ25lzba47vZ ~]# pwd ...

  9. YARN到底是怎么一回事?

    文章思路: 首先提出第一代MRv1(MapReduce Version1.0)的局限性,然后解释YARN是怎么克服这些局限性的,接着说了YARN的编程模型,说了YARN的组成,YARN的通信协议和YA ...

  10. leaflet 利用ajax 将前端地图上的数据post到后台

    生成Google地图,在地图上单击后,将该点的经纬度反馈给后台. 前端HTML代码: <!DOCTYPE html> <html> <head> <meta ...