您好,登錄后才能下訂單哦!
這篇文章主要介紹“.NET Core開發Windows服務之怎么使用Quartz執行定時任務”,在日常操作中,相信很多人在.NET Core開發Windows服務之怎么使用Quartz執行定時任務問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”.NET Core開發Windows服務之怎么使用Quartz執行定時任務”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
最近的項目也是主要為團隊提供API接口,大多都是處理常規的業務邏輯上的事。過程中有個需求是需要每日定時定點執行一些推送消息的任務,一開始也沒多想就將定時任務寫到了API的項目里,部署完測試下人傻了,日志沒有任何執行了任務的痕跡,調試時候沒毛病。回頭一想,IIS這個懶東西應該是休眠了,直接把我的任務一起回收掉了。淡定的我捋了捋思緒查了查方案,可以更改IIS設置修改定時回收的模式,可以通過訪問站點來喚醒,覺得不是很合適,既然是WindowsServer,那我干脆弄一個WindowsService來定時執行任務再好不過了鴨,而且之前也沒用過.net core寫過WindowsService,正好吃個螃蟹。
一開始我是直接弄了個控制臺程序,按照之前.NET Framework的寫法來寫。后來發現.NET Core專門為這種后臺服務(長時間運行的服務)設計了項目模板,稱之為Worker Service。為了滿足在每日的固定時間點執行,這里選擇老牌的Quartz來實現。簡單描述一下Demo要實現的需求:每日定點向一個API接口中發送信息。
使用Visual Studio(我是使用的VS2019)創建項目,選擇Worker Service(如下圖),姑且就命名為WindowsServiceDemo。
項目創建完成之后里面的內容很簡單,一個Program.cs和另一個Work.cs,Work類繼承BackgroundService,并重寫其ExecuteAsync方法。顯而易見,ExecuteAsync方法就是執行后臺任務的入口。
Program.cs中,依舊是類型的通過創建一個IHost并啟動運行。為了方便進行依賴注入,可以創建一個IServiceCollection的擴展方法來進行服務的注冊,接下來一步步介紹。
進行服務注冊之前,先將需要引用的包通過Nuget安裝一下。安裝 Quartz 來實現定時執行任務。另外由于需求需要調用api接口即需要使用HttpClient發送請求,所以還需要另外引入包 Microsoft.Extentsions.Http 。由于需要部署成WindowService,需要引入包 Microsoft.Extensions.Hosting.WindowsServices 。
首先定義Job,即執行任務的具體業務邏輯。創建一個SendMsgJob類,繼承IJob接口,并實現Execute方法。Execute方法就是到了設定好的時間點時執行的方法。這里即是實現了使用注冊的HttpClient來發送消息的過程。
public class SendMsgJob : IJob { private readonly AppSettings _appSettings; private const string ApiClientName = "ApiClient"; private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger<SendMsgJob> _logger; public SendMsgJob(IHttpClientFactory httpClientFactory, IOptions<AppSettings> appSettings, ILogger<SendMsgJob> logger) { _httpClientFactory = httpClientFactory; _logger = logger; _appSettings = appSettings.Value; } /// <summary> /// 定時執行 /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task Execute(IJobExecutionContext context) { _logger.LogInformation($"開始執行定時任務"); //從httpClientFactory獲取我們注冊的named-HttpClient using var client = _httpClientFactory.CreateClient(ApiClientName); var message = new { title = "今日消息", content = _appSettings.MessageNeedToSend }; //發送消息 var response = await client.PostAsync("/msg", new JsonContent(message)); if (response.IsSuccessStatusCode) { _logger.LogInformation($"消息發送成功"); } } }
創建好Job之后,便是設置它讓其定時執行即可。來到Work.cs,替換掉原來的默認演示代碼,換之配置Job執行策略的代碼。使用Quartz配置Job大致分為這么幾部
創建調度器 Scheduler
創建Job實例
創建觸發器來控制Job的執行策略
將Job實例和觸發器實例配對注冊進調度器中
啟動調度器
public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; public Worker(ILogger<Worker> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("服務啟動"); //創建一個調度器 var scheduler = await StdSchedulerFactory.GetDefaultScheduler(stoppingToken); //創建Job var sendMsgJob = JobBuilder.Create<SendMsgJob>() WithIdentity(nameof(SendMsgJob), nameof(Worker)) Build(); //創建觸發器 var sendMsgTrigger = TriggerBuilder.Create() WithIdentity("trigger-" + nameof(SendMsgJob), "trigger-group-" + nameof(Worker)) StartNow() WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(08, 30)) //每日的08:30執行 Build(); await scheduler.Start(stoppingToken); //把Job和觸發器放入調度器中 await scheduler.ScheduleJob(sendMsgJob, sendMsgTrigger, stoppingToken); } }
關于定時任務的配置告一段落,接下來將所需的服務注冊到服務容器中。根據之前所說的,我們創建一個擴展方法來管理我們需要注冊的服務。
public static class DependencyInject { /// <summary> /// 定義擴展方法,注冊服務 /// </summary> public static IServiceCollection AddMyServices(this IServiceCollection services, IConfiguration config) { //配置文件 services.Configure<AppSettings>(config); //注冊“命名HttpClient”,并為其配置攔截器 services.AddHttpClient("ApiClient", client => { client.BaseAddress = new Uri(config["ApiBaseUrl"]); }).AddHttpMessageHandler(_ => new AuthenticRequestDelegatingHandler()); //注冊任務 services.AddSingleton<SendMsgJob>(); return services; } }
修改Program.cs,調用新增的擴展方法
namespace WindowsServiceDemo { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) ConfigureServices((hostContext, services) => { //注冊服務 services.AddMyServices(hostContext.Configuration) AddHostedService<Worker>(); }); } }
到此,主要的代碼就介紹完了。為了調試,可以修改設定好的定時執行時間(比如一分鐘之后),來測試是否能夠成功。修改完觸發器的觸發時間后,直接運行項目。但是遺憾的是,任務并沒有定時觸發。這是什么原因呢?其實是因為雖然我們將我們自定義的Job注入的服務容器,但是調度器創建Job實例時,并不是從我們的服務容器去取的,而是調度器自己走默認的實例化。解決方法是我們為調度器指定JobFactory來重寫實例化Job類型的規則。
首先創建一個MyJobFactory并繼承IJobFactory接口,實現方法 NewJob ,這個方法便是工廠實例化Job的方法,我們可以在這里將實例化Job的方式改寫成從服務容器中獲取實例的方式。
namespace WindowsServiceDemo { /// <summary> /// Job工廠,從服務容器中取Job /// </summary> public class MyJobFactory : IJobFactory { protected readonly IServiceProvider _serviceProvider; public MyJobFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { var jobType = bundle.JobDetail.JobType; try { var job = _serviceProvider.GetService(jobType) as IJob; return job; } catch (Exception e) { Console.WriteLine(e); throw; } } public void ReturnJob(IJob job) { var disposable = job as IDisposable; disposable?.Dispose(); } } }
隨后將 MyJobFactory 也注冊到服務容器中,即在 AddMyServices 擴展方法中添加
1 //添加Job工廠2 services.AddSingleton<MyJobFactory>();
接下來將調度器的Factory替換成 MyJobFactory ,修改Work.cs代碼如下。
public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; private readonly MyJobFactory _jobFactory; public Worker(ILogger<Worker> logger, MyJobFactory jobFactory) { _logger = logger; _jobFactory = jobFactory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("服務啟動"); //創建一個調度器 var scheduler = await StdSchedulerFactory.GetDefaultScheduler(stoppingToken); //指定自定義的JobFactory scheduler.JobFactory = _jobFactory; //創建Job var sendMsgJob = JobBuilder.Create<SendMsgJob>() WithIdentity(nameof(SendMsgJob), nameof(Worker)) Build(); //創建觸發器 var sendMsgTrigger = TriggerBuilder.Create() WithIdentity("trigger-" + nameof(SendMsgJob), "trigger-group-" + nameof(Worker)) StartNow() WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(08, 30)) //每日的08:30執行 Build(); await scheduler.Start(stoppingToken); //把Job和觸發器放入調度器中 await scheduler.ScheduleJob(sendMsgJob, sendMsgTrigger, stoppingToken); } }
在此執行調試,現在一旦到達我們在觸發器中設置的時間點, SendMsgJob 的 Execute 方法便會成功觸發。
開發完成后,現在剩下的任務就是如何將項目發布成一個WindowsService。來到 Program.cs 下,需要進行一些改動
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseWindowsService() //按照Windows Service運行 .ConfigureServices((hostContext, services) => { //注冊服務 services.AddMyServices(hostContext.Configuration) .AddHostedService<Worker>(); });
重新編譯項目成功后,我們便可以使用sc.exe來部署成為windows服務。以管理員身份啟動命令行,執行
> sc.exe create WindowsServiceDemo binPath="D:\workspace\WindowsServiceDemo\WindowsServiceDemo\bin\Debug\netcoreapp3.1\WindowsServiceDemo.exe" [SC] CreateService 成功
此時打開服務面板,便可以看到剛剛部署好的 WindowsServiceDemo 服務了。
到此,關于“.NET Core開發Windows服務之怎么使用Quartz執行定時任務”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。