spring boot 接入xxl-job手册

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);
    }
}
  • 8
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值