SpringBoot整合XXLJob

来源:https://www.cnblogs.com/Naylor/archive/2023/10/10/17753898.html
-Advertisement-
Play Games

目錄XXLJob簡介特性模塊安裝調度中心初始化資料庫配置啟動整合執行器pomymlXxlJobConfig啟動執行器實踐簡單的定時任務在執行器創建任務在調度中心創建執行器在調度中心創建任務帶前置和後置處理的定時任務XxlJob註解詳解創建帶前(後)置處理的任務父子任務父子執行器關聯父子任務執行器側l ...


目錄

XXLJob簡介

XXLJob是一個分散式任務調度平臺,優點:開發迅速、學習簡單、輕量級、易擴展。是大眾點評員工xxl創建並維護,基於 GPL-3.0 開源,可放心商用,目前已經擁有龐大的使用群體。

簡單來說,就是一個定時任務中間件,類似的產品有噹噹網開源的Elastic-Job。

特性

  • 簡單:產品本身基於Java開發的,鑒於JVM的優秀,安裝部署集成簡單,和其他中間件、框架相比,避免了各種環境問題;同時程式員在項目中集成進來也很簡單,有手就行。
  • 觸發策略豐富:按照設置的Cron表達式觸發、固定間隔觸發、固定延時觸發、API(事件)觸發、人工觸發、父子任務觸發
  • 支持失敗重試
  • 包含簡單的告警和豐富的log,玩過定時任務的都知道,可觀測性在實際項目中是多麼的關鍵
  • 支持任務分片:例如將一個大任務,拆分為多個小任務,然後分給不同的執行器節點同時執行
  • 數據加密:調度中心和執行器之間的通訊進行數據加密,提升調度信息安全性
  • 豐富的鉤子:各種回調可細粒度觀察任務的生命周期
  • 跨語言:調度中心與執行器提供語言無關的 RESTful API 服務,第三方任意語言可據此對接調度中心或者實現執行器。除此之外,還提供了 “多任務模式”和“httpJobHandler”等其他跨語言方案;
  • 容器化:對雲原生支持友好
  • 有webUI管理系統
  • 高可用:支持集群部署,可彈性擴容
  • 自動發現:執行器會周期性自動註冊任務, 調度中心將會自動發現註冊上來的執行器並將任務分配給執行器同時觸發執行

模塊

  • 調度中心:管理執行器、任務;查看任務日誌、告警、報表;提供一個webUI管理系統,有簡單的用戶登錄賬號管理功能;依賴資料庫
  • 執行器:執行器是獨立的RESTFul服務,一般集成在業務服務中,它會開闢一個9999(預設)埠和調度中心交互

由上可知,XXLJob為C/S架構,調度中心本身可以高可用部署,執行器集成在業務微服務中,當業務微服務多實例部署的時候,執行器也就可以達到分散式和高可用了。

安裝調度中心

初始化資料庫

調度中心依賴資料庫,安裝前需先初始化資料庫,初始化腳本可從github中獲取 https://github.com/xuxueli/xxl-job/tree/master/doc/db

表說明:

  • xxl_job_lock:任務調度鎖表
  • xxl_job_group:執行器信息表,維護任務執行器信息
  • xxl_job_info:調度擴展信息表: 用於保存XXL-JOB調度任務的擴展信息,如任務分組、任務名、機器地址、執行器、執行入參和報警郵件等等
  • xxl_job_log:調度日誌表: 用於保存XXL-JOB任務調度的歷史信息,如調度結果、執行結果、調度入參、調度機器和執行器等等
  • xxl_job_log_report:調度日誌報表:用戶存儲XXL-JOB任務調度日誌的報表,調度中心報表功能頁面會用到
  • xxl_job_logglue:任務GLUE日誌:用於保存GLUE更新曆史,用於支持GLUE的版本回溯功能
  • xxl_job_registry:執行器註冊表,維護線上的執行器和調度中心機器地址信息
  • xxl_job_user:系統用戶表;

配置

  • 運行埠
  • 資料庫連接
  • 報警郵箱
  • token:一個串,非必填,設置之後,執行器也需要配置此串,才能和調度中心交互,相當於一個簡單的認證
  • 線程池配置

參見官方文檔,本文重點放在SpringBoot整合XXLJob。調度中心的部署,尤其是高可用部署,後續單獨開篇。

啟動

調度中心就是一個SpringBoot程式,以xxljob2.4.0版本為例,其依賴的SpringBoot版本為 2.7.9,所以任何啟動SpringBoot 的方式都可以,webui的預設訪問地址:http://ip:8080/xxl-job-admin

整合執行器

pom


<dependency>    
    <groupId>com.xuxueli</groupId>    
    <artifactId>xxl-job-core</artifactId>  
    <version>2.4.0</version>
</dependency>


yml



server:
  port: 9009
logging:
  level:
    com.ramble: debug
xxl:
  job:
    admin:
      #調度中心部署根地址 [選填]:如調度中心集群部署存在多個地址則用逗號分隔。執行器將會使用該地址進行"執行器心跳註冊"和"任務結果回調";為空則關閉自動註冊;
      addresses: http://127.0.0.1:8080/xxl-job-admin
      
    #執行器通訊TOKEN [選填]:非空時啟用;
    accessToken: 
    executor:
      #執行器AppName [選填]:執行器心跳註冊分組依據;為空則關閉自動註冊
      appname: xxljob-demo-service
      #${spring.application.name}
      #執行器註冊 [選填]:優先使用該配置作為註冊地址,為空時使用內嵌服務 ”IP:PORT“ 作為註冊地址。從而更靈活的支持容器類型執行器動態IP和動態映射埠問題。
      address: ""
      #執行器IP [選填]:預設為空表示自動獲取IP,多網卡時可手動設置指定IP,該IP不會綁定Host僅作為通訊實用;地址信息用於 "執行器註冊" 和 "調度中心請求並觸發任務";
      ip: ""
      #執行器埠號 [選填]:小於等於0則自動獲取;預設埠為9999,單機部署多個執行器時,註意要配置不同執行器埠;
      port: 0
      ###${server-port}
      #執行器運行日誌文件存儲磁碟路徑 [選填] :需要對該路徑擁有讀寫許可權;為空則使用預設路徑;
      logpath: ./logs/xxl-job/jobhandler
      #執行器日誌文件保存天數 [選填] : 過期日誌自動清理, 限制值大於等於3時生效; 否則, 如-1, 關閉自動清理功能;
      logretentiondays: 30


XxlJobConfig



@Slf4j
@Configuration
public class XxlJobConfig {
    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;
    @Value("${xxl.job.accessToken}")
    private String accessToken;
    @Value("${xxl.job.executor.appname}")
    private String appname;
    @Value("${xxl.job.executor.address}")
    private String address;
    @Value("${xxl.job.executor.ip}")
    private String ip;
    @Value("${xxl.job.executor.port}")
    private int port;
    @Value("${xxl.job.executor.logpath}")
    private String logPath;
    @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays;
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        log.info(">>>>>>>>>>> start xxl-job config init");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppname(appname);
        xxlJobSpringExecutor.setAddress(address);
        xxlJobSpringExecutor.setIp(ip);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
        return xxlJobSpringExecutor;
    }
}



啟動執行器

如果一切順利,將在控制台看到如下輸出:


2023-10-09 11:36:23.162  INFO 15736 --- [       Thread-4] com.xxl.job.core.server.EmbedServer      : >>>>>>>>>>> xxl-job remoting server start success, nettype = class com.xxl.job.core.server.EmbedServer, port = 9999

看到這個,說明執行器配置已生效,執行器已經順利和調度中心聯繫上了。

實踐

可以簡單的將任務分兩個步驟,第一在執行器中定義一個任務具體需要乾什麼,第二在調度中心觸發定義的任務

簡單的定時任務

在執行器創建任務

在業務微服務中創建


@Slf4j
@Component
public class DemoJob {
    /**
     * 簡單的job,調度器
     */
    @XxlJob("job1")
    public void job1() {
        log.debug("do job1");
    }
}


在調度中心創建執行器

  • 對於調度中心來說,執行器可能是多實例的,通過AppName確定為同一個集群
  • 執行器可以手動指定,也可以自動發現

創建成功之後可以在執行器列表看到。圖片為編輯頁面,所以可以看到已經有機器地址了。

在調度中心創建任務

  • JobHandler:需要和業務微服務中創建的任務名稱一致

新增完畢之後啟動,如果一切順利,將可以在業務微服務中看到如下log:

2023-10-09 11:50:33.050 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob           : do job1
2023-10-09 11:50:38.044 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob           : do job1
2023-10-09 11:50:44.100 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob           : do job1
2023-10-09 11:50:48.052 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob           : do job1
2023-10-09 11:50:53.043 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob           : do job1

每5s執行了一次任務

帶前置和後置處理的定時任務

XxlJob註解詳解

XxlJob註解有三個參數:
value:JobHandler的名稱,需要在執行器和調度中心保持一致
init:定時任務前置處理,僅在定時任務首次運行前執行一次
destory:定時任務後置處理,僅在定時任務銷毀的時候執行一次

這裡需要註意:

  • 一個定時任務可能反覆執行多次,例如我們設置了固定10s執行一次,那麼可能每10s就執行一次,但是前置處理和後置處理僅執行一次,前置處理和後置處理僅針對整個任務,而非任務的一次執行
  • 在前置處理中拋異常並不會阻止任務的創建和執行
  • 可以在前置處理和後置處理中訪問資料庫或者做一些業務邏輯

創建帶前(後)置處理的任務



@Slf4j
@Component
public class DemoJob {
    /**
     * 創建帶前(後)置處理的任務
     * Job方法添加註解 "@XxlJob(value="自定義jobhandler名稱", init = "JobHandler初始化方法", destroy = "JobHandler銷毀方法")",註解value值對應的是調度中心新建任務的JobHandler屬性的值。
     * <p>
     * 執行日誌:需要通過 "XxlJobHelper.log" 列印執行日誌;
     * <p>
     * 任務結果:預設任務結果為 "成功" 狀態,不需要主動設置;如有訴求,比如設置任務結果為失敗,可以通過 "XxlJobHelper.handleFail/handleSuccess" 自主設置任務結果;
     */
    @XxlJob(value = "job2", init = "job2Init", destroy = "job2Destroy")
    public void job2() throws InterruptedException {
        LocalDateTime now = LocalDateTime.now();
        XxlJobHelper.log("進入job2,time={}", now.toString());
        log.debug("job2 - doSomething ...");
        Thread.sleep(2000);
        XxlJobHelper.log("離開job2,time={}", now.toString());
    }
    public void job2Init() {
        log.debug("job2Init - doSomething ...");
    }
    public void job2Destroy() {
        log.debug("job2Destroy - doSomething ...");
    }
}


調度中心需要創建對應job2的任務並啟動。如果一切順利將在執行器控制台看到如下log:



2023-10-09 13:31:44.104 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob           : job2Init - doSomething ...
2023-10-09 13:31:44.110 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob           : job2 - doSomething ...
2023-10-09 13:31:54.052 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob           : job2 - doSomething ...
2023-10-09 13:32:04.053 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob           : job2 - doSomething ...
2023-10-09 13:32:14.054 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob           : job2 - doSomething ...
Disconnected from the target VM, address: '127.0.0.1:50819', transport: 'socket'
2023-10-09 13:32:21.606  INFO 35848 --- [       Thread-4] com.xxl.job.core.server.EmbedServer      : >>>>>>>>>>> xxl-job remoting server stop.
2023-10-09 13:32:21.618  INFO 35848 --- [rRegistryThread] c.x.j.c.thread.ExecutorRegistryThread    : >>>>>>>>>>> xxl-job registry-remove success, registryParam:RegistryParam{registryGroup='EXECUTOR', registryKey='xxljob-demo-service', registryValue='http://192.168.3.191:9999/'}, registryResult:ReturnT [code=200, msg=null, content=null]
2023-10-09 13:32:21.618  INFO 35848 --- [rRegistryThread] c.x.j.c.thread.ExecutorRegistryThread    : >>>>>>>>>>> xxl-job, executor registry thread destroy.
2023-10-09 13:32:21.621  INFO 35848 --- [ionShutdownHook] com.xxl.job.core.server.EmbedServer      : >>>>>>>>>>> xxl-job remoting server destroy success.
2023-10-09 13:32:21.622 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob           : job2Destroy - doSomething ...
2023-10-09 13:32:21.622  INFO 35848 --- [7-1696829504104] com.xxl.job.core.thread.JobThread        : >>>>>>>>>>> xxl-job JobThread stoped, hashCode:Thread[xxl-job, JobThread-27-1696829504104,10,main]
2023-10-09 13:32:21.623  INFO 35848 --- [FileCleanThread] c.x.j.core.thread.JobLogFileCleanThread  : >>>>>>>>>>> xxl-job, executor JobLogFileCleanThread thread destroy.

通過log可以觀測到:

  • 執行器啟動的時候,在任務首次運行前列印了init方法中的log
  • 而後定時任務按照既定執行策略執行
  • 當執行器服務停止的時候,列印了destory方法中的log
  • 通過XxlJobHelper.log 列印的日誌,可以在調度中心 調度日誌--->操作--->執行日誌 中看到

父子任務

當兩個任務需要關聯觸發的時候可以使用父子任務的功能,當然了子任務還可以有子任務。

這種情況只需要啟動父任務,不需要啟動子任務,當父任務執行成功了,會觸發子任務的啟動。當父任務執行失敗了,不會觸發子任務的啟動。

父子執行器



 /**
     * 父任務
     */
     @XxlJob("jobFather")
     public void jobFather() {
         // 創建一個新的隨機數生成器
         Random random = new Random();
         // 生成一個0到100之間的隨機整數
         int randomNumber = random.nextInt(101);
         if (randomNumber % 2 == 0) {
             log.debug("do - jobFather - success");
             XxlJobHelper.handleSuccess();
         } else {
             log.debug("do - jobFather - fail");
             XxlJobHelper.handleFail("調用XxlJobHelper.handleFail,調度中心就任務此任務執行失敗");
         }
     }
 
     /**
      * 子任務
      */
     @XxlJob("jobChild")
     public void jobChild() {
         log.debug("do - jobChild");
     }


  • 父、子執行器並沒有什麼特殊的地方
  • 在調度中心手動關聯父、子任務後,父執行器執行成功後就會觸發子執行器執行
  • 上述父執行器中通過XxlJobHelper.handleSuccess() 告訴調度中心,此任務執行成功了
  • 上述父執行器中通過XxlJobHelper.handleFail() 告訴調度中心,此任務執行失敗了,調度中心將不會觸發子任務

關聯父子任務

  • 子任務、父任務分別創建
  • 然後編輯父任務,將子任務的id填寫到“子任務ID”中,此時就關聯上了
  • 啟動父任務,不需要啟動子任務
  • 子任務的啟動交由調度中心觸發,當父任務執行成功了,調度中心自然會啟動子任務
  • 如果將子任務啟動了,那麼子任務將擁有兩個觸發維度,第一是根據子任務自身的調度類型和調度速度觸發,第二是調度中心觸發

執行器側log



2023-10-10 09:13:00.276  INFO 19228 --- [       Thread-4] com.xxl.job.core.server.EmbedServer      : >>>>>>>>>>> xxl-job remoting server start success, nettype = class com.xxl.job.core.server.EmbedServer, port = 9999
2023-10-10 09:13:23.549  INFO 19228 --- [Pool-1699379094] c.xxl.job.core.executor.XxlJobExecutor   : >>>>>>>>>>> xxl-job regist JobThread success, jobId:29, handler:com.xxl.job.core.handler.impl.MethodJobHandler@2b43f314[class com.ramble.xxljob.task.DemoJob#jobFather]
2023-10-10 09:13:23.552 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob           : do - jobFather - fail
2023-10-10 09:13:28.495 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob           : do - jobFather - fail
2023-10-10 09:13:34.500 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob           : do - jobFather - success
2023-10-10 09:13:34.511  INFO 19228 --- [Pool-1699379094] c.xxl.job.core.executor.XxlJobExecutor   : >>>>>>>>>>> xxl-job regist JobThread success, jobId:30, handler:com.xxl.job.core.handler.impl.MethodJobHandler@7e3d2ebd[class com.ramble.xxljob.task.DemoJob#jobChild]
2023-10-10 09:13:34.512 DEBUG 19228 --- [0-1696900414511] com.ramble.xxljob.task.DemoJob           : do - jobChild
2023-10-10 09:13:38.494 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob           : do - jobFather - fail
2023-10-10 09:13:43.526 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob           : do - jobFather - success
2023-10-10 09:13:43.539 DEBUG 19228 --- [0-1696900414511] com.ramble.xxljob.task.DemoJob           : do - jobChild

通過日誌可以觀察到:

  • 一開始僅將父任務的 JobThread註冊到了調度中心
  • 而後父任務按照調度速度執行
  • 當父任務執行失敗沒有觸發子任務執行
  • 當父任務執行成功首先將子任務的 JobThread 註冊到了調度中心,隨即執行了子任務

調度中心-任務管理詳解

執行器

任務需要綁定到執行器,任務觸發調度時將會自動發現註冊成功的執行器, 實現任務自動發現功能; 另一方面也可以方便的進行任務分組。每個任務必須綁定一個執行器, 可在 "執行器管理" 進行設置

路由策略

當執行器集群部署時,提供豐富的路由策略,包括:

  • FIRST(第一個):固定選擇第一個機器;
  • LAST(最後一個):固定選擇最後一個機器;
  • ROUND(輪詢);
  • RANDOM(隨機):隨機選擇線上的機器;
  • CONSISTENT_HASH(一致性HASH):每個任務按照Hash演算法固定選擇某一臺機器,且所有任務均勻散列在不同機器上。
  • LEAST_FREQUENTLY_USED(最不經常使用):使用頻率最低的機器優先被選舉;
  • LEAST_RECENTLY_USED(最近最久未使用):最久未使用的機器優先被選舉;
  • FAILOVER(故障轉移):按照順序依次進行心跳檢測,第一個心跳檢測成功的機器選定為目標執行器併發起調度;
  • BUSYOVER(忙碌轉移):按照順序依次進行空閑檢測,第一個空閑檢測成功的機器選定為目標執行器併發起調度;
  • SHARDING_BROADCAST(分片廣播):廣播觸發對應集群中所有機器執行一次任務,同時系統自動傳遞分片參數;可根據分片參數開發分片任務;

調度過期策略

  • 忽略:調度過期後,忽略過期的任務,從當前時間開始重新計算下次觸發時間
  • 立即執行一次:調度過期後,立即執行一次,並從當前時間開始重新計算下次觸發時間

阻塞處理策略

調度過於密集執行器來不及處理時的處理策略

  • 單機串列(預設):調度請求進入單機執行器後,調度請求進入FIFO隊列並以串列方式運行
  • 丟棄後續調度:調度請求進入單機執行器後,發現執行器存在運行的調度任務,本次請求將會被丟棄並標記為失敗
  • 覆蓋之前調度:調度請求進入單機執行器後,發現執行器存在運行的調度任務,將會終止運行中的調度任務並清空隊列,然後運行本次調度任務

超時和重試

  • 任務超時時間:支持自定義任務超時時間,任務運行超時將會主動中斷任務
  • 失敗重試次數;支持自定義任務失敗重試次數,當任務失敗時將會按照預設的失敗重試次數主動進行重試

引用

郵箱:[email protected] 技術交流QQ群:1158377441 歡迎關註我的微信公眾號,後續博文將在公眾號首發: pSr8iCD.png
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前幾天,在我們的技術交流群里看到有小伙伴問:有沒有練手搭建Redis集群的方式推薦: 這裡不禁讓我想到,對於各種集群和分散式基礎設施的搭建,其實是每個開發者進階時候都要經歷的一個成長過程。但是,這裡對於不少開發者來說,卻又面臨著一個現實問題:我沒有足夠的資源(主機或配置)去嘗試和練習。 最近,DD剛 ...
  • C++ Windows下使用Cmake編譯Poco庫 1.編譯前準備: 先配置OpenSSL環境 (openssl version -a查看) 如果openssl是1.0.*版本,Poco版本最高用1.9.4。 如果1.1或者更高,用最新版本。 2.編譯命令(演示使用VS2022編譯v140版本Wi ...
  • 對於rpc項目,在接受大佬指導的時候曾問過對於長連接和短連接是處理處理的,在面試的時候也被問起socket是長連接還是短連接,發現自己沒有好好思考過這個問題,因此好好總結一下。 前置知識點:rpc基礎,tcp基礎 rpc項目中的長連接與短連接的思考 什麼是rpc項目中的長連接和短連接 類似於http ...
  • 4.1 環境搭建 創建名為spring_mvc_demo2的新module,過程參考3.1節 4.1.1、創建請求控制器 package org.rain.controller; import org.springframework.stereotype.Controller; /** * @aut ...
  • 一、當你擁有一個excel版的介面用例 excel中有用例名稱、url、請求方式和請求參數 二、獲取excel的Url、請求方式和請求參數 # 單獨獲取某個單元格的值,第二行第二列# 第二行數據 row代表行,column代表列# url=sh.cell(row=2,column=2).value# ...
  • 看到一篇 IDEA 快捷鍵的總結,非常全面,分享一下。 本文參考了 IntelliJ IDEA 的官網,列舉了IntelliJ IDEA(Windows 版)的所有快捷鍵。併在此基礎上,為 90% 以上的快捷鍵提供了動圖演示,能夠直觀的看到操作效果。 該快捷鍵共分 16 種,可以方便的按各類查找自己 ...
  • 本文深入探討了Go語言中通道(Channel)的各個方面,從基礎概念到高級應用。文章詳細解析了通道的類型、操作方法以及垃圾回收機制,更進一步通過具體代碼示例展示了通道在數據流處理、任務調度和狀態監控等多個實際應用場景中的作用。本文旨在為讀者提供一個全面而深入的理解,以更有效地使用Go中的通道進行併發 ...
  • 堆疊柱狀圖,是一種用來分解整體、比較各部分的圖。與柱狀圖類似,堆疊柱狀圖常被用於比較不同類別的數值。而且,它的每一類數值內部,又被劃分為多個子類別,這些子類別一般用不同的顏色來指代。 柱狀圖幫助我們觀察“總量”,堆疊柱狀圖則可以同時反映“總量”與“結構”。也就是說,堆疊柱狀圖不僅可以反映總量是多少? ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...