3千字带你搞懂XXL-JOB任务调度平台
思維導(dǎo)圖
文章已收錄Github精選,歡迎Star:https://github.com/yehongzhi/learningSummary
一、概述
在平時的業(yè)務(wù)場景中,經(jīng)常有一些場景需要使用定時任務(wù),比如:
- 時間驅(qū)動的場景:某個時間點發(fā)送優(yōu)惠券,發(fā)送短信等等。
- 批量處理數(shù)據(jù):批量統(tǒng)計上個月的賬單,統(tǒng)計上個月銷售數(shù)據(jù)等等。
- 固定頻率的場景:每隔5分鐘需要執(zhí)行一次。
所以定時任務(wù)在平時開發(fā)中并不少見,而且對于現(xiàn)在快速消費的時代,每天都需要發(fā)送各種推送,消息都需要依賴定時任務(wù)去完成,應(yīng)用非常廣泛。
二、為什么需要任務(wù)調(diào)度平臺
在Java中,傳統(tǒng)的定時任務(wù)實現(xiàn)方案,比如Timer,Quartz等都或多或少存在一些問題:
- 不支持集群、不支持統(tǒng)計、沒有管理平臺、沒有失敗報警、沒有監(jiān)控等等
而且在現(xiàn)在分布式的架構(gòu)中,有一些場景需要分布式任務(wù)調(diào)度:
- 同一個服務(wù)多個實例的任務(wù)存在互斥時,需要統(tǒng)一的調(diào)度。
- 任務(wù)調(diào)度需要支持高可用、監(jiān)控、故障告警。
- 需要統(tǒng)一管理和追蹤各個服務(wù)節(jié)點任務(wù)調(diào)度的結(jié)果,需要記錄保存任務(wù)屬性信息等。
顯然傳統(tǒng)的定時任務(wù)已經(jīng)不滿足現(xiàn)在的分布式架構(gòu),所以需要一個分布式任務(wù)調(diào)度平臺,目前比較主流的是elasticjob和xxl-job。
elasticjob由當(dāng)當(dāng)網(wǎng)開源,目前github有6.5k的Star,使用的公司在官網(wǎng)登記有76家。
跟xxl-job不同的是,elasticjob是采用zookeeper實現(xiàn)分布式協(xié)調(diào),實現(xiàn)任務(wù)高可用以及分片。
三、為什么選擇XXL-JOB
實際上更多公司選擇xxl-job,目前xxl-job的github上有15.7k個star,登記公司有348個。毫無疑問elasticjob和xxl-job都是非常優(yōu)秀的技術(shù)框架,接下來我們進一步對比討論,探索一下為什么更多公司會選擇xxl-job。
首先先介紹一下xxl-job,這是出自大眾點評許雪里(xxl就是作者名字的拼音首字母)的開源項目,官網(wǎng)上介紹這是一個輕量級分布式任務(wù)調(diào)度框架,其核心設(shè)計目標(biāo)是開發(fā)迅速、學(xué)習(xí)簡單、輕量級、易擴展。跟elasticjob不同,xxl-job環(huán)境依賴于mysql,不用ZooKeeper,這也是最大的不同。
elasticjob的初衷是為了面對高并發(fā)復(fù)雜的業(yè)務(wù),即使是在業(yè)務(wù)量大,服務(wù)器多的時候也能做好任務(wù)調(diào)度,盡可能的利用服務(wù)器的資源。使用ZooKeeper使其具有高可用、一致性的,而且還具有良好的擴展性。官網(wǎng)上寫elasticjob是無中心化的,通過ZooKeeper的選舉機制選舉出主服務(wù)器,如果主服務(wù)器掛了,會重新選舉新的主服務(wù)器。因此elasticjob具有良好的擴展性和可用性,但是使用和運維有一定的復(fù)雜。
xxl-job則相反,是通過一個中心式的調(diào)度平臺,調(diào)度多個執(zhí)行器執(zhí)行任務(wù),調(diào)度中心通過DB鎖保證集群分布式調(diào)度的一致性,這樣擴展執(zhí)行器會增大DB的壓力,但是如果實際上這里數(shù)據(jù)庫只是負(fù)責(zé)任務(wù)的調(diào)度執(zhí)行。但是如果沒有大量的執(zhí)行器的話和任務(wù)的情況,是不會造成數(shù)據(jù)庫壓力的。實際上大部分公司任務(wù)數(shù),執(zhí)行器并不多(雖然面試經(jīng)常會問一些高并發(fā)的問題)。
相對來說,xxl-job中心式的調(diào)度平臺輕量級,開箱即用,操作簡易,上手快,與SpringBoot有非常好的集成,而且監(jiān)控界面就集成在調(diào)度中心,界面又簡潔,對于企業(yè)維護起來成本不高,還有失敗的郵件告警等等。這就使很多企業(yè)選擇xxl-job做調(diào)度平臺。
四、安裝
4.1 拉取源碼
搭建xxl-job很簡單,有docker拉取鏡像部署和源碼編譯兩種方式,docker部署的方式比較簡單,我就講源碼編譯的方式。首先到github拉取xxl-job源碼到本地。
4.2 導(dǎo)入IDEA
拉取源碼下來后,可以看到項目結(jié)構(gòu),如下:
導(dǎo)入到IDEA,配置一下Maven,下載相關(guān)的jar包,稍等一下后,就可以看到這樣的項目:
4.3 初始化數(shù)據(jù)庫
前面講過xxl-job需要依賴mysql,所以需要初始化數(shù)據(jù)庫,在xxl-jobdocdb路徑下找到tables_xxl_job.sql文件。在mysql上運行sql文件。
4.4 配置文件
接著就改一下配置文件,在admin項目下找到application.properties文件。
### 調(diào)度中心JDBC鏈接 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password= spring.datasource.driver-class-name=com.mysql.jdbc.Driver ### 報警郵箱 spring.mail.host=smtp.qq.com spring.mail.port=25 spring.mail.username=xxx@qq.com spring.mail.password=xxx spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true spring.mail.properties.mail.smtp.starttls.required=true spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory ### 調(diào)度中心通訊TOKEN [選填]:非空時啟用; xxl.job.accessToken= ### 調(diào)度中心國際化配置 [必填]: 默認(rèn)為 "zh_CN"/中文簡體, 可選范圍為 "zh_CN"/中文簡體, "zh_TC"/中文繁體 and "en"/英文; xxl.job.i18n=zh_CN ## 調(diào)度線程池最大線程配置【必填】 xxl.job.triggerpool.fast.max=200 xxl.job.triggerpool.slow.max=100 ### 調(diào)度中心日志表數(shù)據(jù)保存天數(shù) [必填]:過期日志自動清理;限制大于等于7時生效,否則, 如-1,關(guān)閉自動清理功能; xxl.job.logretentiondays=104.5 編譯運行
簡單一點直接跑admin項目的main方法啟動也行。
如果部署在服務(wù)器呢,那我們需要打包成jar包,在IDEA利用Maven插件打包。
然后在xxl-jobxxl-job-admintarget路徑下,找到j(luò)ar包。
然后就得到j(luò)ar包了,使用java -jar命令就可以啟動了。
到這里就已經(jīng)完成了!打開瀏覽器,輸入http://localhost:8080/xxl-job-admin進入管理頁面。默認(rèn)賬號/密碼:admin/123456。
五、永遠(yuǎn)的HelloWord
部署了調(diào)度中心之后,需要往調(diào)度中心注冊執(zhí)行器,添加調(diào)度任務(wù)。接下來就參考xxl-job寫一個簡單的例子。
首先創(chuàng)建一個SpringBoot項目,名字叫"xxljob-demo",添加依賴。
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><!-- 官網(wǎng)的demo是2.2.1,中央maven倉庫還沒有,所以就用2.2.0 --><groupId>com.xuxueli</groupId><artifactId>xxl-job-core</artifactId><version>2.2.0</version></dependency> </dependencies>接著修改application.properties。
# web port server.port=8081 # log config logging.config=classpath:logback.xml spring.application.name=xxljob-demo ### 調(diào)度中心部署跟地址 [選填]:如調(diào)度中心集群部署存在多個地址則用逗號分隔。執(zhí)行器將會使用該地址進行"執(zhí)行器心跳注冊"和"任務(wù)結(jié)果回調(diào)";為空則關(guān)閉自動注冊; xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin ### 執(zhí)行器通訊TOKEN [選填]:非空時啟用; xxl.job.accessToken= ### 執(zhí)行器AppName [選填]:執(zhí)行器心跳注冊分組依據(jù);為空則關(guān)閉自動注冊 xxl.job.executor.appname=xxl-job-demo ### 執(zhí)行器注冊 [選填]:優(yōu)先使用該配置作為注冊地址,為空時使用內(nèi)嵌服務(wù) ”IP:PORT“ 作為注冊地址。從而更靈活的支持容器類型執(zhí)行器動態(tài)IP和動態(tài)映射端口問題。 xxl.job.executor.address= ### 執(zhí)行器IP [選填]:默認(rèn)為空表示自動獲取IP,多網(wǎng)卡時可手動設(shè)置指定IP,該IP不會綁定Host僅作為通訊實用;地址信息用于 "執(zhí)行器注冊" 和 "調(diào)度中心請求并觸發(fā)任務(wù)"; xxl.job.executor.ip= ### 執(zhí)行器端口號 [選填]:小于等于0則自動獲取;默認(rèn)端口為9999,單機部署多個執(zhí)行器時,注意要配置不同執(zhí)行器端口; xxl.job.executor.port=9999 ### 執(zhí)行器運行日志文件存儲磁盤路徑 [選填] :需要對該路徑擁有讀寫權(quán)限;為空則使用默認(rèn)路徑; xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler ### 執(zhí)行器日志文件保存天數(shù) [選填] : 過期日志自動清理, 限制值大于等于3時生效; 否則, 如-1, 關(guān)閉自動清理功能; xxl.job.executor.logretentiondays=10接著寫一個配置類XxlJobConfig。
@Configuration public class XxlJobConfig {private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);@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;@Beanpublic XxlJobSpringExecutor xxlJobExecutor() {logger.info(">>>>>>>>>>> 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;} }接著編寫一個任務(wù)類XxlJobDemoHandler,使用Bean模式。
@Component public class XxlJobDemoHandler {/*** Bean模式,一個方法為一個任務(wù)* 1、在Spring Bean實例中,開發(fā)Job方法,方式格式要求為 "public ReturnT<String> execute(String param)"* 2、為Job方法添加注解 "@XxlJob(value="自定義jobhandler名稱", init = "JobHandler初始化方法", destroy = "JobHandler銷毀方法")",注解value值對應(yīng)的是調(diào)度中心新建任務(wù)的JobHandler屬性的值。* 3、執(zhí)行日志:需要通過 "XxlJobLogger.log" 打印執(zhí)行日志;*/@XxlJob("demoJobHandler")public ReturnT<String> demoJobHandler(String param) throws Exception {XxlJobLogger.log("java, Hello World~~~");XxlJobLogger.log("param:" + param);return ReturnT.SUCCESS;} }在resources目錄下,添加logback.xml文件。
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false" scan="true" scanPeriod="1 seconds"><contextName>logback</contextName><property name="log.path" value="/data/applogs/xxl-job/xxl-job-executor-sample-springboot.log"/><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${log.path}</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern></rollingPolicy><encoder><pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n</pattern></encoder></appender><root level="info"><appender-ref ref="console"/><appender-ref ref="file"/></root> </configuration>寫完之后啟動服務(wù),然后可以打開管理界面,找到執(zhí)行器管理,添加執(zhí)行器。
接著到任務(wù)管理,添加任務(wù)。
最后我們可以到任務(wù)管理去測試一下,運行demoJobHandler。
點擊保存后,會立即執(zhí)行。點擊查看日志,可以看到任務(wù)執(zhí)行的歷史日志記錄。
打開剛剛執(zhí)行的執(zhí)行日志,我們可以看到,運行成功。
這就是簡單的Demo演示,非常簡單,上手也快。
六、談?wù)劶軜?gòu)設(shè)計
下面簡單地說一下xxl-job的架構(gòu),我們先看官網(wǎng)提供的一張架構(gòu)圖來分析。
從架構(gòu)圖可以看出,分別有調(diào)度中心和執(zhí)行器兩大組成部分
- 調(diào)度中心。負(fù)責(zé)管理調(diào)度信息,按照調(diào)度配置發(fā)出調(diào)度請求,自身不承擔(dān)業(yè)務(wù)代碼。支持可視化界面,可以在調(diào)度中心對任務(wù)進行新增,更新,刪除,會實時生效。支持監(jiān)控調(diào)度結(jié)果,查看執(zhí)行日志,查看調(diào)度任務(wù)統(tǒng)計報表,任務(wù)失敗告警等等。
- 執(zhí)行器。負(fù)責(zé)接收調(diào)度請求,執(zhí)行調(diào)度任務(wù)的業(yè)務(wù)邏輯。執(zhí)行器啟動后需要注冊到調(diào)度中心。接收調(diào)度中心的發(fā)出的執(zhí)行請求,終止請求,日志請求等等。
接下來我們看一下xxl-job的工作原理。
- 任務(wù)執(zhí)行器根據(jù)配置的調(diào)度中心的地址,自動注冊到調(diào)度中心。
- 達(dá)到任務(wù)觸發(fā)條件,調(diào)度中心下發(fā)任務(wù)。
- 執(zhí)行器基于線程池執(zhí)行任務(wù),并把執(zhí)行結(jié)果放入內(nèi)存隊列中、把執(zhí)行日志寫入日志文件中。
- 執(zhí)行器的回調(diào)線程消費內(nèi)存隊列中的執(zhí)行結(jié)果,主動上報給調(diào)度中心。
- 當(dāng)用戶在調(diào)度中心查看任務(wù)日志,調(diào)度中心請求任務(wù)執(zhí)行器,任務(wù)執(zhí)行器讀取任務(wù)日志文件并返回日志詳情。
絮叨
看完以上的內(nèi)容,基本算入門了。實際上,xxl-job還有很多功能,要深入學(xué)習(xí),還需要到官網(wǎng)去研究探索。最好就是自己在本地搭建一個xxl-job來玩玩,動手實踐是學(xué)得最快的學(xué)習(xí)方式。
?
原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的3千字带你搞懂XXL-JOB任务调度平台的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何使用Arthas提高日常开发效率?
- 下一篇: Azkaban业务流程如何转化为Data