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

阅读本文你的收获

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

一、定时任务的应用场景

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

定时任务的使用场景有:

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

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

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

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

三、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抽象类

    csharp 复制代码
    /// <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. 注册托管服务

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

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

3.1. Timer计时器

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

csharp 复制代码
//以下演示如何使用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接口

    csharp 复制代码
    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)抽象方法,启动定时器

    csharp 复制代码
    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. 在定时器执行的任务中,使用有作用域的服务,如使用订单服务。

    csharp 复制代码
    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();
            }
        }
    }

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

csharp 复制代码
   private readonly IServiceProvider _serviceP; 

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

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

结语

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

如果本文对你有帮助的话,请点赞+关注,或者转发给需要的朋友。

相关推荐
金銀銅鐵3 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
念何架构之路3 小时前
go-grpc设计思想
开发语言·后端·golang
system_error_x4 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
步行cgn5 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
码事漫谈6 小时前
AI 读你的提示词,其实很没耐心
后端
用户8356290780517 小时前
如何使用 Python 格式化 Excel 单元格中的长文本
后端·python
我命由我123457 小时前
Python 开发 - Python 的 pathlib
java·开发语言·后端·python·学习·java-ee·学习方法
IT_陈寒7 小时前
Java性能优化完全指南
前端·人工智能·后端
电子IT笔记7 小时前
对接群晖NAS,上传和删除踩坑记录
前端·后端
Mk598787 小时前
P4:把 Agent 的记忆搬进 Redis —— AgentScope-Java 工程化实战
后端