给.net core安排后台定时任务,并且解决iis自动回收导致任务被终止的问题

1、在 ASP.NET Core 中使用托管服务实现后台任务

很多时候我们需要给程序添加一些后台任务,帮我处理一些需要定时处理的任务,比如定时发送邮件,定时做一些统计之类的工作,这时候我们可以写一个windows服务来搞定,或者在现有的webapi项目中添加定时。

写windows服务来处理后台任务固然是个比较好的选择,但是这样就会增加运维成本,原本只有一个项目,现在有两个,最主要的是windows服务很容易被遗忘掉,升级或者迁移的时候增加风险,对于一些比较简单的任务,我选择直接在 ASP.NET Core 中使用托管服务实现后台任务,非常的简单。这样就可以在一起维护和管理。

编写一个定时任务有两个步骤:

1、实现IHostedService,IDisposable这两个接口,或者直接继承BackgroundService抽象类(因为它实现IHostedService,IDisposable),看代码:

    //实现接口:IHostedService,IDisposable
    public class EmailBackgroundService : IHostedService,IDisposable
    {
        //定义一个定时器
        private Timer _timer;
        
        /// <summary>
        /// 启动任务绑定
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StartAsync(CancellationToken cancellationToken)
        {
            Common.WriteEmailLog("定时任务被启动", "...start...");
            //绑定定时任务
            //设置延迟时间
             _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(60 * Interval));
            return Task.CompletedTask;
        }

        /// <summary>
        /// 定时执行的操作,绑定到定时器上
        /// </summary>
        /// <param name="state"></param>
        /// <returns></returns>
        private void DoWork(object state)
        {
            Common.WriteEmailLog("定时任务被触发", "开始一波邮件发送");
            try
            {
                //一波操作
            }
            catch (Exception ex)
            {
                Common.WriteEmailLog("定时发送邮件时报错", ex.Message);
            }
        }

        /// <summary>
        /// 任务关闭时执行
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StopAsync(CancellationToken cancellationToken)
        {
            Common.WriteEmailLog("定时任务被关闭", "...end...");
            _timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }

        /// <summary>
        /// 释放托管资源,释放时触发
        /// </summary>
        public void Dispose()
        {
            Common.WriteEmailLog("定时任务被释放闭", "...Dispose...");
            _timer?.Dispose();
            //iis会回收这个定时任务,这边在回收的时候触发一个请求,来再次唤醒该服务
            Thread.Sleep(5000);
            HttpHelper.HttpGet(_configuration.GetSection("AwakenUrl").Value);
        }
    }

2、在Startup类中注册后台任务:

//配置后台任务
//services.AddTransient(typeof(Microsoft.Extensions.Hosting.IHostedService), typeof(EmailBackgroundService));
services.AddHostedService<EmailBackgroundService>();

2、解决iis自动回收导致任务被终止的问题

IIS会定时回收,类似于自动重新启动网站,我们都有知道网站启动后第一次访问往往会比较慢。所以网站的使用次数不是很多的时候,我们过段时间打开这个网站,总要经历一段短暂的等待,很不舒服,这个就是因为iis回收导致的。

定时回收除了会出现上面的情况外,还会将我们托管的后台任务回收掉,导致我们的后台任务终止执行,直到进来一个请求(网站内任意地址)任务启动,那么如果请求很久没来,这个任务将无法被启动,错过任务应该执行的档口。虽然我们可以通过调整自动回收的时间,甚至设置他不自动回收,但这样始终不是最好的解决方案。

定时任务是托管的资源,实现了IDisposable接口,并且实现了Dispose()方法,这个方法在回收的时候会被触发。通过这一点我们可以在Dispose()方法中,再请求一下我们的网站或接口(请求地址尽量选择资源消耗比较小的),通过访问这样一个请求,我们服务又会起来。具体的代码是这样的:

/// <summary>
/// 释放托管资源
/// </summary>
public void Dispose()
{
    Common.WriteEmailLog("定时任务被释放闭", "...Dispose...");
    _timer?.Dispose();
    //iis会回收这个定时任务,这边在回收的时候触发一个请求,来再次唤醒该服务
    Thread.Sleep(5000);
    HttpHelper.HttpGet(_configuration.GetSection("AwakenUrl").Value);
}

上边代码中的Thread.Sleep(5000);去掉后任务大概率还是不能起来,我猜测是因为回收是需要一定时间的,立即就去请求这个链接,很可能在回收之前,所以没有起到重新唤醒的作用,加上后,基本上就能正常唤醒了。下图显示任务在回收后被自动唤醒起来。

 iis也可以设置网站回收后自动发送一个请求,具体的设置方法,我已经想不起来了,感兴趣的朋友可以去研究下。

猜你喜欢

转载自blog.csdn.net/maaici/article/details/108620830