You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

README.md 14 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. # spring-boot-demo-task-xxl-job
  2. > 此 demo 主要演示了 Spring Boot 如何集成 XXL-JOB 实现分布式定时任务,并提供绕过 `xxl-job-admin` 对定时任务的管理的方法,包括定时任务列表,触发器列表,新增定时任务,删除定时任务,停止定时任务,启动定时任务,修改定时任务,手动触发定时任务。
  3. ## 1. xxl-job-admin调度中心
  4. > https://github.com/xuxueli/xxl-job.git
  5. 克隆 调度中心代码
  6. ```bash
  7. $ git clone https://github.com/xuxueli/xxl-job.git
  8. ```
  9. ### 1.1. 创建调度中心的表结构
  10. 数据库脚本地址:`/xxl-job/doc/db/tables_xxl_job.sql`
  11. ### 1.2. 修改 application.properties
  12. ```properties
  13. server.port=18080
  14. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?Unicode=true&characterEncoding=UTF-8&useSSL=false
  15. spring.datasource.username=root
  16. spring.datasource.password=root
  17. ```
  18. ### 1.3. 修改日志配置文件 logback.xml
  19. ```xml
  20. <property name="log.path" value="logs/xxl-job/xxl-job-admin.log"/>
  21. ```
  22. ### 1.4. 启动调度中心
  23. Run `XxlJobAdminApplication`
  24. 默认用户名密码:admin/admin
  25. ![image-20190808105554414](https://static.xkcoding.com/spring-boot-demo/2019-08-08-025555.png)
  26. ![image-20190808105628852](https://static.xkcoding.com/spring-boot-demo/2019-08-08-025629.png)
  27. ## 2. 编写执行器项目
  28. ### 2.1. pom.xml
  29. ```xml
  30. <?xml version="1.0" encoding="UTF-8"?>
  31. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  32. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  33. <modelVersion>4.0.0</modelVersion>
  34. <artifactId>spring-boot-demo-task-xxl-job</artifactId>
  35. <version>1.0.0-SNAPSHOT</version>
  36. <packaging>jar</packaging>
  37. <name>spring-boot-demo-task-xxl-job</name>
  38. <description>Demo project for Spring Boot</description>
  39. <parent>
  40. <groupId>com.xkcoding</groupId>
  41. <artifactId>spring-boot-demo</artifactId>
  42. <version>1.0.0-SNAPSHOT</version>
  43. </parent>
  44. <properties>
  45. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  46. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  47. <java.version>1.8</java.version>
  48. <xxl-job.version>2.1.0</xxl-job.version>
  49. </properties>
  50. <dependencies>
  51. <dependency>
  52. <groupId>org.springframework.boot</groupId>
  53. <artifactId>spring-boot-starter-web</artifactId>
  54. </dependency>
  55. <dependency>
  56. <groupId>org.springframework.boot</groupId>
  57. <artifactId>spring-boot-configuration-processor</artifactId>
  58. <optional>true</optional>
  59. </dependency>
  60. <!-- xxl-job-core -->
  61. <dependency>
  62. <groupId>com.xuxueli</groupId>
  63. <artifactId>xxl-job-core</artifactId>
  64. <version>${xxl-job.version}</version>
  65. </dependency>
  66. <dependency>
  67. <groupId>org.springframework.boot</groupId>
  68. <artifactId>spring-boot-starter-test</artifactId>
  69. <scope>test</scope>
  70. </dependency>
  71. <dependency>
  72. <groupId>cn.hutool</groupId>
  73. <artifactId>hutool-all</artifactId>
  74. </dependency>
  75. <dependency>
  76. <groupId>com.google.guava</groupId>
  77. <artifactId>guava</artifactId>
  78. </dependency>
  79. <dependency>
  80. <groupId>org.projectlombok</groupId>
  81. <artifactId>lombok</artifactId>
  82. <optional>true</optional>
  83. </dependency>
  84. </dependencies>
  85. <build>
  86. <finalName>spring-boot-demo-task-xxl-job</finalName>
  87. <plugins>
  88. <plugin>
  89. <groupId>org.springframework.boot</groupId>
  90. <artifactId>spring-boot-maven-plugin</artifactId>
  91. </plugin>
  92. </plugins>
  93. </build>
  94. </project>
  95. ```
  96. ### 2.2. 编写 配置类 XxlJobProps.java
  97. ```java
  98. /**
  99. * <p>
  100. * xxl-job 配置
  101. * </p>
  102. *
  103. * @author yangkai.shen
  104. * @date Created in 2019-08-07 10:25
  105. */
  106. @Data
  107. @ConfigurationProperties(prefix = "xxl.job")
  108. public class XxlJobProps {
  109. /**
  110. * 调度中心配置
  111. */
  112. private XxlJobAdminProps admin;
  113. /**
  114. * 执行器配置
  115. */
  116. private XxlJobExecutorProps executor;
  117. /**
  118. * 与调度中心交互的accessToken
  119. */
  120. private String accessToken;
  121. @Data
  122. public static class XxlJobAdminProps {
  123. /**
  124. * 调度中心地址
  125. */
  126. private String address;
  127. }
  128. @Data
  129. public static class XxlJobExecutorProps {
  130. /**
  131. * 执行器名称
  132. */
  133. private String appName;
  134. /**
  135. * 执行器 IP
  136. */
  137. private String ip;
  138. /**
  139. * 执行器端口
  140. */
  141. private int port;
  142. /**
  143. * 执行器日志
  144. */
  145. private String logPath;
  146. /**
  147. * 执行器日志保留天数,-1
  148. */
  149. private int logRetentionDays;
  150. }
  151. }
  152. ```
  153. ### 2.3. 编写配置文件 application.yml
  154. ```yaml
  155. server:
  156. port: 8080
  157. servlet:
  158. context-path: /demo
  159. xxl:
  160. job:
  161. # 执行器通讯TOKEN [选填]:非空时启用;
  162. access-token:
  163. admin:
  164. # 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
  165. address: http://localhost:18080/xxl-job-admin
  166. executor:
  167. # 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
  168. app-name: spring-boot-demo-task-xxl-job-executor
  169. # 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
  170. ip:
  171. # 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
  172. port: 9999
  173. # 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
  174. log-path: logs/spring-boot-demo-task-xxl-job/task-log
  175. # 执行器日志保存天数 [选填] :值大于3时生效,启用执行器Log文件定期清理功能,否则不生效;
  176. log-retention-days: -1
  177. ```
  178. ### 2.4. 编写自动装配类 XxlConfig.java
  179. ```java
  180. /**
  181. * <p>
  182. * xxl-job 自动装配
  183. * </p>
  184. *
  185. * @author yangkai.shen
  186. * @date Created in 2019-08-07 10:20
  187. */
  188. @Slf4j
  189. @Configuration
  190. @EnableConfigurationProperties(XxlJobProps.class)
  191. @RequiredArgsConstructor(onConstructor_ = @Autowired)
  192. public class XxlJobConfig {
  193. private final XxlJobProps xxlJobProps;
  194. @Bean(initMethod = "start", destroyMethod = "destroy")
  195. public XxlJobSpringExecutor xxlJobExecutor() {
  196. log.info(">>>>>>>>>>> xxl-job config init.");
  197. XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
  198. xxlJobSpringExecutor.setAdminAddresses(xxlJobProps.getAdmin().getAddress());
  199. xxlJobSpringExecutor.setAccessToken(xxlJobProps.getAccessToken());
  200. xxlJobSpringExecutor.setAppName(xxlJobProps.getExecutor().getAppName());
  201. xxlJobSpringExecutor.setIp(xxlJobProps.getExecutor().getIp());
  202. xxlJobSpringExecutor.setPort(xxlJobProps.getExecutor().getPort());
  203. xxlJobSpringExecutor.setLogPath(xxlJobProps.getExecutor().getLogPath());
  204. xxlJobSpringExecutor.setLogRetentionDays(xxlJobProps.getExecutor().getLogRetentionDays());
  205. return xxlJobSpringExecutor;
  206. }
  207. }
  208. ```
  209. ### 2.5. 编写具体的定时逻辑 DemoTask.java
  210. ```java
  211. /**
  212. * <p>
  213. * 测试定时任务
  214. * </p>
  215. *
  216. * @author yangkai.shen
  217. * @date Created in 2019-08-07 10:15
  218. */
  219. @Slf4j
  220. @Component
  221. @JobHandler("demoTask")
  222. public class DemoTask extends IJobHandler {
  223. /**
  224. * execute handler, invoked when executor receives a scheduling request
  225. *
  226. * @param param 定时任务参数
  227. * @return 执行状态
  228. * @throws Exception 任务异常
  229. */
  230. @Override
  231. public ReturnT<String> execute(String param) throws Exception {
  232. // 可以动态获取传递过来的参数,根据参数不同,当前调度的任务不同
  233. log.info("【param】= {}", param);
  234. XxlJobLogger.log("demo task run at : {}", DateUtil.now());
  235. return RandomUtil.randomInt(1, 11) % 2 == 0 ? SUCCESS : FAIL;
  236. }
  237. }
  238. ```
  239. ### 2.6. 启动执行器
  240. Run `SpringBootDemoTaskXxlJobApplication`
  241. ## 3. 配置定时任务
  242. ### 3.1. 将启动的执行器添加到调度中心
  243. 执行器管理 - 新增执行器
  244. ![image-20190808105910203](https://static.xkcoding.com/spring-boot-demo/2019-08-08-025910.png)
  245. ### 3.2. 添加定时任务
  246. 任务管理 - 新增 - 保存
  247. ![image-20190808110127956](https://static.xkcoding.com/spring-boot-demo/2019-08-08-030128.png)
  248. ### 3.3. 启停定时任务
  249. 任务列表的操作列,拥有以下操作:执行、启动/停止、日志、编辑、删除
  250. 执行:单次触发任务,不影响定时逻辑
  251. 启动:启动定时任务
  252. 停止:停止定时任务
  253. 日志:查看当前任务执行日志
  254. 编辑:更新定时任务
  255. 删除:删除定时任务
  256. ## 4. 使用API添加定时任务
  257. > 实际场景中,如果添加定时任务都需要手动在 xxl-job-admin 去操作,这样可能比较麻烦,用户更希望在自己的页面,添加定时任务参数、定时调度表达式,然后通过 API 的方式添加定时任务
  258. ### 4.1. 改造xxl-job-admin
  259. #### 4.1.1. 修改 JobGroupController.java
  260. ```java
  261. ...
  262. // 添加执行器列表
  263. @RequestMapping("/list")
  264. @ResponseBody
  265. // 去除权限校验
  266. @PermissionLimit(limit = false)
  267. public ReturnT<List<XxlJobGroup>> list(){
  268. return new ReturnT<>(xxlJobGroupDao.findAll());
  269. }
  270. ...
  271. ```
  272. #### 4.1.2. 修改 JobInfoController.java
  273. ```java
  274. // 分别在 pageList、add、update、remove、pause、start、triggerJob 方法上添加注解,去除权限校验
  275. @PermissionLimit(limit = false)
  276. ```
  277. ### 4.2. 改造 执行器项目
  278. #### 4.2.1. 添加手动触发类
  279. ```java
  280. /**
  281. * <p>
  282. * 手动操作 xxl-job
  283. * </p>
  284. *
  285. * @author yangkai.shen
  286. * @date Created in 2019-08-07 14:58
  287. */
  288. @Slf4j
  289. @RestController
  290. @RequestMapping("/xxl-job")
  291. @RequiredArgsConstructor(onConstructor_ = @Autowired)
  292. public class ManualOperateController {
  293. private final static String baseUri = "http://127.0.0.1:18080/xxl-job-admin";
  294. private final static String JOB_INFO_URI = "/jobinfo";
  295. private final static String JOB_GROUP_URI = "/jobgroup";
  296. /**
  297. * 任务组列表,xxl-job叫做触发器列表
  298. */
  299. @GetMapping("/group")
  300. public String xxlJobGroup() {
  301. HttpResponse execute = HttpUtil.createGet(baseUri + JOB_GROUP_URI + "/list").execute();
  302. log.info("【execute】= {}", execute);
  303. return execute.body();
  304. }
  305. /**
  306. * 分页任务列表
  307. * @param page 当前页,第一页 -> 0
  308. * @param size 每页条数,默认10
  309. * @return 分页任务列表
  310. */
  311. @GetMapping("/list")
  312. public String xxlJobList(Integer page, Integer size) {
  313. Map<String, Object> jobInfo = Maps.newHashMap();
  314. jobInfo.put("start", page != null ? page : 0);
  315. jobInfo.put("length", size != null ? size : 10);
  316. jobInfo.put("jobGroup", 2);
  317. jobInfo.put("triggerStatus", -1);
  318. HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/pageList").form(jobInfo).execute();
  319. log.info("【execute】= {}", execute);
  320. return execute.body();
  321. }
  322. /**
  323. * 测试手动保存任务
  324. */
  325. @GetMapping("/add")
  326. public String xxlJobAdd() {
  327. Map<String, Object> jobInfo = Maps.newHashMap();
  328. jobInfo.put("jobGroup", 2);
  329. jobInfo.put("jobCron", "0 0/1 * * * ? *");
  330. jobInfo.put("jobDesc", "手动添加的任务");
  331. jobInfo.put("author", "admin");
  332. jobInfo.put("executorRouteStrategy", "ROUND");
  333. jobInfo.put("executorHandler", "demoTask");
  334. jobInfo.put("executorParam", "手动添加的任务的参数");
  335. jobInfo.put("executorBlockStrategy", ExecutorBlockStrategyEnum.SERIAL_EXECUTION);
  336. jobInfo.put("glueType", GlueTypeEnum.BEAN);
  337. HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/add").form(jobInfo).execute();
  338. log.info("【execute】= {}", execute);
  339. return execute.body();
  340. }
  341. /**
  342. * 测试手动触发一次任务
  343. */
  344. @GetMapping("/trigger")
  345. public String xxlJobTrigger() {
  346. Map<String, Object> jobInfo = Maps.newHashMap();
  347. jobInfo.put("id", 4);
  348. jobInfo.put("executorParam", JSONUtil.toJsonStr(jobInfo));
  349. HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/trigger").form(jobInfo).execute();
  350. log.info("【execute】= {}", execute);
  351. return execute.body();
  352. }
  353. /**
  354. * 测试手动删除任务
  355. */
  356. @GetMapping("/remove")
  357. public String xxlJobRemove() {
  358. Map<String, Object> jobInfo = Maps.newHashMap();
  359. jobInfo.put("id", 4);
  360. HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/remove").form(jobInfo).execute();
  361. log.info("【execute】= {}", execute);
  362. return execute.body();
  363. }
  364. /**
  365. * 测试手动停止任务
  366. */
  367. @GetMapping("/stop")
  368. public String xxlJobStop() {
  369. Map<String, Object> jobInfo = Maps.newHashMap();
  370. jobInfo.put("id", 4);
  371. HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/stop").form(jobInfo).execute();
  372. log.info("【execute】= {}", execute);
  373. return execute.body();
  374. }
  375. /**
  376. * 测试手动停止任务
  377. */
  378. @GetMapping("/start")
  379. public String xxlJobStart() {
  380. Map<String, Object> jobInfo = Maps.newHashMap();
  381. jobInfo.put("id", 4);
  382. HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/start").form(jobInfo).execute();
  383. log.info("【execute】= {}", execute);
  384. return execute.body();
  385. }
  386. }
  387. ```
  388. > 后端其余代码请 clone 本项目,查看具体代码
  389. ## 参考
  390. - [《分布式任务调度平台xxl-job》](http://www.xuxueli.com/xxl-job/#/)

一个用来深度学习并实战 spring boot 的项目,目前总共包含 66 个集成demo,已经完成 55 个。