溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

.NET Core開(kāi)發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時(shí)任務(wù)

發(fā)布時(shí)間:2021-10-19 09:31:58 來(lái)源:億速云 閱讀:183 作者:iii 欄目:編程語(yǔ)言

這篇文章主要介紹“.NET Core開(kāi)發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時(shí)任務(wù)”,在日常操作中,相信很多人在.NET Core開(kāi)發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時(shí)任務(wù)問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”.NET Core開(kāi)發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時(shí)任務(wù)”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

最近的項(xiàng)目也是主要為團(tuán)隊(duì)提供API接口,大多都是處理常規(guī)的業(yè)務(wù)邏輯上的事。過(guò)程中有個(gè)需求是需要每日定時(shí)定點(diǎn)執(zhí)行一些推送消息的任務(wù),一開(kāi)始也沒(méi)多想就將定時(shí)任務(wù)寫(xiě)到了API的項(xiàng)目里,部署完測(cè)試下人傻了,日志沒(méi)有任何執(zhí)行了任務(wù)的痕跡,調(diào)試時(shí)候沒(méi)毛病。回頭一想,IIS這個(gè)懶東西應(yīng)該是休眠了,直接把我的任務(wù)一起回收掉了。淡定的我捋了捋思緒查了查方案,可以更改IIS設(shè)置修改定時(shí)回收的模式,可以通過(guò)訪問(wèn)站點(diǎn)來(lái)喚醒,覺(jué)得不是很合適,既然是WindowsServer,那我干脆弄一個(gè)WindowsService來(lái)定時(shí)執(zhí)行任務(wù)再好不過(guò)了鴨,而且之前也沒(méi)用過(guò).net core寫(xiě)過(guò)WindowsService,正好吃個(gè)螃蟹。

一開(kāi)始我是直接弄了個(gè)控制臺(tái)程序,按照之前.NET Framework的寫(xiě)法來(lái)寫(xiě)。后來(lái)發(fā)現(xiàn).NET Core專門(mén)為這種后臺(tái)服務(wù)(長(zhǎng)時(shí)間運(yùn)行的服務(wù))設(shè)計(jì)了項(xiàng)目模板,稱之為Worker Service。為了滿足在每日的固定時(shí)間點(diǎn)執(zhí)行,這里選擇老牌的Quartz來(lái)實(shí)現(xiàn)。簡(jiǎn)單描述一下Demo要實(shí)現(xiàn)的需求:每日定點(diǎn)向一個(gè)API接口中發(fā)送信息。

使用Visual Studio(我是使用的VS2019)創(chuàng)建項(xiàng)目,選擇Worker Service(如下圖),姑且就命名為WindowsServiceDemo。

.NET Core開(kāi)發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時(shí)任務(wù)

項(xiàng)目創(chuàng)建完成之后里面的內(nèi)容很簡(jiǎn)單,一個(gè)Program.cs和另一個(gè)Work.cs,Work類繼承BackgroundService,并重寫(xiě)其ExecuteAsync方法。顯而易見(jiàn),ExecuteAsync方法就是執(zhí)行后臺(tái)任務(wù)的入口。

.NET Core開(kāi)發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時(shí)任務(wù)

Program.cs中,依舊是類型的通過(guò)創(chuàng)建一個(gè)IHost并啟動(dòng)運(yùn)行。為了方便進(jìn)行依賴注入,可以創(chuàng)建一個(gè)IServiceCollection的擴(kuò)展方法來(lái)進(jìn)行服務(wù)的注冊(cè),接下來(lái)一步步介紹。

進(jìn)行服務(wù)注冊(cè)之前,先將需要引用的包通過(guò)Nuget安裝一下。安裝 Quartz 來(lái)實(shí)現(xiàn)定時(shí)執(zhí)行任務(wù)。另外由于需求需要調(diào)用api接口即需要使用HttpClient發(fā)送請(qǐng)求,所以還需要另外引入包 Microsoft.Extentsions.Http 。由于需要部署成WindowService,需要引入包 Microsoft.Extensions.Hosting.WindowsServices 。

首先定義Job,即執(zhí)行任務(wù)的具體業(yè)務(wù)邏輯。創(chuàng)建一個(gè)SendMsgJob類,繼承IJob接口,并實(shí)現(xiàn)Execute方法。Execute方法就是到了設(shè)定好的時(shí)間點(diǎn)時(shí)執(zhí)行的方法。這里即是實(shí)現(xiàn)了使用注冊(cè)的HttpClient來(lái)發(fā)送消息的過(guò)程。

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>
    /// 定時(shí)執(zhí)行
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public async Task Execute(IJobExecutionContext context)
    {
        _logger.LogInformation($"開(kāi)始執(zhí)行定時(shí)任務(wù)");
        //從httpClientFactory獲取我們注冊(cè)的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è)置它讓其定時(shí)執(zhí)行即可。來(lái)到Work.cs,替換掉原來(lái)的默認(rèn)演示代碼,換之配置Job執(zhí)行策略的代碼。使用Quartz配置Job大致分為這么幾部

  1. 創(chuàng)建調(diào)度器 Scheduler 

  2. 創(chuàng)建Job實(shí)例

  3. 創(chuàng)建觸發(fā)器來(lái)控制Job的執(zhí)行策略

  4. 將Job實(shí)例和觸發(fā)器實(shí)例配對(duì)注冊(cè)進(jìn)調(diào)度器中

  5. 啟動(dòng)調(diào)度器

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("服務(wù)啟動(dòng)");

        //創(chuàng)建一個(gè)調(diào)度器
        var scheduler = await StdSchedulerFactory.GetDefaultScheduler(stoppingToken);
        //創(chuàng)建Job
        var sendMsgJob = JobBuilder.Create<SendMsgJob>()
        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)于定時(shí)任務(wù)的配置告一段落,接下來(lái)將所需的服務(wù)注冊(cè)到服務(wù)容器中。根據(jù)之前所說(shuō)的,我們創(chuàng)建一個(gè)擴(kuò)展方法來(lái)管理我們需要注冊(cè)的服務(wù)。

public static class DependencyInject
{
    /// <summary>
    /// 定義擴(kuò)展方法,注冊(cè)服務(wù)
    /// </summary>
    public static IServiceCollection AddMyServices(this IServiceCollection services, IConfiguration config)
    {
        //配置文件
        services.Configure<AppSettings>(config);

        //注冊(cè)“命名HttpClient”,并為其配置攔截器
        services.AddHttpClient("ApiClient", client =>
        {
            client.BaseAddress = new Uri(config["ApiBaseUrl"]);
        }).AddHttpMessageHandler(_ => new AuthenticRequestDelegatingHandler());

        //注冊(cè)任務(wù)
        services.AddSingleton<SendMsgJob>();

        return services;
    }
}

修改Program.cs,調(diào)用新增的擴(kuò)展方法

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) =>
            {
                //注冊(cè)服務(wù)
                services.AddMyServices(hostContext.Configuration)
                AddHostedService<Worker>();
            });
    }
}

到此,主要的代碼就介紹完了。為了調(diào)試,可以修改設(shè)定好的定時(shí)執(zhí)行時(shí)間(比如一分鐘之后),來(lái)測(cè)試是否能夠成功。修改完觸發(fā)器的觸發(fā)時(shí)間后,直接運(yùn)行項(xiàng)目。但是遺憾的是,任務(wù)并沒(méi)有定時(shí)觸發(fā)。這是什么原因呢?其實(shí)是因?yàn)殡m然我們將我們自定義的Job注入的服務(wù)容器,但是調(diào)度器創(chuàng)建Job實(shí)例時(shí),并不是從我們的服務(wù)容器去取的,而是調(diào)度器自己走默認(rèn)的實(shí)例化。解決方法是我們?yōu)檎{(diào)度器指定JobFactory來(lái)重寫(xiě)實(shí)例化Job類型的規(guī)則。

首先創(chuàng)建一個(gè)MyJobFactory并繼承IJobFactory接口,實(shí)現(xiàn)方法 NewJob ,這個(gè)方法便是工廠實(shí)例化Job的方法,我們可以在這里將實(shí)例化Job的方式改寫(xiě)成從服務(wù)容器中獲取實(shí)例的方式。

namespace WindowsServiceDemo
{
    /// <summary>
    /// Job工廠,從服務(wù)容器中取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 也注冊(cè)到服務(wù)容器中,即在 AddMyServices 擴(kuò)展方法中添加

1 //添加Job工廠2 services.AddSingleton<MyJobFactory>();

接下來(lái)將調(diào)度器的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("服務(wù)啟動(dòng)");

        //創(chuàng)建一個(gè)調(diào)度器
        var scheduler = await StdSchedulerFactory.GetDefaultScheduler(stoppingToken);

        //指定自定義的JobFactory
        scheduler.JobFactory = _jobFactory;

        //創(chuàng)建Job
        var sendMsgJob = JobBuilder.Create<SendMsgJob>()
        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á)我們?cè)谟|發(fā)器中設(shè)置的時(shí)間點(diǎn), SendMsgJob 的 Execute 方法便會(huì)成功觸發(fā)。

開(kāi)發(fā)完成后,現(xiàn)在剩下的任務(wù)就是如何將項(xiàng)目發(fā)布成一個(gè)WindowsService。來(lái)到 Program.cs 下,需要進(jìn)行一些改動(dòng)

 public static IHostBuilder CreateHostBuilder(string[] args) =>     Host.CreateDefaultBuilder(args)         .UseWindowsService()    //按照Windows Service運(yùn)行 .ConfigureServices((hostContext, services) =>         {             //注冊(cè)服務(wù)
             services.AddMyServices(hostContext.Configuration)                     .AddHostedService<Worker>();         });

重新編譯項(xiàng)目成功后,我們便可以使用sc.exe來(lái)部署成為windows服務(wù)。以管理員身份啟動(dòng)命令行,執(zhí)行

> sc.exe create WindowsServiceDemo binPath="D:\workspace\WindowsServiceDemo\WindowsServiceDemo\bin\Debug\netcoreapp3.1\WindowsServiceDemo.exe"
[SC] CreateService 成功

此時(shí)打開(kāi)服務(wù)面板,便可以看到剛剛部署好的 WindowsServiceDemo 服務(wù)了。

.NET Core開(kāi)發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時(shí)任務(wù)

到此,關(guān)于“.NET Core開(kāi)發(fā)Windows服務(wù)之怎么使用Quartz執(zhí)行定時(shí)任務(wù)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI