在系統開發不可以避免的要使用到定時任務,簡單的任務可以使用spring的@Scheduled注解或者quartz來實現,但對於復雜的任務最好使用分布式的調度框架來處理,這樣可以部署集群,保證系統的擴展性及高可用性。本文主要介紹XXL-JOB的基本使用,詳細說明請參考官方文檔:https://www.xuxueli.com/xxl-job;文中使用到的軟件及版本:XXL-JOB 2.2.0、SpringBoot 2.2.5.RELEASE、Java 1.8.0_191、MySQL 5.7。
1、@Scheduled及Quartz的不足
1.1、@Scheduled的不足
Spring的@Scheduled對於單機的簡單任務使用起來很方便,但只能單節點運行,不利於橫向擴展。
1.2、Quartz的不足
Quartz作為開源作業調度中的佼佼者,是作業調度的首選。但是集群環境中Quartz存在以下問題:
問題一:調用API的的方式操作任務,不人性化;
問題二:需要持久化業務QuartzJobBean到底層數據表中,系統侵入性相當嚴重;
問題三:調度邏輯和QuartzJobBean耦合在同一個項目中,這將導致一個問題,在調度任務數量逐漸增多,同時調度任務邏輯逐漸加重的情況下,此時調度系統的性能將大大受限於業務;
問題四:quartz底層以“搶占式”獲取DB鎖並由搶占成功節點負責運行任務,會導致節點負載懸殊非常大;
XXL-JOB彌補了quartz的上述不足之處。
2、XXL-JOB使用
2.1、下載源碼
下載地址:https://github.com/xuxueli/xxl-job,下載后用idea打開:

2.2、初始化調度數據庫
SQL腳本位置為:
/xxl-job/doc/db/tables_xxl_job.sql
2.3、配置部署調度中心(xxl-job-admin)
2.3.1、配置修改
配置文件路徑為:
/xxl-job/xxl-job-admin/src/main/resources/application.properties
修改配置文件中的數據庫的相關信息,其他參數根據需要修改:
spring.datasource.url=jdbc:mysql://10.49.196.10:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai spring.datasource.username=admin spring.datasource.password=Root_123! spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2.3.2、部署調度中心
調度中心是一個SpringBoot的工程,在本地可以直接運行,或打成jar包到服務器上運行。
調度中心訪問地址:http://localhost:8080/xxl-job-admin (該地址執行器將會使用到,作為回調地址)
默認登錄賬號:admin/123456
2.3.3、調度中心集群部署
調度中心支持集群部署,提升調度系統容災和可用性。
調度中心集群部署時,幾點要求和建議:
DB配置保持一致;
集群機器時鍾保持一致(單機集群忽視);
建議:推薦通過nginx為調度中心集群做負載均衡,分配域名。調度中心訪問、執行器回調配置、調用API服務等操作均通過該域名進行。
2.4、配置部署執行器項目
可以直接在運行XXL-JOB自帶的樣例執行器項目xxl-job-executor-sample-springboot(需要修改數據庫等配置參數再運行),這里自己使用SpringBoot來開發執行器。
2.4.1、引入依賴
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.2.0</version>
</dependency>
2.4.2、執行器配置文件修改
拷貝/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties中的配置信息到自己SpringBoot工程中的配置文件中,並根據需要修改對應的配置信息。
# web port server.port=8081 # no web #spring.main.web-environment=false # log config logging.config=classpath:logback.xml ### xxl-job admin address list, such as "http://address" or "http://address01,http://address02" xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin ### xxl-job, access token xxl.job.accessToken= ### xxl-job executor appname xxl.job.executor.appname=xxl-job-executor-sample ### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null xxl.job.executor.address= ### xxl-job executor server-info xxl.job.executor.ip=10.39.196.58 xxl.job.executor.port=9999 ### xxl-job executor log-path xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler ### xxl-job executor log-retention-days xxl.job.executor.logretentiondays=30
2.4.3、執行器配置
@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; @Bean public 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; } /** * 針對多網卡、容器內部署等情況,可借助 "spring-cloud-commons" 提供的 "InetUtils" 組件靈活定制注冊IP; * * 1、引入依賴: * <dependency> * <groupId>org.springframework.cloud</groupId> * <artifactId>spring-cloud-commons</artifactId> * <version>${version}</version> * </dependency> * * 2、配置文件,或者容器啟動變量 * spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.' * * 3、獲取IP * String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); */ }
可以直接拷貝/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/core/config/XxlJobConfig.java到自己的工程中。
2.4.4、部署執行器
執行器是一個SpringBoot的工程,在本地可以直接運行,或打成jar包到服務器上運行。
2.4.5、執行器集群部署(可選)
執行器支持集群部署,提升調度系統可用性,同時提升任務處理能力。
執行器集群部署時,幾點要求和建議:
執行器回調地址(xxl.job.admin.addresses)需要保持一致;執行器根據該配置進行執行器自動注冊等操作。
同一個執行器集群內AppName(xxl.job.executor.appname)需要保持一致;調度中心根據該配置動態發現不同集群的在線執行器列表。
2.5、任務創建
2.5.1、BEAN模式任務
任務以JobHandler方式維護在執行器端;前台結合 "JobHandler" 屬性匹配執行器中任務。
2.5.1.1、開發JobHandler
在執行器的項目中新建類,方法上增加@XxlJob注解即表示一個JobHandler。
@Component public class TestJob { protected static Logger logger = LoggerFactory.getLogger(TestJob.class); @XxlJob("testJobHandler") public ReturnT<String> testJobHandler(String param) throws Exception { XxlJobLogger.log("test-JOB, Hello World."); for (int i = 0; i < 5; i++) { //XxlJobLogger打印的日志可以在控制台查看 XxlJobLogger.log("beat at:" + i); logger.info("beat at:" + i); TimeUnit.SECONDS.sleep(2); } return ReturnT.SUCCESS; } }
2.5.1.2、前台配置任務
JobHandler填的值對應上一步@XxlJob中的值。

2.5.2、GLUE模式(Java)任務
任務以源碼方式維護在調度中心;該模式的任務實際上是一段繼承自IJobHandler的Java類代碼,它在執行器項目中運行,可使用@Resource/@Autowire注入執行器里中的其他服務.

保存后在“操作”中點擊GLUE IDE:

