zoukankan      html  css  js  c++  java
  • 定时器_在.net core3.0 webapi中添加自定义定时器功能

      前言:想在.net framework环境使用自定义定时器的话,参考我的另一篇文章:https://www.cnblogs.com/lxhbky/p/10242839.html

      想在.net core中使用定时器功能,需要借助一个服务接口:IHostedService,   继承并实现对应方法,最后再setup.cs类中添加注册服务:services.AddHostedService<实现服务类>();   既然要写计时器的服务器,那么该实现类就要包含定时器,本篇博客也是借助System.Timers.Timer类封装的。

      下面展示具体代码:

      1-公用基类:

      

    public class ModelBase
        {
            protected IServiceProvider Services { get; set; }
            protected IWebHostEnvironment WebHostEnvironment { get; set; }
            /// <summary>
            /// 配置帮助类
            /// </summary>
            protected ConfigHelper ConfigHelper { get; set; }
            /// <summary>
            /// 等同于ASP.NET里面的WebCache(HttpRuntime.Cache)
            /// </summary>
            protected IMemoryCache MemoryCache { get; set; }
    
            /// <summary>
            /// 日志
            /// </summary>
            protected ILogger Logger { get; set; }
    
            /// <summary>
            /// 授权帮助
            /// </summary>
            protected OAuthHelper OAuthHelper { get; set; }
    
            /// <summary>
            /// HttpClient帮助工厂
            /// </summary>
            protected IHttpClientFactory HttpClientFactory { get; set; }
    
    
            public ModelBase(params object[] @params)
            {
                foreach (var item in @params)
                {
                    if (item is IServiceProvider)
                    {
                        this.Services = (IServiceProvider)item;
                    }
                    else if (item is IWebHostEnvironment)
                    {
                        this.WebHostEnvironment = (IWebHostEnvironment)item;
                    }
                    else if (item is ConfigHelper)
                    {
                        this.ConfigHelper = (ConfigHelper)item;
                    }
                    else if (item is IMemoryCache)
                    {
                        this.MemoryCache = (IMemoryCache)item;
                    }
                    else if (item is ILogger)
                    {
                        this.Logger = (ILogger)item;
                    }
                    else if (item is OAuthHelper)
                    {
                        this.OAuthHelper = (OAuthHelper)item;
                    }
                    else if (item is IHttpClientFactory)
                    {
                        this.HttpClientFactory = (IHttpClientFactory)item;
                    }
                    
                }
    
            }
        }

      

      2-计时器封装类:

      相对于.net framework文章计时器部分的类做了对应优化,更加简化了:

    using Microsoft.Extensions.Logging;
    using PaymentAccountAPI.Common;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Timers;
    
    namespace PaymentAccountAPI.Helper
    {
        /// <summary>
        /// 定时周期帮助类
        /// </summary>
        public class TimeCycleHelp : ModelBase
        {
            public TimeCycleHelp(ILogger<TimeCycleHelp> logger) : base(logger)
            {
                this.Timer = new System.Timers.Timer();
            }
    
            /// <summary>
            /// 服务专属计时器
            /// </summary>
            private System.Timers.Timer Timer;
    
            /// <summary>
            /// 默认计时器时间间隔1秒(提高计时器开始时间准确度)
            /// </summary>
            private double DefaultTimerInterval = 1 * 1000;
    
            /// <summary>
            /// 设置多个循环周期
            /// </summary>
            private List<TimeCycle> TimeCycleList { get; set; }
    
    
            /// <summary>
            /// 更新一个计时器的计时周期
            /// </summary>
            /// <param name="newTimerInterval">新的计时周期</param>
            /// <param name="isFirstStart">是否是首次更新计时器周期</param>
            private void UpdateTimeInterval(double newTimerInterval, bool isFirstStart = false)
            {
                if (this.Timer != null && newTimerInterval > 0)
                {
                    this.Timer.Stop();
                    if (this.Timer.Interval != newTimerInterval)
                    {
                        this.Timer.Interval = newTimerInterval;
                    }
                    if (isFirstStart)
                    {
                        this.Timer.Elapsed += new System.Timers.ElapsedEventHandler(this.ServiceAction);
                    }
                    this.Timer.AutoReset = true;
                    this.Timer.Start();
                }
            }
    
            /// <summary>
            /// 内部辅助方法
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void ServiceAction(object sender, ElapsedEventArgs e)
            {
                List<TimeCycle> currentTimeCycleList = new List<TimeCycle>(0);
    
                DateTime now = DateTime.Now;
                DateTime cycleBeginTime;
                DateTime cycleEndTime;
                foreach (TimeCycle timeCycle in this.TimeCycleList)
                {
                    cycleBeginTime = Convert.ToDateTime(timeCycle.BeginTime);
                    cycleBeginTime = now.Date.AddHours(cycleBeginTime.Hour).AddMinutes(cycleBeginTime.Minute).AddSeconds(cycleBeginTime.Second);
                    cycleEndTime = Convert.ToDateTime(timeCycle.EndTime);
                    cycleEndTime = now.Date.AddHours(cycleEndTime.Hour).AddMinutes(cycleEndTime.Minute).AddSeconds(cycleEndTime.Second);
                    if (cycleEndTime < cycleBeginTime)
                    {
                        cycleEndTime = cycleEndTime.AddDays(1);
                    }
    
                    if (now >= cycleBeginTime && now <= cycleEndTime)
                    {
                        //有最大执行次数限制或者没有限制
                        if (timeCycle.ActionExecutionTimes < timeCycle.MaxActionTimes || timeCycle.MaxActionTimes == 0)
                        {
                            TimeSpan timeSpan = now - cycleBeginTime;
                            bool isCanAction = (int)timeSpan.TotalSeconds % timeCycle.ActionSeconds == 0 ? true : false;
                            if (isCanAction)
                            {
                                timeCycle.ActionExecutionTimes++;
                                currentTimeCycleList.Add(timeCycle);
                            }
                        }
                    }
                    else
                    {
                        //不在计时周期内,已执行次数清零
                        timeCycle.ActionExecutionTimes = 0;
                    }
                }
                //找到当前循环周期后,执行周期内动作
                if (currentTimeCycleList.Count > 0)
                {
                    currentTimeCycleList.ForEach(item =>
                    {
                        //使用多线程执行任务,让代码快速执行
                        Task.Run(() => item.Action());
                    });
                }
            }
    
            /// <summary>
            /// 开启计时器
            /// </summary>
            /// <param name="timeCycleArray"></param>
            public void Start(params TimeCycle[] timeCycleArray)
            {
                if (timeCycleArray != null && timeCycleArray.Length > 0)
                {
                    if (this.TimeCycleList == null)
                    {
                        this.TimeCycleList = new List<TimeCycle>(100);
                    }
                    this.TimeCycleList = timeCycleArray.ToList();
    
                    //设置首次计时器周期(首次动作执行,是在计时器启动后在设置的时间间隔后做出的动作)
                    this.UpdateTimeInterval(this.DefaultTimerInterval, true);
                }
            }
    
            /// <summary>
            /// 结束计时器
            /// </summary>
            public void Stop()
            {
                this.Timer.Stop();
            }
    
        }
    
        /// <summary>
        /// 计时周期类
        /// </summary>
        public class TimeCycle
        {
            /// <summary>
            /// 唯一标识
            /// </summary>
            public int ID { get; set; }
            /// <summary>
            /// 开始时间(误差1秒=取决于计时器默认时间间隔)
            /// </summary>
            public string BeginTime { get; set; }
            /// <summary>
            /// 结束时间
            /// </summary>
            public string EndTime { get; set; }
            /// <summary>
            /// 最大执行次数
            /// </summary>
            public int MaxActionTimes { get; set; }
            /// <summary>
            /// 计时周期内执行的动作(动作会在到达开始时间后的)
            /// </summary>
            public Action Action { get; set; }
            /// <summary>
            /// 动作执行时间间隔(秒)
            /// </summary>
            public int ActionSeconds { get; set; }
            /// <summary>
            /// 方法执行次数
            /// </summary>
            internal int ActionExecutionTimes { get; set; }
    
            public TimeCycle(int id, Action action, int actionSeconds) : this(id, "00:00:00", action, actionSeconds)
            {
            }
    
            public TimeCycle(int id, string beginTime, Action action, int actionSeconds) : this(id, beginTime, action, actionSeconds, 0)
            {
            }
    
            public TimeCycle(int id, string beginTime, Action action, int actionSeconds, int maxActionTimes) : this(id, beginTime, "23:59:59", action, actionSeconds, maxActionTimes)
            {
            }
    
            /// <summary>
            /// 基本构造器
            /// </summary>
            /// <param name="id">唯一标识</param>
            /// <param name="beginTime">开始时间</param>
            /// <param name="endTime">结束时间</param>
            /// <param name="action">要执行的任务</param>
            /// <param name="actionSeconds">任务执行时间间隔</param>
            /// <param name="maxActionTimes">最大执行次数</param>
            public TimeCycle(int id, string beginTime, string endTime, Action action, int actionSeconds, int maxActionTimes)
            {
                this.ID = id;
                this.BeginTime = beginTime;
                this.EndTime = endTime;
                this.Action = action;
                this.ActionSeconds = actionSeconds;
                this.MaxActionTimes = maxActionTimes;
            }
        }
    }

      3-webAPI服务封装类:

      

    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using PaymentAccountAPI.Common;
    using PaymentAccountAPI.Helper;
    using PaymentAccountAPI.PaymentAccountHelper;
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace PaymentAccountAPI.Hangfire
    {
        public class SyncToCMSService : ModelBase, IHostedService
        {private TimeCycleHelp _TimeCycleHelp { get; set; }
    
            public SyncToCMSService(ConfigHelper configHelper,
                                    TimeCycleHelp timeCycleHelp, ILogger<SyncToCMSService> logger) : base(logger, configHelper)
            {this._TimeCycleHelp = timeCycleHelp;
            }
    
            public void SyncData()
            {
               //...需要执行的任务代码
    
                this.Logger.LogInformation($"定时任务:完成{currentDate.ToShortDateString()}的交易记录同步!");
            }
    
    
    
            public Task StartAsync(CancellationToken cancellationToken)
            {
                this.Logger.LogInformation($"定时任务:同步服务已启动...");
    
                //正式代码
                string syncBeginTime = this.ConfigHelper.GetAppSettingValue("SyncCMSBeginTime");
                string syncEndTime = this.ConfigHelper.GetAppSettingValue("SyncCMSEndTime");
                this._TimeCycleHelp.Start(new TimeCycle(999, syncBeginTime, syncEndTime, this.SyncData, 3600, 2));
    
                //测试代码
                //this._TimeCycleHelp.Start(new TimeCycle(999, "12:00:00", () =>
                //{
                //    this.Logger.LogInformation("test123");
                //}, 10, 3));
    
                return Task.CompletedTask;
            }
    
            public Task StopAsync(CancellationToken cancellationToken)
            {
                this.Logger.LogInformation($"同步服务已停止...");
                this._TimeCycleHelp.Stop();
    
                return Task.CompletedTask;
            }
    
        }
    }

      4-在startup.cs添加注册服务:

      

                #region 添加定时任务
    
                services.AddSingleton<TimeCycleHelp>();
    
                services.AddHostedService<SyncToCMSService>();
    
                #endregion

      5-部署到IIS站点项目时的注意事项(引用下面道友的一段话):

           关于GenericHost的生存周期问题

      如果你使用的是控制台启动,则此问题暂时可以忽略。

      如果你使用的是站点项目,并且还是通过IIS启动,那么你可能要注意了,因为.net core 的站点自身是有HOST宿主处理,IIS是其上代理,其启动关闭,端口映射等由IIS内部完成。所以其依然受限于IIS的闲置回收影响,当IIS闲置回收时,其后的.Net Host也会被一同关闭,需要有新的请求进来时才会再次启动。不过鉴于当前任务处理已经如此简单,有个取巧的做法,实现一个站点自身的心跳检测任务,IIS默认20分钟回收,任务时间可以设为15分钟(你也可以设置IIS站点回收时间),当然如果你的任务如果没有那么严格的时间要求你也可以不用处理,因为回收后一旦接受到新的请求,任务会再次发起。

      最后感谢一篇道友的文章:.Net Core 简单定时任务框架封装

  • 相关阅读:
    卸载office密钥的命令
    断言的使用
    stm32的NVIC是什么?
    STM32 Cube mx 安装
    不用移位计算获得高位数据
    分组数据
    Vue Router
    存储过程
    js 中 json.stringfy()将对象、数组转换成字符串
    js中 json对象的转化 JSON.parse()
  • 原文地址:https://www.cnblogs.com/lxhbky/p/12218988.html
Copyright © 2011-2022 走看看