Quartz.net的最佳實踐

来源:https://www.cnblogs.com/workcn/archive/2023/06/19/17491550.html
-Advertisement-
Play Games

## 一:背景 ### 1. 講故事 前些天有位朋友找到我,說他的程式跑著跑著就崩潰了,讓我看下怎麼回事,其實沒怎麼回事,抓它的 crash dump 就好,具體怎麼抓也是被問到的一個高頻問題,這裡再補一下鏈接: [.NET程式崩潰了怎麼抓 Dump ? 我總結了三種方案] https://www. ...


Quartz.NET 官網

Quartz.net是什麼

Quartz.NET 是一個功能齊全的開源作業調度系統,他的前身來源於java的Quartz.

Quartz.net安裝和使用

基於visual studio引用安裝,其他IDE類似,或者下載DLL手動引用也是可以的;運行環境基於.net core,源.net程式類似 0  

Quartz.net的架構和關鍵屬性以及方法

三個主要的概念

  • scheduler 作業調度,作業計劃在給定觸發器發生時運行,實際就是領導
  • job 作業,實現簡單 IJob 介面的任何 .NET 類,實際就是幹活的員工
  • trigger 偵聽器,負責捕獲調度事件以監視或控製作業,實際就是監工
可以這樣理解:
監工發現員工偷懶了,報告給領導,領導知道後,給員工派了很多活,導致了員工天天996. 大概是這麼個關係; 同時呢,一個員工可以被多個監工監理,同理一個監工也可以監理多個員工,他們是多對多的關係;多個員工也可以共屬於一個領導,當然也可以一個領導只有一個員工,他們直接也是多對多的關係  

Quartz.net的一些關鍵屬性

類型  
ISchedulerFactory SchedulerBuilder的工廠類
IScheduler 用於與調度程式交互的主要 API
SchedulerBuilder 用於定義/構建調度程式實例,需要 Quartz 3.1 或更高版本
IJobFactory JobBuilder的工廠類
IJob 由您希望由調度程式執行的組件實現的介面
IJobDetail 用於定義作業的實例
JobBuilder 用於定義/構建 JobDetail 實例,這些實例定義作業的實例
TriggerBuilder 用於定義/構建觸發器實例
ITrigger 定義執行給定作業的計劃的一個組件,作業可以有多個關聯的觸發器
ListenerManager 偵聽器事件,例如:執行job工作之前,之後觸發等等,同時也可用於觸發器偵聽
 

IServiceCollectionQuartzConfigurator 參數

Scheduler Name 調度作業的名稱
Scheduler Id SchedulerId
Max Batch Size 同時執行job的最大數量
InterruptJobsOnShutdown  
InterruptJobsOnShutdownWithWait  
BatchTriggerAcquisitionFireAheadTimeWindow  
 

在通用host或者webhost中的最佳實踐

通用host或者webhost代碼是一樣的

執行流程

  1. 在通用主機服務中註入服務AddQuartz,AddQuartzHostedService
  2. 在AddQuartz中配置調度作業的基本屬性(SchedulerId等等)和調度器以及作業(ScheduleJob,AddJob,AddTrigger);可以在這個地方寫入所有的調度作業,也可以寫入一個initjob作業,在主機完全啟動5秒後執行相應的業務(可規避掉某些依賴服務未啟動的問題)
  3. 在initjob中,初始化其他定時任務。官網介紹job只能有一個無參的構造函數,但我親測可以註入(笑臉)
  4. 關於job和reigger的具體參數,可查看官網

如下

以下代碼和執行結果,其中執行順序一目瞭然
代碼
   static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");

            LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
            //通用主機配置
            var build = Host.CreateDefaultBuilder(args)
                .ConfigureServices((host, services) =>
                {
                    Console.WriteLine("--------1");
                    
                    //調度作業的唯一id的唯一標識,用於集群搭建cluster
                    q.SchedulerId = "SchedulerId_01";
                    //配置Quartz服務
                    services.AddQuartz(q =>
                    {
                        Console.WriteLine("--------2");
                        //依賴註入,ISchedulerFactory,Ijob等等
                        q.UseMicrosoftDependencyInjectionJobFactory();

                        //方法一和方法二使用不同方法的寫法,本質基本是一樣的
                        //方法一
                        q.ScheduleJob<InitJob>(
                            trigger =>
                            {
                                Console.WriteLine("--------33");
                                //WithIdentity 綁定觸發器或者job的唯一屬性和組
                                //TriggerKey,JobKey 都是代表唯一個屬性和組
                                trigger.WithIdentity(new TriggerKey("trigger1", "triggergroup1"))
                                       .WithSimpleSchedule(x => x.WithIntervalInSeconds(5))
                                       // .StartAt(DateBuilder.EvenSecondDate(DateTimeOffset.UtcNow.AddSeconds(5)))
                                       //  .WithDailyTimeIntervalSchedule(x => x.WithInterval(10, IntervalUnit.Second))
                                       .WithDescription("init 描述");
                            },
                            jobConfigure =>
                            {
                                Console.WriteLine("--------44");
                                jobConfigure.WithIdentity(new JobKey("Init1", "jobgroup1"));
                            }
                        );

                        //方法二
                        //q.AddJob<InitJob>(opts =>
                        //{
                        //    Console.WriteLine("--------3");
                        //    opts.WithIdentity(new JobKey("Init1", "jobgroup1"));

                        //});

                        //q.AddTrigger(opts =>
                        //{
                        //    Console.WriteLine("--------4");
                        //    //將job添加至觸發器中
                        //    opts.ForJob(new JobKey("Init1", "jobgroup1"))
                        //    .WithIdentity("trigger1", "triggergroup1")
                        //    .WithSimpleSchedule(x =>
                        //    {
                        //        Console.WriteLine("--------6");
                        //        x.WithIntervalInSeconds(5);
                        //        //.RepeatForever();
                        //        //.WithRepeatCount(5);
                        //    });

                        //});

                    });
                    services.AddQuartzHostedService(options =>
                    {    
                        options.WaitForJobsToComplete = true;
                    });

                }).Build();

            //var schedulerFactory =  build.Services.GetService<ISchedulerFactory>();
            //var scheduler = schedulerFactory.GetScheduler();
            build.Run();
            Console.WriteLine("--------7");
        }
    }


    public class SampleJob : IJob
    {
        public SampleJob(ISchedulerFactory schedulerFactory, IJobFactory jobFactory)
        {
            Console.WriteLine("--------8");
        }
        public async Task Execute(IJobExecutionContext context)
        {
            Console.WriteLine("--------9");
            context.JobDetail.JobDataMap.GetString("我是sample的job數據key");
            Console.WriteLine($"我是sample的job數據key: {context.JobDetail.JobDataMap.GetString("我是sample的job數據key")}");
            Console.WriteLine($"我是sample的Trigger數據key: {context.MergedJobDataMap.GetString("我是sample的Trigger數據key")}");

        }
    }
    public class InitJob : IJob
    {
        public ISchedulerFactory _schedulerFactory;
        public IJobFactory _jobFactory;
        public InitJob(ISchedulerFactory schedulerFactory, IJobFactory jobFactory)
        {
            Console.WriteLine("--------12");
            _schedulerFactory = schedulerFactory;
            _jobFactory = jobFactory;
        }
        public async Task Execute(IJobExecutionContext context)
        {
            Console.WriteLine("--------13");
            Console.WriteLine("InitJob Execute " + Random.Shared.Next(0, 100));
            
            //創建job
            IJobDetail job = JobBuilder.Create<SampleJob>()
                                //寫入參數
                                .UsingJobData("我是sample的job數據key", "我是sample的job數據value")
                                .WithIdentity("sample1", "jobgroup1").Build();
            //創建觸發器
            ITrigger trigger = TriggerBuilder.Create()
                                   .UsingJobData("我是sample的Trigger數據key", "我是sample的Trigger數據value")
                                   .WithIdentity("trigger_sample1", "triggergroup1")
                                   .WithDescription("我是描述")
                                   //通過corn符號來創建觸發器
                                   //.WithCronSchedule(taskOptions.CronExpression)
                                   .WithSimpleSchedule(x =>
                                       x.WithIntervalInSeconds(5) //5秒後執行
                                       .RepeatForever()  //重覆
                                    )
                                   .Build();
            //通過工廠獲取一個作業調度
            var scheduler = await _schedulerFactory.GetScheduler();
            //綁定一個job的事件偵聽器,從執行順序上看 new JobListen是一個單例類
            scheduler.ListenerManager.AddJobListener(new JobListen(), KeyMatcher<JobKey>.KeyEquals(new JobKey("sample1", "jobgroup1")));
            //將作業和從觸發器綁定至作業調度上
            await scheduler.ScheduleJob(job, trigger);
            //啟動作業調度
            await scheduler.Start();

            Console.WriteLine("--------14");
        }
    }
    //作業偵聽器
    public class JobListen : JobListenerSupport
    {
        public JobListen()
        {
            Console.WriteLine("--------20");
        }
        public override string Name { get { return "JobListen20"; } }
        //調用job之前執行
        public override Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken)
        {
            Console.WriteLine("--------21");
            return base.JobToBeExecuted(context, cancellationToken);
        }
    }
    //日誌組件
    public class ConsoleLogProvider : ILogProvider
    {
        public Logger GetLogger(string name)
        {
            return (level, func, exception, parameters) =>
            {
                if (level >= LogLevel.Info && func != null)
                {
                    Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters);
                }
                return true;
            };
        }
        public IDisposable OpenNestedContext(string message)
        {
            throw new NotImplementedException();
        }
        public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
        {
            throw new NotImplementedException();
        }
    }
執行結果
  0  
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## 1 目標 不在現有查詢代碼邏輯上做任何改動,實現dao維度的數據源切換(即表維度) ## 2 使用場景 節約bdp的集群資源。接入新的寬表時,通常uat驗證後就會停止集群釋放資源,在對應的查詢伺服器uat環境時需要查詢的是生產庫的表數據(uat庫表因為bdp實時任務停止,沒有數據落入),只進行 ...
  • ## 教程簡介 Tapestry並不是一種單純的MVC框架,它更像MVC框架和模板技術的結合,它不僅包含了前端的MVC框架,還包含了一種視圖層的模板技術,使用Tapestry完全可以與Servlet/JSP API分離,是一種非常優秀的設計。 通過使用Tapestry,開發者完全不需要使用JSP技術 ...
  • `Comparable` 介面的 `compareTo` 方法的升序或降序取決於實現該介面的類的具體實現。按照慣例,`compareTo` 方法應該返回負數、零或正數來指示當前對象是小於、等於還是大於傳入的對象。具體來說: - 如果 `this` 對象小於傳入的對象,則 `compareTo` 應該 ...
  • # 1、概述 ## 功能 Elasticsearch 是一個分散式的 RESTful 搜索和分析引擎,可用來集中存儲您的數據,以便您對形形色色、規模不一的數據進行搜索、索引和分析。 例如: - 在電商網站搜索商品 ![image](https://img2023.cnblogs.com/blog/3 ...
  • # Java 運算符的使用 # 1.算術運算符 ## 算術運算符包括: +, -, *, /, %, ++, --,其中需要註意的是%,++,--; ## % 取模運算也叫做取餘,在 Java 中取餘的規則: a % b = a - a / b * b,如果是小數的話是這樣:a % b = a- ( ...
  • 為了更好的認識函數,我們還要研究值傳遞問題,再研究這個問題之前,我們已經知道了函數之間的值傳遞,是實參變數值傳遞給形參變數,然後讓形參變數在函數內完成相應的功能。但是因為數據類型的不同,這裡的值傳遞產生的對實參變數的效果是不同的 # 1.傳遞數據本質 參數傳遞之間傳遞的肯定是數據,而這種數據本質上是 ...
  • ## 一、概述 ### 1.什麼是多租戶架構? 多租戶架構是指在一個應用中支持多個租戶(Tenant)同時訪問,每個租戶擁有獨立的資源和數據,並且彼此之間完全隔離。通俗來說,多租戶就是把一個應用按照客戶的需求“分割”成多個獨立的實例,每個實例互不幹擾。 ### 2. 多租戶架構的優勢 - 更好地滿足 ...
  • 經過前幾篇文章的講解,初步瞭解ASP.NET Core MVC項目創建,啟動運行,以及命名約定,創建控制器,視圖,模型,接收參數,傳遞數據ViewData,ViewBag,路由,頁面佈局,wwwroot和客戶端庫,Razor語法,EnityFrameworkCore與資料庫,HttpContext,... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...