zoukankan      html  css  js  c++  java
  • Abp后台工作者类使用hangfire

    一、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*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<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=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<T>(Action method, WorkerConfig config) where T: IBackgroundWorkerDo
            {
                Config = config;
                string workerId = config.WorkerId;
                string cron = Cron.MinuteInterval(config.IntervalSecond/60);
                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插件模块的妙用。

  • 相关阅读:
    玩转Visual Studio Editor篇
    .Net2.0的集合操作 What i know?
    Log文件压缩
    [转]比较高效地实现从两个不同数组中提取相同部分组成新的数组(只支持Int类型) [C#]
    Stream 和 Byte[]互操作
    net 2.0 中如何在线程引发的事件中控制forms controls
    C# 操作Word文档(转)
    利用Lucene.net搜索引擎进行多条件搜索的做法
    c# 添加图片水印,可以指定水印位置+生成缩略图
    SDN第一次作业
  • 原文地址:https://www.cnblogs.com/94pm/p/6803829.html
Copyright © 2011-2022 走看看