ASP.NET Core基础之定时任务(一)-托管服务+Timer

发布时间:2023年12月21日

阅读本文你的收获

  1. 了解定时任务的使用场景
  2. 学会在ASP.NET Core 中使用托管服务
  3. 学会利用托管服务+Timer定时器实现定时任务

一、定时任务的应用场景

在系统开发过程中,很多时候我们不能对某一状态或某一行为实时进行人为监控,所以就希望系统能自动实现这一功能。这就需要写一些后台程序,它们可以在应用系统启动时,或者在某个时间点去触发启动,启动后就能自动、定期地在后台运行,这就是后台的定时任务。

定时任务的使用场景有:

  • 定时给客户群发邮件
  • 定时检查系统版本更新
  • 定时从远程接口更新本地缓存数据,系统间数据同步
  • 定时进行文件切割、临时文件删除。
  • 定时更新数据看板的数据,如待办任务数量、订单数、每日销售额等
  • 电商系统中定时检查未支付订单,自动关闭等。

二、ASP.NET Core中怎么做定时任务

在ASP.NET Core中,可以使用托管服务(Hosted Service)结合Timer计时器可以实现后台定时任务。这种方案比较适合简单的场景,对于任务的定时规则设置也很单一,也不用去监控任务的执行状态。本文主要探讨这种方案。

对于更加复杂的应用场景,也可以使用功能更加强大的任务调度框架,来实现定时任务。这样的框架有:

  • QuartZ.NET
  • HangFire

三、ASP.NET Core中的托管服务

在 ASP.NET Core 中,后台任务是作为托管服务(Hosted Service)实现的。 想要实现后台任务可以实现IHostedService接口或者直接继承BackgroundService抽象类。

  1. IHostedService接口是托管服务接口,在Microsoft.Extensions.Hosting命名空间下面,有两个接口方法:
  • StartAsync(CancellationToken) :当应用程序主机准备好启动服务时触发该方法
  • StopAsync(CancellationToken):当应用程序主机执行正常关闭时触发该方法。
  1. BackgroundService类是一个抽象类,这个类实现 IHostedService 接口,并将包含后台任务的具体代码逻辑。该抽象类有一个抽象方法:
  • ExecuteAsync(CancellationToken): 子类需要重写该方法以执行具体的任务处理

3.1 编写一个托管服务

开发环境

平台版本是:.NET6
开发框架:ASP.NET Core WebApi
开发工具:Visual Studio 2022

  1. 定义一个后台服务类,注意实现IHostedService 接口或直接继承BackgroundService抽象类

    /// <summary>
    /// 自定义后台任务类
    /// </summary>
    public class MyHostedService : BackgroundService
    {
        /// <summary>
        /// 执行任务
        /// </summary>
        /// <param name="stoppingToken"></param>
        /// <returns></returns>
        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            int i = 1;
     		//如果没停止,就一直执行
            while (!stoppingToken.IsCancellationRequested)
            {
                DoWork(i++);
            }
    
            return Task.CompletedTask;
        }
    
        /// <summary>
        /// 打印输出一句话
        /// </summary>
        /// <param name="i"></param>
        private void DoWork(int i)
        {
            Console.WriteLine("后台任务执行MyHostedService,第{0}次", i);
        }
    }
    
  2. 注册托管服务

    //.NET6中的Program.cs中注册后台任务
    builder.Services.AddHostedService<MyHostedService>(); 
    
  3. 运行项目,就会看到后台任务不断被执行
    输出结果

四、Timer定时器+托管服务 实现简单定时任务

3.1. Timer计时器

System.Threading.Timer是一个非常常用的定时器类,是一个使用回调方法的计时器,而且由线程池线程服务,简单且对资源要求不高。

//以下演示如何使用Timer类
using System;
using System.Threading;

public class TestTimer
{
    /// <summary>
    /// 定时器
    /// </summary>
    private Timer _iTimer;
    /// <summary>
    /// 构造方法
    /// </summary>
    public TestTimer()
    {
        _iTimer = new Timer(Doing,null,TimeSpan.Zero, TimeSpan.FromMinutes(5));
    }
    /// <summary>
    /// 执行具体定定时任务
    /// </summary>
    /// <param name="nObject"></param>
    public void Doing(object nObject)
    {
        //具体代码。。
    }
}

3.2. 实现每10分钟1次检查订单状态

  1. 创建后台服务类TimedHostedService,继承BackgroundService抽象基类,该抽象类实现了 IHostedService接口

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Hosting;
    
    namespace XfTechOAWeb.ApiServer.Background
    {
        /// <summary>
        /// 定时服务
        /// </summary>
        public class TimedHostedService : BackgroundService
        {
            /// <summary>
            /// 执行任务
            /// </summary>
            /// <param name="stoppingToken"></param>
            /// <returns></returns>
            protected override Task ExecuteAsync(CancellationToken stoppingToken)
            {
                return Task.CompletedTask;
            }
        }
    }
    
  2. 实现ExecuteAsync(CancellationToken stoppingToken)抽象方法,启动定时器

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Hosting;
    
    namespace XfTechOAWeb.ApiServer.Background
    {
        /// <summary>
        /// 定时服务
        /// </summary>
        public class TimedHostedService : BackgroundService
        {
            private Timer _timer;   //定时器
    
            /// <summary>
            /// 执行任务
            /// </summary>
            /// <param name="stoppingToken"></param>
            /// <returns></returns>
            protected override Task ExecuteAsync(CancellationToken stoppingToken)
            {
                //定时器创建后马上执行DoWork方法,每10秒钟执行一次
                _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(10)); 
    
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// 定时器具体执行的方法
            /// </summary>
            /// <param name="state"></param>
            private void DoWork(object state)
            {
                //做一些事情
    
            }
    
            /// <summary>
            /// 释放资源
            /// </summary>
            public override void Dispose()
            {
                _timer?.Dispose();  //如果_timer对象不为null,则销毁
    
                base.Dispose();
            }
    
        }
    }
    
  3. 在定时器执行的任务中,使用有作用域的服务,如使用订单服务。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.DependencyInjection; //IServiceProvider接口的命名空间
    using XfTechOAWeb.Application.Interfaces;       //应用服务接口的命名空间
    
    namespace XfTechOAWeb.ApiServer.Background
    {
        /// <summary>
        /// 定时服务
        /// </summary>
        public class TimedHostedService : BackgroundService
        {
            private readonly IServiceProvider _serviceP; 
            private Timer _timer;   //定时器
    
            /// <summary>
            /// 构造方法
            /// </summary>
            /// <param name="scopeFactory"></param>
            public TimedHostedService(IServiceProvider serviceP)
            {
                _serviceP = serviceP;
            }
    
            /// <summary>
            /// 执行任务
            /// </summary>
            /// <param name="stoppingToken"></param>
            /// <returns></returns>
            protected override Task ExecuteAsync(CancellationToken stoppingToken)
            {
                _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(10*60)); //定时器创建后马上执行,每10分钟执行一次
    
                return Task.CompletedTask;
            }
    
            /// <summary>
            /// 定时器具体执行的方法
            /// </summary>
            /// <param name="state"></param>
            private void DoWork(object state)
            {
     			//创建服务作用域
     			using(var scope = _serviceP.CreateScope())
     			{
     			    //IOrderService 的实现代码 略
     			    var orderService = scope.ServiceProvider.GetRequiredService<IOrderService>();
     			    //检查订单是否已支付,超过10分钟未支付订单自动关闭
     			    orderService.CheckOrderPayment(); 
     			}
            }
    
            /// <summary>
            /// 释放资源
            /// </summary>
            public override void Dispose()
            {
                _timer?.Dispose();  //如果_timer对象不为null,则销毁
    
                base.Dispose();
            }
        }
    }
    

在托管服务中使用服务的一种好方法是在需要时创建作用域。这允许将应用服务/数据库上下文等与设置它们的生命周期配置一起使用。

   private readonly IServiceProvider _serviceP; 

	//创建服务作用域
	using(var scope = _serviceP.CreateScope())
	{
		//...
	}
  1. IHostBuilder.ConfigureServices (Program.cs) 中注册托管服务及有作用域的其他服务

    builder.Services.AddHostedService<TimedHostedService>();   //注册托管服务--定时任务
    builder.Services.AddScoped<IOrderService, OrderService>(); //注册订单服务--有作用域的服务
    

结语

本文提供了一个简单示例来演示如何使用IHostedService+Timer实现定时任务,并将其注册到ASP.NET Core应用程序中。它的优点是轻量级别,使用简单;如果是比较复杂的,批量的定时任务,并且需要对任务进行监听,还需要并发控制等场景,那么推荐你还是使用第三方定时任务组件,比如QuartZ等主流工具。
如果本文对你有帮助的话,请点赞+关注,或者转发给需要的朋友。

文章来源:https://blog.csdn.net/ousetuhou/article/details/135081996
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。