while 循环内部使用 Task.Delay 设置时间,在 this.options.CheckTime 计时结束后继续下一轮的调度任务
实际上,Task.Delay 方法内部也是使用了 System.Threading.Timer 类进行计时,但是,当内部的 Timer 计时结束后,会马上被 Dispose 掉
2.2 任务管理类 BackManagerService 包含一个带参数的构造方法,是一个匿名委托,需要传入参数 BackManagerOptions,该参数表示一个任务的调度参数
2.3 创建 BackManagerOptions 任务调度操作类
public class BackManagerOptions { /// <summary> /// 任务名称 /// </summary> public string Name { get; set; } /// <summary> /// 获取或者设置检查时间间隔,单位:毫秒,默认 10 秒 /// </summary> public int CheckTime { get; set; } = 10 * 1000; /// <summary> /// 回调委托 /// </summary> public Action Callback { get; set; } /// <summary> /// 执行细节传递委托 /// </summary> public Action<BackHandler> Handler { get; set; } /// <summary> /// 传递内部信息到外部组件中,以方便处理扩展业务 /// </summary> /// <param>0=Info,1=Debug,2=Error,3=exit</param> /// <param></param> /// <param></param> /// <param></param> public void OnHandler(int level, string message, Exception ex = null, object state = null) { Handler?.Invoke(new BackHandler() { Level = level, Message = message, Exception = ex, State = state }); } }2.4 该 BackManagerOptions 任务调度操作类包含了一些基础的设置内容,比如任务名称,执行周期间隔,回调委托 Callback,任务管理器内部执行细节传递委托 Handler,这些定义非常有用,下面会用到
2.5 其中,执行细节传递委托 Handler 包含一个参数,其实就是传递的细节,非常简单的一个实体对象类,无非就是信息级别,消息描述,异常信息,执行对象
public class BackHandler { /// <summary> /// 0=Info,1=Debug,2=Error /// </summary> public int Level { get; set; } public string Message { get; set; } public Exception Exception { get; set; } public object State { get; set; } }2.6 定义好上面的 3 个对象后,现在来创建一个订单管理类,用于定时轮询数据库订单是否超时未付款,然后返还库存
public class OrderManagerService { public void CheckOrder() { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("==业务执行完成=="); Console.ForegroundColor = ConsoleColor.Gray; } public void OnBackHandler(BackHandler handler) { switch (handler.Level) { default: case 0: break; case 1: case 3: Console.ForegroundColor = ConsoleColor.Yellow; break; case 2: Console.ForegroundColor = ConsoleColor.Red; break; } Console.WriteLine("{0} | {1} | {2} | {3}", handler.Level, handler.Message, handler.Exception, handler.State); Console.ForegroundColor = ConsoleColor.Gray; if (handler.Level == 2) { // 服务执行出错,进行补偿等工作 } else if (handler.Level == 3) { // 退出事件,清理你的业务 CleanUp(); } } public void CleanUp() { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("==清理完成=="); Console.ForegroundColor = ConsoleColor.Gray; } }2.7 这个 OrderManagerService 业务类定义了 3 个方法,CheckOrder 检查订单,OnBackHandler 输出执行信息,CleanUp 在程序退出的时候去做一些清理工作,非常简单,前两个方法是用于注册到 BackManagerService 任务调度器中,后一个是内部方法。
3. 注册 BackManagerService 任务调度器到进程中3.1 定义好业务类后,我们需要把它注册到进程中,以便程序启动和退出的时候自动执行
3.2 在 Startup.cs 的 ConfigureServices 方法中注册托管主机,看下面的代码
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddSingleton<Microsoft.Extensions.Hosting.IHostedService, BackManagerService>(factory => { OrderManagerService order = new OrderManagerService(); return new BackManagerService(options => { options.Name = "订单超时检查"; options.CheckTime = 5 * 1000; options.Callback = order.CheckOrder; options.Handler = order.OnBackHandler; }); }); }3.3 上面的代码通过将 BackManagerService 注册到托管主机中,并在初始化的时候设置了 BackManagerOptions ,然后将 OrderManagerService 的方法注册到 BackManagerOptions 的委托中,实现业务执行
3.4 运行程序,观察输出结果
3.4 输出结果清晰的表示创建的托管服务运行良好,我们来看一下执行顺序
执行顺序
启动托管服务
执行“订单超时检查”任务,连续执行了 3 次,间隔 5 秒,每次执行都向外部传递了执行细节信息
由于我们故意设置任务执行到第 3 次的时候模拟抛出异常,可以看到,异常被正确的捕获并安全的传递到外部
任务继续执行
强制终止了程序,然后托管服务收到了程序停止的信号并立即进行了清理工作,通知外部业务委托执行清理
清理完成,托管服务停止并退出