docker部署xxl-job 通用反射執行器

ingxx發表於2019-07-31

原因

最近在公司寫一些job,公司使用的是spring boot提供的註解形式實現的。
這樣在自測的時候很麻煩,而且測試提測的時候需要修改cron表示式->提交git->jenkins打包重啟

解決方案

查閱資料後決定選用任務排程平臺,有很多優秀的任務排程平臺,選擇xxl-job是因為文件清晰、使用簡單、基於遠端RPC呼叫、官方提供spring boot例子。

部署

首先需要執行官網提供的sql
使用docker下載映象 這裡最新版本是2.0.2

docker pull xuxueli/xxl-job-admin:2.0.2

然後執行docker映象 注意修改引數

docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://資料庫地址:3306/xxl-job?Unicode=true&characterEncoding=UTF-8 --spring.datasource.password=資料庫密碼 --spring.mail.host=smtp.163.com --spring.mail.username=郵箱名 --spring.mail.password=郵箱密碼 --xxl.job.login.password=登入密碼" -p 8080:8080 -v /tmp:/data/applogs --name xxl-job-admin --privileged=true  -d xuxueli/xxl-job-admin:2.0.2

注意一些引數如郵箱可以省略

在專案中配置

這裡配置使用官網示例中的spring boot配置

@Configuration
public class XxlJobConfig {
    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);

    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;

    @Value("${xxl.job.executor.appname}")
    private String appName;

    @Value("${xxl.job.executor.ip}")
    private String ip;

    @Value("${xxl.job.executor.port}")
    private int port;

    @Value("${xxl.job.accessToken}")
    private String accessToken;

    @Value("${xxl.job.executor.logpath}")
    private String logPath;

    @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays;


    @Bean(initMethod = "start", destroyMethod = "destroy")
    public XxlJobSpringExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppName(appName);
        xxlJobSpringExecutor.setIp(ip);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);

        return xxlJobSpringExecutor;
    }
}

官網給出的執行器配置說明

### 排程中心部署跟地址 [選填]:如排程中心叢集部署存在多個地址則用逗號分隔。執行器將會使用該地址進行"執行器心跳註冊"和"任務結果回撥";為空則關閉自動註冊;
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin

### 執行器AppName [選填]:執行器心跳註冊分組依據;為空則關閉自動註冊
xxl.job.executor.appname=xxl-job-executor-sample

### 執行器IP [選填]:預設為空表示自動獲取IP,多網路卡時可手動設定指定IP,該IP不會繫結Host僅作為通訊實用;地址資訊用於 "執行器註冊" 和 "排程中心請求並觸發任務";
xxl.job.executor.ip=

### 執行器埠號 [選填]:小於等於0則自動獲取;預設埠為9999,單機部署多個執行器時,注意要配置不同執行器埠;
xxl.job.executor.port=9999

### 執行器通訊TOKEN [選填]:非空時啟用;
xxl.job.accessToken=

### 執行器執行日誌檔案儲存磁碟路徑 [選填] :需要對該路徑擁有讀寫許可權;為空則使用預設路徑;
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler

### 執行器日誌儲存天數 [選填] :值大於3時生效,啟用執行器Log檔案定期清理功能,否則不生效;
xxl.job.executor.logretentiondays=-1

執行器

官方給出了不少執行器,但是要在原有專案上改造需要自己寫執行器,當然可以使用一個任務寫一個執行器,這樣執行器就會很多,很難以維護。所以這裡採用反射的方式
首先是全類名執行器使用反射的方式

@Component
@JobHandler(value = "BeanByClassHandler")
public class BeanByClassHandler extends IJobHandler {
    @Autowired
    private ApplicationContext applicationContext;

    //根據完整類名 通過反射執行指定方法
    @Override
    public ReturnT<String> execute(String param) throws Exception {
        XxlJobLogger.log(param);
        if (param == null || param.equals("")) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "引數不能為空!");
        }
        String[] split = param.split(",");
        if (split == null || split.length < 2) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "引數格式錯誤,應為 完整類名,方法名");
        }
        Class taskBeanClass = null;
        try {
            taskBeanClass = Class.forName(split[0]);
        } catch (Exception e) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "類" + split[0] + "不存在");
        }
        Method method = null;
        try {
            method = taskBeanClass.getMethod(split[1]);
        } catch (Exception e) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "方法" + split[1] + "不存在");
        }
        Object o = applicationContext.getBean(taskBeanClass);
        if (o == null) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "在Application中類不存在");
        }
        try {
            method.invoke(o);
        } catch (Exception e) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "方法執行失敗");
        }
        return new ReturnT<>(ReturnT.SUCCESS_CODE, "執行成功");

    }
}

這樣全類名很長所以可以使用spring管理beanName獲得例項進行反射

@Component
@JobHandler(value = "BeanByNameHandler")
public class BeanByNameHandler extends IJobHandler {
    @Autowired
    private ApplicationContext applicationContext;
    //根據spring管理的bean name獲取指定類
    @Override
    public ReturnT<String> execute(String param) throws Exception {
        XxlJobLogger.log(param);
        if (param == null || param.equals("")) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "引數不能為空!");
        }
        String[] split = param.split(",");
        if (split == null || split.length < 2) {
            return new ReturnT<>(ReturnT.FAIL_CODE, "引數格式錯誤,應為bean名稱,方法名");
        }
        Object o = applicationContext.getBean(split[0]);
        if(o == null){
            return new ReturnT<>(ReturnT.FAIL_CODE,"類在applicationContext中不存在");
        }
        Method method;
        try {
            method = o.getClass().getMethod(split[1]);
        }catch (Exception e){
            return new ReturnT<>(ReturnT.FAIL_CODE,"方法"+split[1]+"不存在");
        }
        try {
            method.invoke(o);
        }catch (Exception e){
            return new ReturnT<>(ReturnT.FAIL_CODE,"呼叫方法失敗");
        }
        return new ReturnT<>(ReturnT.SUCCESS_CODE,"呼叫job成功");
    }
}

呼叫

在web介面新建任務 引數為 全類名,方法名即可。如下圖所示
docker部署xxl-job 通用反射執行器
當然也可以執行shell指令碼
docker部署xxl-job 通用反射執行器

缺點

  1. xxl-job只支援mysql資料庫,公司使用的Oracle 所以docker需要執行一個mysql資料庫
  2. xxl-job截至2.0.2版本沒有提供多使用者及許可權管理

相關文章