spring boot 接入xxl-job手冊

版權聲明:文章內容由本文作者提供,轉載請註明出處。 https://blog.csdn.net/lich1n/article/details/81407400

xxl-job 分佈式job調度中心接入手冊

項目是spring cloud框架,eureka註冊中心,使用許雪裏的xxl-job,打算後續將其改造爲spring boot工程,接入服務中心。以下是我對其源碼接入spring cloud/boot的使用方式,可改良之處尚多,僅給大家提供參考,方便使用。

xxl-job項目地址:

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

document http://www.xuxueli.com/xxl-job/

對原項目的介紹此處不再細說,可點開上面的參考文檔熟悉
改動後的admin地址:https://github.com/lich1n/my-xxl-job

改動內容:
- 添加了通過業務單號查詢job的方法
- 將jobinfo操作的幾個接口直接暴露出來了,接我們系統權限驗證的我移除了。
- jobinfo表增加業務類型及單號字段

使用方式
將xxl-job-admin打包部署好,自己的業務系統接入job中心啓動即可。上面改動過的admin修改mysql連接地址後即可運行,sql腳本爲resources下的tables_xxl_job.sql

以下是業務模塊的接入過程


spring boot 項目接入步驟


1. 引入依賴
2. yml配置
3. 裝配配置類
4. 創建jobhandler執行器
5. 進入xxl-job控制檯新增執行器
6. 使用新增的執行器開始任務
7. 在業務模塊中操作job的【增、刪、改、查、暫停】


1 . 引入依賴

<!-- xxl-job-core -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>1.9.1</version>
</dependency>

2. yml配置

xxl:
  job:
    admin:
      ### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
      addresses: http://localhost:8080
    ### xxl-job executor address
    executor:
      appname: mktcenter
      ip:
      port: 9888
      ### xxl-job log path
      logpath: /data/applogs/xxl-job/jobhandler
      ### xxl-job log retention days
      logretentiondays: -1
    ### xxl-job, access token
    accessToken:

3. 裝配配置類

package com.bizvane.mktcenterserviceimpl.common.job;
import com.xxl.job.core.executor.XxlJobExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.bizvane.mktcenterserviceimpl.service.jobhandler")
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 XxlJobExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobExecutor xxlJobExecutor = new XxlJobExecutor();
        xxlJobExecutor.setAdminAddresses(adminAddresses);
        xxlJobExecutor.setAppName(appName);
        xxlJobExecutor.setIp(ip);
        xxlJobExecutor.setPort(port);
        xxlJobExecutor.setAccessToken(accessToken);
        xxlJobExecutor.setLogPath(logPath);
        xxlJobExecutor.setLogRetentionDays(-1);

        return xxlJobExecutor;
    }
}

4. 創建jobhandler執行器

package com.bizvane.mktcenterserviceimpl.service.jobhandler;

import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHandler;
import org.springframework.stereotype.Component;

@JobHandler(value="activity")
@Component
public class ActivityJobHandler extends IJobHandler {

    @Override
    public ReturnT<String> execute(String param) throws Exception {
        System.out.println("開始執行生日活動");
        return null;
    }
}

5. 進入xxl-job控制檯新增執行器

注意appname與yml中配置名字保持一致 !
這裏寫圖片描述

6. 使用新增的執行器開始任務
這裏寫圖片描述
JobHandler名字爲步驟4中@JobHandler註解value值
這裏寫圖片描述

保存即可。

7. 在業務模塊中操作job的【增、刪、改、查、暫停】等
jobclient工具類,使用restTemplate進行遠程調用

package com.bizvane.utils.jobutils;

import java.util.Random;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

@Component
public class JobClient {
    @Autowired
    private RestTemplate restTemplate;
    @Value("${xxl.job.admin.addresses}")
    private String[] jobAdminUrl;
    private static String add = "/jobinfo/add";
    private static String update = "/jobinfo/update";
    private static String remove = "/jobinfo/remove";
    private static String pause = "/jobinfo/pause";
    private static String resume = "/jobinfo/resume";
    private static String getJobInfoByBiz = "/jobinfo/getJobInfoByBiz";

    public JobClient() {
    }

    public ResponseEntity<String> addJob(XxlJobInfo xxlJobInfo) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> xxlJobInfoMap = MapUtil.obj2Map(xxlJobInfo);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity(xxlJobInfoMap, headers);
        ResponseEntity<String> response = this.restTemplate.postForEntity(this.getLoadUrl(add), request, String.class, new Object[0]);
        return response;
    }

    public ResponseEntity<String> updateJob(XxlJobInfo xxlJobInfo) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> xxlJobInfoMap = MapUtil.obj2Map(xxlJobInfo);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity(xxlJobInfoMap, headers);
        ResponseEntity<String> response = this.restTemplate.postForEntity(this.getLoadUrl(update), request, String.class, new Object[0]);
        return response;
    }

    public ResponseEntity<String> removeJob(Integer xxlJobInfoId) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<Integer> request = new HttpEntity(xxlJobInfoId, headers);
        ResponseEntity<String> response = this.restTemplate.postForEntity(this.getLoadUrl(remove), request, String.class, new Object[0]);
        return response;
    }

    public ResponseEntity<String> pauseJob(int xxlJobInfoId) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<Integer> request = new HttpEntity(xxlJobInfoId, headers);
        ResponseEntity<String> response = this.restTemplate.postForEntity(this.getLoadUrl(pause), request, String.class, new Object[0]);
        return response;
    }

    public ResponseEntity<String> resumeJob(int xxlJobInfoId) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<Integer> request = new HttpEntity(xxlJobInfoId, headers);
        ResponseEntity<String> response = this.restTemplate.postForEntity(this.getLoadUrl(resume), request, String.class, new Object[0]);
        return response;
    }

    public ResponseEntity<String> getJobInfoByBizJob(XxlJobInfo xxlJobInfo) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> xxlJobInfoMap = MapUtil.obj2Map(xxlJobInfo);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity(xxlJobInfoMap, headers);
        ResponseEntity<String> response = this.restTemplate.postForEntity(this.getLoadUrl(getJobInfoByBiz), request, String.class, new Object[0]);
        return response;
    }

    public String getLoadUrl(String method) {
        int length = this.jobAdminUrl.length;
        Random random = new Random();
        int i = random.nextInt(length);
        String url = this.jobAdminUrl[i] + method;
        return url;
    }
}

map工具類

package com.bizvane.utils.jobutils;

import java.lang.reflect.Field;
import java.util.Collections;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

public class MapUtil {
    public MapUtil() {
    }

    public static MultiValueMap<String, String> obj2Map(Object obj) {
        MultiValueMap<String, String> map = new LinkedMultiValueMap();
        Field[] fields = obj.getClass().getDeclaredFields();
        int i = 0;

        for(int len = fields.length; i < len; ++i) {
            String varName = fields[i].getName();

            try {
                boolean accessFlag = fields[i].isAccessible();
                fields[i].setAccessible(true);
                Object o = fields[i].get(obj);
                if (o != null) {
                    map.put(varName, Collections.singletonList(o.toString()));
                }

                fields[i].setAccessible(accessFlag);
            } catch (IllegalArgumentException var8) {
                var8.printStackTrace();
            } catch (IllegalAccessException var9) {
                var9.printStackTrace();
            }
        }

        return map;
    }

    public static MultiValueMap<String, String> obj2MapWithNull(Object obj) {
        MultiValueMap<String, String> map = new LinkedMultiValueMap();
        Field[] fields = obj.getClass().getDeclaredFields();
        int i = 0;

        for(int len = fields.length; i < len; ++i) {
            String varName = fields[i].getName();

            try {
                boolean accessFlag = fields[i].isAccessible();
                fields[i].setAccessible(true);
                Object o = fields[i].get(obj);
                if (o != null) {
                    map.put(varName, Collections.singletonList(o.toString()));
                } else {
                    map.put(varName, (Object)null);
                }

                fields[i].setAccessible(accessFlag);
            } catch (IllegalArgumentException var8) {
                var8.printStackTrace();
            } catch (IllegalAccessException var9) {
                var9.printStackTrace();
            }
        }

        return map;
    }

    public static MultiValueMap<String, String> obj2MapWithString(Object obj) {
        MultiValueMap<String, String> map = new LinkedMultiValueMap();
        Field[] fields = obj.getClass().getDeclaredFields();
        int i = 0;

        for(int len = fields.length; i < len; ++i) {
            String varName = fields[i].getName();

            try {
                boolean accessFlag = fields[i].isAccessible();
                fields[i].setAccessible(true);
                Object o = fields[i].get(obj);
                if (o != null) {
                    map.put(varName, Collections.singletonList(o.toString()));
                } else {
                    map.put(varName, Collections.singletonList(""));
                }

                fields[i].setAccessible(accessFlag);
            } catch (IllegalArgumentException var8) {
                var8.printStackTrace();
            } catch (IllegalAccessException var9) {
                var9.printStackTrace();
            }
        }

        return map;
    }
}

添加了業務類型及code的實體類

package com.bizvane.utils.jobutils;

import java.util.Date;

public class XxlJobInfo {
    private int id;
    private int jobGroup;
    private String jobCron;
    private String jobDesc;
    private Date addTime;
    private Date updateTime;
    private String author;
    private String alarmEmail;
    private String executorRouteStrategy;
    private String executorHandler;
    private String executorParam;
    private String executorBlockStrategy;
    private String executorFailStrategy;
    private int executorTimeout;
    private String glueType;
    private String glueSource;
    private String glueRemark;
    private Date glueUpdatetime;
    private String childJobId;
    private String jobStatus;
    private String appName;
    private Integer bizType;
    private String bizCode;

    public XxlJobInfo() {
    }

    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getJobGroup() {
        return this.jobGroup;
    }

    public void setJobGroup(int jobGroup) {
        this.jobGroup = jobGroup;
    }

    public String getJobCron() {
        return this.jobCron;
    }

    public void setJobCron(String jobCron) {
        this.jobCron = jobCron;
    }

    public String getJobDesc() {
        return this.jobDesc;
    }

    public void setJobDesc(String jobDesc) {
        this.jobDesc = jobDesc;
    }

    public Date getAddTime() {
        return this.addTime;
    }

    public void setAddTime(Date addTime) {
        this.addTime = addTime;
    }

    public Date getUpdateTime() {
        return this.updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    public String getAuthor() {
        return this.author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getAlarmEmail() {
        return this.alarmEmail;
    }

    public void setAlarmEmail(String alarmEmail) {
        this.alarmEmail = alarmEmail;
    }

    public String getExecutorRouteStrategy() {
        return this.executorRouteStrategy;
    }

    public void setExecutorRouteStrategy(String executorRouteStrategy) {
        this.executorRouteStrategy = executorRouteStrategy;
    }

    public String getExecutorHandler() {
        return this.executorHandler;
    }

    public void setExecutorHandler(String executorHandler) {
        this.executorHandler = executorHandler;
    }

    public String getExecutorParam() {
        return this.executorParam;
    }

    public void setExecutorParam(String executorParam) {
        this.executorParam = executorParam;
    }

    public String getExecutorBlockStrategy() {
        return this.executorBlockStrategy;
    }

    public void setExecutorBlockStrategy(String executorBlockStrategy) {
        this.executorBlockStrategy = executorBlockStrategy;
    }

    public String getExecutorFailStrategy() {
        return this.executorFailStrategy;
    }

    public void setExecutorFailStrategy(String executorFailStrategy) {
        this.executorFailStrategy = executorFailStrategy;
    }

    public int getExecutorTimeout() {
        return this.executorTimeout;
    }

    public void setExecutorTimeout(int executorTimeout) {
        this.executorTimeout = executorTimeout;
    }

    public String getGlueType() {
        return this.glueType;
    }

    public void setGlueType(String glueType) {
        this.glueType = glueType;
    }

    public String getGlueSource() {
        return this.glueSource;
    }

    public void setGlueSource(String glueSource) {
        this.glueSource = glueSource;
    }

    public String getGlueRemark() {
        return this.glueRemark;
    }

    public void setGlueRemark(String glueRemark) {
        this.glueRemark = glueRemark;
    }

    public Date getGlueUpdatetime() {
        return this.glueUpdatetime;
    }

    public void setGlueUpdatetime(Date glueUpdatetime) {
        this.glueUpdatetime = glueUpdatetime;
    }

    public String getChildJobId() {
        return this.childJobId;
    }

    public void setChildJobId(String childJobId) {
        this.childJobId = childJobId;
    }

    public String getJobStatus() {
        return this.jobStatus;
    }

    public void setJobStatus(String jobStatus) {
        this.jobStatus = jobStatus;
    }

    public String getAppName() {
        return this.appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    public Integer getBizType() {
        return this.bizType;
    }

    public void setBizType(Integer bizType) {
        this.bizType = bizType;
    }

    public String getBizCode() {
        return this.bizCode;
    }

    public void setBizCode(String bizCode) {
        this.bizCode = bizCode;
    }
}

再封裝一個工具類使用jobclient,下面給出了一個添加job的操作

package com.bizvane.mktcenterserviceimpl.common.utils;

import com.bizvane.mktcenterservice.models.po.MktTaskPOWithBLOBs;
import com.bizvane.mktcenterservice.models.vo.ActivitySmartVO;
import com.bizvane.mktcenterservice.models.vo.ActivityVO;
import com.bizvane.mktcenterserviceimpl.common.constants.JobHandlerConstants;
import com.bizvane.mktcenterserviceimpl.common.enums.BusinessTypeEnum;
import com.bizvane.mktcenterserviceimpl.common.job.XxlJobConfig;
import com.bizvane.utils.enumutils.JobEnum;
import com.bizvane.utils.jobutils.JobClient;
import com.bizvane.utils.jobutils.XxlJobInfo;
import com.bizvane.utils.tokens.SysAccountPO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @author chen.li
 * @date on 2018/7/4 9:50
 * @description
 */
@Component
public class JobUtil {
    @Autowired
    private XxlJobConfig xxlJobConfig;

    @Autowired
    private JobClient jobClient;

    /**
     * 通用job添加方法
     * @param execuDate
     * @param desc
     * @param param
     * @param author
     * @param jobHandler
     * @param businessType
     */
    public void addJob(Date execuDate,String desc,String param,String author,String jobHandler,int businessType){
        //構建job對象
        XxlJobInfo xxlJobInfo = new XxlJobInfo();
        //設置appName
        xxlJobInfo.setAppName(xxlJobConfig.getAppName());
        //設置路由策略
        xxlJobInfo.setExecutorRouteStrategy(JobEnum.EXECUTOR_ROUTE_STRATEGY_FIRST.getValue());
        //設置job定時器
        xxlJobInfo.setJobCron(DateUtil.getCronExpression(execuDate));
        //設置運行模式
        xxlJobInfo.setGlueType(JobEnum.GLUE_TYPE_BEAN.getValue());
        //設置job處理器
        xxlJobInfo.setExecutorHandler(jobHandler);
        //設置job描述
        xxlJobInfo.setJobDesc(desc);
        //設置執行參數
        xxlJobInfo.setExecutorParam(param);
        //設置阻塞處理策略
        xxlJobInfo.setExecutorBlockStrategy(JobEnum.EXECUTOR_BLOCK_SERIAL_EXECUTION.getValue());
        //設置失敗處理策略
        xxlJobInfo.setExecutorFailStrategy(JobEnum.EXECUTOR_FAIL_STRATEGY_NULL.getValue());
        //設置負責人
        xxlJobInfo.setAuthor(author);
        //設置業務類型
        xxlJobInfo.setBizType(businessType);
        //添加job
        jobClient.addJob(xxlJobInfo);
    }

}

至此將job-admin啓動起來,自己的業務模塊啓動起來,就可通過業務操作對job中心的job進行更改,也可以直接在job平臺直觀操作job控制業務模塊的jobhandler的執行。由於restTemplate的loadBalance註解會產生問題,具體問題我沒有細查,因此在啓動類上需要註釋掉此註解,於是我手動在上面jobClient最下方中做了簡單的請求負載。以下是啓動類代碼

package com.bizvane.mktcenterserviceimpl;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootApplication(scanBasePackages = "com.bizvane")
@EnableDiscoveryClient
@EnableSwagger2
@EnableFeignClients(basePackages={"com.bizvane.centerstageservice.rpc","com.bizvane.members.facade.service.api","com.bizvane.couponfacade.interfaces"})
@MapperScan("com.bizvane.mktcenterserviceimpl.mappers")
public class MktcenterApplication {

    // @Value("${swagger.show}")
    // private boolean swaggerShow;

//  @LoadBalanced
    @Bean
    RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
        return restTemplateBuilder.build();
    }

    public static void main(String[] args) {
        SpringApplication.run(MktcenterApplication.class, args);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章