這篇文章主要介紹“.NET Core開發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時任務(wù)”,在日常操作中,相信很多人在.NET Core開發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時任務(wù)問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”.NET Core開發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時任務(wù)”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
專注于為中小企業(yè)提供網(wǎng)站設(shè)計制作、成都網(wǎng)站設(shè)計服務(wù),電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)平南免費做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了上1000家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實現(xiàn)規(guī)模擴充和轉(zhuǎn)變。
最近的項目也是主要為團(tuán)隊提供API接口,大多都是處理常規(guī)的業(yè)務(wù)邏輯上的事。過程中有個需求是需要每日定時定點執(zhí)行一些推送消息的任務(wù),一開始也沒多想就將定時任務(wù)寫到了API的項目里,部署完測試下人傻了,日志沒有任何執(zhí)行了任務(wù)的痕跡,調(diào)試時候沒毛病?;仡^一想,IIS這個懶東西應(yīng)該是休眠了,直接把我的任務(wù)一起回收掉了。淡定的我捋了捋思緒查了查方案,可以更改IIS設(shè)置修改定時回收的模式,可以通過訪問站點來喚醒,覺得不是很合適,既然是WindowsServer,那我干脆弄一個WindowsService來定時執(zhí)行任務(wù)再好不過了鴨,而且之前也沒用過.net core寫過WindowsService,正好吃個螃蟹。
一開始我是直接弄了個控制臺程序,按照之前.NET Framework的寫法來寫。后來發(fā)現(xiàn).NET Core專門為這種后臺服務(wù)(長時間運行的服務(wù))設(shè)計了項目模板,稱之為Worker Service。為了滿足在每日的固定時間點執(zhí)行,這里選擇老牌的Quartz來實現(xiàn)。簡單描述一下Demo要實現(xiàn)的需求:每日定點向一個API接口中發(fā)送信息。
使用Visual Studio(我是使用的VS2019)創(chuàng)建項目,選擇Worker Service(如下圖),姑且就命名為WindowsServiceDemo。
項目創(chuàng)建完成之后里面的內(nèi)容很簡單,一個Program.cs和另一個Work.cs,Work類繼承BackgroundService,并重寫其ExecuteAsync方法。顯而易見,ExecuteAsync方法就是執(zhí)行后臺任務(wù)的入口。
Program.cs中,依舊是類型的通過創(chuàng)建一個IHost并啟動運行。為了方便進(jìn)行依賴注入,可以創(chuàng)建一個IServiceCollection的擴展方法來進(jìn)行服務(wù)的注冊,接下來一步步介紹。
進(jìn)行服務(wù)注冊之前,先將需要引用的包通過Nuget安裝一下。安裝 Quartz 來實現(xiàn)定時執(zhí)行任務(wù)。另外由于需求需要調(diào)用api接口即需要使用HttpClient發(fā)送請求,所以還需要另外引入包 Microsoft.Extentsions.Http 。由于需要部署成WindowService,需要引入包 Microsoft.Extensions.Hosting.WindowsServices 。
首先定義Job,即執(zhí)行任務(wù)的具體業(yè)務(wù)邏輯。創(chuàng)建一個SendMsgJob類,繼承IJob接口,并實現(xiàn)Execute方法。Execute方法就是到了設(shè)定好的時間點時執(zhí)行的方法。這里即是實現(xiàn)了使用注冊的HttpClient來發(fā)送消息的過程。
public class SendMsgJob : IJob { private readonly AppSettings _appSettings; private const string ApiClientName = "ApiClient"; private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger_logger; public SendMsgJob(IHttpClientFactory httpClientFactory, IOptions appSettings, ILogger logger) { _httpClientFactory = httpClientFactory; _logger = logger; _appSettings = appSettings.Value; } /// /// 定時執(zhí)行 /// /// ///public async Task Execute(IJobExecutionContext context) { _logger.LogInformation($"開始執(zhí)行定時任務(wù)"); //從httpClientFactory獲取我們注冊的named-HttpClient using var client = _httpClientFactory.CreateClient(ApiClientName); var message = new { title = "今日消息", content = _appSettings.MessageNeedToSend }; //發(fā)送消息 var response = await client.PostAsync("/msg", new JsonContent(message)); if (response.IsSuccessStatusCode) { _logger.LogInformation($"消息發(fā)送成功"); } } }
創(chuàng)建好Job之后,便是設(shè)置它讓其定時執(zhí)行即可。來到Work.cs,替換掉原來的默認(rèn)演示代碼,換之配置Job執(zhí)行策略的代碼。使用Quartz配置Job大致分為這么幾部
創(chuàng)建調(diào)度器 Scheduler
創(chuàng)建Job實例
創(chuàng)建觸發(fā)器來控制Job的執(zhí)行策略
將Job實例和觸發(fā)器實例配對注冊進(jìn)調(diào)度器中
啟動調(diào)度器
public class Worker : BackgroundService { private readonly ILogger_logger; public Worker(ILogger logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("服務(wù)啟動"); //創(chuàng)建一個調(diào)度器 var scheduler = await StdSchedulerFactory.GetDefaultScheduler(stoppingToken); //創(chuàng)建Job var sendMsgJob = JobBuilder.Create () WithIdentity(nameof(SendMsgJob), nameof(Worker)) Build(); //創(chuàng)建觸發(fā)器 var sendMsgTrigger = TriggerBuilder.Create() WithIdentity("trigger-" + nameof(SendMsgJob), "trigger-group-" + nameof(Worker)) StartNow() WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(08, 30)) //每日的08:30執(zhí)行 Build(); await scheduler.Start(stoppingToken); //把Job和觸發(fā)器放入調(diào)度器中 await scheduler.ScheduleJob(sendMsgJob, sendMsgTrigger, stoppingToken); } }
關(guān)于定時任務(wù)的配置告一段落,接下來將所需的服務(wù)注冊到服務(wù)容器中。根據(jù)之前所說的,我們創(chuàng)建一個擴展方法來管理我們需要注冊的服務(wù)。
public static class DependencyInject { ////// 定義擴展方法,注冊服務(wù) /// public static IServiceCollection AddMyServices(this IServiceCollection services, IConfiguration config) { //配置文件 services.Configure(config); //注冊“命名HttpClient”,并為其配置攔截器 services.AddHttpClient("ApiClient", client => { client.BaseAddress = new Uri(config["ApiBaseUrl"]); }).AddHttpMessageHandler(_ => new AuthenticRequestDelegatingHandler()); //注冊任務(wù) services.AddSingleton (); return services; } }
修改Program.cs,調(diào)用新增的擴展方法
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) => { //注冊服務(wù) services.AddMyServices(hostContext.Configuration) AddHostedService(); }); } }
到此,主要的代碼就介紹完了。為了調(diào)試,可以修改設(shè)定好的定時執(zhí)行時間(比如一分鐘之后),來測試是否能夠成功。修改完觸發(fā)器的觸發(fā)時間后,直接運行項目。但是遺憾的是,任務(wù)并沒有定時觸發(fā)。這是什么原因呢?其實是因為雖然我們將我們自定義的Job注入的服務(wù)容器,但是調(diào)度器創(chuàng)建Job實例時,并不是從我們的服務(wù)容器去取的,而是調(diào)度器自己走默認(rèn)的實例化。解決方法是我們?yōu)檎{(diào)度器指定JobFactory來重寫實例化Job類型的規(guī)則。
首先創(chuàng)建一個MyJobFactory并繼承IJobFactory接口,實現(xiàn)方法 NewJob ,這個方法便是工廠實例化Job的方法,我們可以在這里將實例化Job的方式改寫成從服務(wù)容器中獲取實例的方式。
namespace WindowsServiceDemo { ////// Job工廠,從服務(wù)容器中取Job /// 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 也注冊到服務(wù)容器中,即在 AddMyServices 擴展方法中添加
1 //添加Job工廠2 services.AddSingleton();
接下來將調(diào)度器的Factory替換成 MyJobFactory ,修改Work.cs代碼如下。
public class Worker : BackgroundService { private readonly ILogger_logger; private readonly MyJobFactory _jobFactory; public Worker(ILogger logger, MyJobFactory jobFactory) { _logger = logger; _jobFactory = jobFactory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("服務(wù)啟動"); //創(chuàng)建一個調(diào)度器 var scheduler = await StdSchedulerFactory.GetDefaultScheduler(stoppingToken); //指定自定義的JobFactory scheduler.JobFactory = _jobFactory; //創(chuàng)建Job var sendMsgJob = JobBuilder.Create () WithIdentity(nameof(SendMsgJob), nameof(Worker)) Build(); //創(chuàng)建觸發(fā)器 var sendMsgTrigger = TriggerBuilder.Create() WithIdentity("trigger-" + nameof(SendMsgJob), "trigger-group-" + nameof(Worker)) StartNow() WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(08, 30)) //每日的08:30執(zhí)行 Build(); await scheduler.Start(stoppingToken); //把Job和觸發(fā)器放入調(diào)度器中 await scheduler.ScheduleJob(sendMsgJob, sendMsgTrigger, stoppingToken); } }
在此執(zhí)行調(diào)試,現(xiàn)在一旦到達(dá)我們在觸發(fā)器中設(shè)置的時間點, SendMsgJob 的 Execute 方法便會成功觸發(fā)。
開發(fā)完成后,現(xiàn)在剩下的任務(wù)就是如何將項目發(fā)布成一個WindowsService。來到 Program.cs 下,需要進(jìn)行一些改動
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseWindowsService() //按照Windows Service運行 .ConfigureServices((hostContext, services) => { //注冊服務(wù) services.AddMyServices(hostContext.Configuration) .AddHostedService(); });
重新編譯項目成功后,我們便可以使用sc.exe來部署成為windows服務(wù)。以管理員身份啟動命令行,執(zhí)行
> sc.exe create WindowsServiceDemo binPath="D:\workspace\WindowsServiceDemo\WindowsServiceDemo\bin\Debug\netcoreapp3.1\WindowsServiceDemo.exe" [SC] CreateService 成功
此時打開服務(wù)面板,便可以看到剛剛部署好的 WindowsServiceDemo 服務(wù)了。
到此,關(guān)于“.NET Core開發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時任務(wù)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
分享題目:.NETCore開發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時任務(wù)
分享鏈接:http://weahome.cn/article/jeoigg.html