springboot整合分佈式任務調度xxl-job

此 demo 主要演示了 Spring Boot 如何集成 XXL-JOB 實現分佈式定時任務,並提供繞過 xxl-job-admin 對定時任務的管理的方法,包括定時任務列表,觸發器列表,新增定時任務,刪除定時任務,停止定時任務,啓動定時任務,修改定時任務,手動觸發定時任務。

xxl-job-admin調度中心

  • 克隆 調度中心代碼

    $ git clone https://github.com/xuxueli/xxl-job.git

  • 修改 application.properties

  server.port=8084
  spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?Unicode=true&characterEncoding=UTF-8&useSSL=false
  spring.datasource.username=root
  spring.datasource.password=root

啓動xxl-job-admin調度中心

Run XxlJobAdminApplication

在瀏覽器輸入: http://localhost:8084/xxl-job-admin

默認用戶名密碼:admin/admin

xxl

編寫執行器項目

pom.xml

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>com.xuxueli</groupId>
        <artifactId>xxl-job-core</artifactId>
        <version>2.1.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-commons -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-commons</artifactId>
        <version>2.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.7</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!--改造成API的方式時需要-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.1.0</version>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>28.2-jre</version>
    </dependency>

編寫配置類

  • XxlJobProps
/**
 * Created by haoxiaoyong on 2020/1/10 下午 4:52
 * e-mail: [email protected]
 * github:https://github.com/haoxiaoyong1014
 * Blog: www.haoxiaoyong.cn
 */
@Data
@ConfigurationProperties(prefix = "xxl.job")
public class XxlJobProps {

    /**
     * 調度中心配置
     */
    private XxlJobAdminProps admin;

    /**
     * 執行器配置
     */
    private XxlJobExecutorProps executor;

    /**
     * 與調度中心交互的accessToken
     */
    private String accessToken;

    @Data
    public static class XxlJobAdminProps {
        /**
         * 調度中心地址
         */
        private String address;
    }
    @Data
    public static class XxlJobExecutorProps {
        /**
         * 執行器名稱
         */
        private String appName;

        /**
         * 執行器 IP
         */
        private String ip;

        /**
         * 執行器端口
         */
        private int port;

        /**
         * 執行器日誌
         */
        private String logPath;

        /**
         * 執行器日誌保留天數
         */
        private int logRetentionDays;
    }

}

  • 配置文件
# web port
server:
  port: 8082

### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
xxl:
  job:
    # 執行器通訊TOKEN [選填]:非空時啓用;
    access-token:
    admin:
     # 調度中心部署跟地址 [選填]:如調度中心集羣部署存在多個地址則用逗號分隔。執行器將會使用該地址進行"執行器心跳註冊"和"任務結果回調";爲空則關閉自動註冊;
      address: http://127.0.0.1:8084/xxl-job-admin
    executor:
      # 執行器AppName [選填]:執行器心跳註冊分組依據;爲空則關閉自動註冊
      app-name: xxl-job-executor-sample
      # 執行器IP [選填]:默認爲空表示自動獲取IP,多網卡時可手動設置指定IP,該IP不會綁定Host僅作爲通訊實用;地址信息用於 "執行器註冊" 和 "調度中心請求並觸發任務";
      ip:
      # 執行器端口號 [選填]:小於等於0則自動獲取;默認端口爲9999,單機部署多個執行器時,注意要配置不同執行器端口;
      port: 9999
      # 執行器運行日誌文件存儲磁盤路徑 [選填] :需要對該路徑擁有讀寫權限;爲空則使用默認路徑;
      log-path: /data/applogs/xxl-job/jobhandler
      # 執行器日誌保存天數 [選填] :值大於3時生效,啓用執行器Log文件定期清理功能,否則不生效;
      log-retention-days: 30

  • 編寫自動裝配類 JobConfig.java
@Slf4j
@Configuration
@EnableConfigurationProperties(XxlJobProps.class)
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class JobConfig {
    
    private final XxlJobProps xxlJobProps;
    private final InetUtils inetUtils;
    
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        log.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(xxlJobProps.getAdmin().getAddress());
        xxlJobSpringExecutor.setAppName(xxlJobProps.getExecutor().getAppName());
        String ip = xxlJobProps.getExecutor().getIp();
        if (StringUtils.isBlank(ip)) {
            ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
        }
        log.info("IP地址爲: " + ip);
        log.info("AdminAddresses地址爲: " + xxlJobProps.getAdmin().getAddress());
        xxlJobSpringExecutor.setIp(ip);
        xxlJobSpringExecutor.setPort(xxlJobProps.getExecutor().getPort());
        xxlJobSpringExecutor.setAccessToken(xxlJobProps.getAccessToken());
        xxlJobSpringExecutor.setLogPath(xxlJobProps.getExecutor().getLogPath());
        xxlJobSpringExecutor.setLogRetentionDays(xxlJobProps.getExecutor().getLogRetentionDays());
        return xxlJobSpringExecutor;
    }
}

編寫具體的定時邏輯 TestJobHandler

@Component
public class TestJobHandler {

    @Autowired
    private InService inService;

    @XxlJob("jobHandler")
    public ReturnT<String> execute(String param) throws InterruptedException {
        for (int i = 0; i < 5; i++) {
            XxlJobLogger.log("beat at:" + i);
            TimeUnit.SECONDS.sleep(2);
        }

        inService.xxl();
       return ReturnT.SUCCESS;
    }
}
@Service
public class InService {

    public void xxl(){
        System.out.println("Hello World......");
    }

}

啓動執行器 JobApplication

  • 將啓動的執行器添加到調度中心

    執行器管理 - 新增執行器

    image.png

  • 添加定時任務

    任務管理 - 新增 - 保存

    image.png

    這裏的JobHandler要和@XxlJob()註解中的值一致;

  • 點擊操作,執行一次任務,控制檯輸出Hello World… 你也可以在ui界面中查看日誌

使用API添加定時任務

實際場景中,如果添加定時任務都需要手動在 xxl-job-admin 去操作,這樣可能比較麻煩,用戶更希望在自己的頁面,添加定時任務參數、定時調度表達式,然後通過 API 的方式添加定時任務

克隆調度中心代碼

git clone https://github.com/xuxueli/xxl-job/

改造xxl-job-admin

  • JobGroupController中新增
	// 添加執行器列表
	@RequestMapping("/list")
	@ResponseBody
    // 去除權限校驗
	@PermissionLimit(limit = false)
	public ReturnT<List<XxlJobGroup>> list(){
		return  new ReturnT<>(xxlJobGroupDao.findAll());
	}
  • 修改 JobInfoController

    // 分別在 pageList、add、update、remove、pause、start、triggerJob 方法上添加註解,去除權限校驗
    @PermissionLimit(limit = false)
    

改造執行器項目

  • 添加手動觸發類

    /**
     * Created by haoxiaoyong on 2020/1/11 下午 3:39
     * e-mail: [email protected]
     * github:https://github.com/haoxiaoyong1014
     * Blog: www.haoxiaoyong.cn
     */
    @Slf4j
    @RestController
    @RequestMapping("/xxl-job")
    @RequiredArgsConstructor(onConstructor_ = @Autowired)
    public class ManualOperateController {
        private final static String baseUri = "http://127.0.0.1:8084/xxl-job-admin";
        private final static String JOB_INFO_URI = "/jobinfo";
        private final static String JOB_GROUP_URI = "/jobgroup";
    
        /**
         * 任務組列表,xxl-job叫做觸發器列表
         */
        @GetMapping("/group")
        public String xxlJobGroup() {
            HttpResponse execute = HttpUtil.createGet(baseUri + JOB_GROUP_URI + "/list").execute();
            log.info("【execute】= {}", execute);
            return execute.body();
        }
    
        /**
         * 分頁任務列表
         *
         * @param page 當前頁,第一頁 -> 0
         * @param size 每頁條數,默認10
         * @return 分頁任務列表
         */
        @GetMapping("/list")
        public String xxlJobList(Integer page, Integer size) {
            Map<String, Object> jobInfo = Maps.newHashMap();
            jobInfo.put("start", page != null ? page : 0);
            jobInfo.put("length", size != null ? size : 10);
            jobInfo.put("jobGroup", 2);
            jobInfo.put("triggerStatus", -1);
    
            HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/pageList").form(jobInfo).execute();
            log.info("【execute】= {}", execute);
            return execute.body();
        }
    
        /**
         * 測試手動保存任務
         */
        @GetMapping("/add")
        public String xxlJobAdd() {
            Map<String, Object> jobInfo = Maps.newHashMap();
            jobInfo.put("jobGroup", 2);
            jobInfo.put("jobCron", "0 0/1 * * * ? *");
            jobInfo.put("jobDesc", "手動添加的任務");
            jobInfo.put("author", "admin");
            jobInfo.put("executorRouteStrategy", "ROUND");
            jobInfo.put("executorHandler", "demoTask");
            jobInfo.put("executorParam", "手動添加的任務的參數");
            jobInfo.put("executorBlockStrategy", ExecutorBlockStrategyEnum.SERIAL_EXECUTION);
            jobInfo.put("glueType", GlueTypeEnum.BEAN);
    
            HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/add").form(jobInfo).execute();
            log.info("【execute】= {}", execute);
            return execute.body();
        }
    
        /**
         * 測試手動觸發一次任務
         */
        @GetMapping("/trigger")
        public String xxlJobTrigger() {
            Map<String, Object> jobInfo = Maps.newHashMap();
            jobInfo.put("id", 5);
            jobInfo.put("executorParam", JSONUtil.toJsonStr(jobInfo));
    
            HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/trigger").form(jobInfo).execute();
            log.info("【execute】= {}", execute);
            return execute.body();
        }
    
        /**
         * 測試手動刪除任務
         */
        @GetMapping("/remove")
        public String xxlJobRemove() {
            Map<String, Object> jobInfo = Maps.newHashMap();
            jobInfo.put("id", 4);
    
            HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/remove").form(jobInfo).execute();
            log.info("【execute】= {}", execute);
            return execute.body();
        }
    
        /**
         * 測試手動停止任務
         */
        @GetMapping("/stop")
        public String xxlJobStop() {
            Map<String, Object> jobInfo = Maps.newHashMap();
            jobInfo.put("id", 4);
    
            HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/stop").form(jobInfo).execute();
            log.info("【execute】= {}", execute);
            return execute.body();
        }
    
        /**
         * 測試手動啓動任務
         */
        @GetMapping("/start")
        public String xxlJobStart() {
            Map<String, Object> jobInfo = Maps.newHashMap();
            jobInfo.put("id", 4);
    
            HttpResponse execute = HttpUtil.createGet(baseUri + JOB_INFO_URI + "/start").form(jobInfo).execute();
            log.info("【execute】= {}", execute);
            return execute.body();
        }
    }
    
    

測試

以手動觸發一次任務爲例

  • 啓動 xxl-job-admin

  • 啓動執行器項目

  • 訪問localhost:8082/xxl-job/trigger

  • 控制檯日誌

    xxl-job.png

擴展:使用Docker 鏡像方式搭建調度中心

使用docker鏡像方式部署xxl-job-admin;

附上執行腳本: xxl-job.sh

不使用腳本也可以直接在命令窗口鍵入:

docker run -d --rm  -e PARAMS="--spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 --spring.datasource.use
rname=root --spring.datasource.password=123456"  -p 8680:8080  --name xxl-job-admin xuxueli/xxl-job-admin:2.1.1

配置好數據庫名稱以及密碼即可!

此示例完整代碼

發佈了51 篇原創文章 · 獲贊 47 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章