分布式任务调度平台中文文档:https://www.xuxueli.com/xxl-job/

1、 概述

XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。

2、快速入门

2.1 初始化“调度数据库”

下载项目源码并解压sql脚本路径:/xxl-job/doc/db/tables_xxl_job.sql

2.2、编译源码

xxl-job-admin:调度中心
xxl-job-core:公共依赖
xxl-job-executor-samples:执行器Sample示例(选择合适的版本执行器,可直接使用,也可以参考其并将现有项目改造成执行器)
    :xxl-job-executor-sample-springboot:Springboot版本,通过Springboot管理执行器,推荐这种方式;
    :xxl-job-executor-sample-frameless:无框架版本;

2.3 配置部署“调度中心 xxl-job-admin”

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
### 报警邮箱
spring.mail.host=smtp.qq.com
spring.mail.port=25
spring.mail.username=xxx@qq.com
spring.mail.password=xxx
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
### 调度中心通讯TOKEN [选填]:非空时启用;
xxl.job.accessToken=
### 调度中心国际化配置 [必填]: 默认为 "zh_CN"/中文简体, 可选范围为 "zh_CN"/中文简体, "zh_TC"/中文繁体 and "en"/英文;
xxl.job.i18n=zh_CN
## 调度线程池最大线程配置【必填】
xxl.job.triggerpool.fast.max=200
xxl.job.triggerpool.slow.max=100
### 调度中心日志表数据保存天数 [必填]:过期日志自动清理;限制大于等于7时生效,否则, 如-1,关闭自动清理功能;
xxl.job.logretentiondays=30

2.4 配置部署执行器 xxl-job-executor-sample-springboot

        <!-- xxl-job-core -->
        <dependency>
            <groupId>com.xuxueli</groupId>
            <artifactId>xxl-job-core</artifactId>
            <version>${project.parent.version}</version>
        </dependency>
### 调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### 执行器通讯TOKEN [选填]:非空时启用;
xxl.job.accessToken=
### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
xxl.job.executor.appname=xxl-job-executor-sample
### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
xxl.job.executor.address=
### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
xxl.job.executor.ip=
### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
xxl.job.executor.port=9999
### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
xxl.job.executor.logretentiondays=30

2.5 执行器组件配置


import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
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.Configuration;

/**
 * xxl-job config
 *
 * @author xuxueli 2017-04-28
 */
@Configuration
public class XxlJobConfig {
    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);

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

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

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

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

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

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

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

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


    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppname(appname);
        xxlJobSpringExecutor.setAddress(address);
        xxlJobSpringExecutor.setIp(ip);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);

        return xxlJobSpringExecutor;
    }

    /**
     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
     *
     *      1、引入依赖:
     *          <dependency>
     *             <groupId>org.springframework.cloud</groupId>
     *             <artifactId>spring-cloud-commons</artifactId>
     *             <version>${version}</version>
     *         </dependency>
     *
     *      2、配置文件,或者容器启动变量
     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
     *
     *      3、获取IP
     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
     */

}

2.6 自定义任务执行器

1、任务开发:在Spring Bean实例中,开发Job方法;
2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。
3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志;
4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果;
@Component
public class MyXxlJob {
    /**
     *
     * @throws Exception
     */
    @XxlJob("myJobHandler")
    public void execute() throws Exception {
        /**
         * 获取调度任务中传递的任务参数 可以为 单个值 也可以为 json
         */
        String jobParam = XxlJobHelper.getJobParam();
        System.out.println("测试");
    }
}

2.7 任务调度中心创建执行器

  AppName: 是每个执行器集群的唯一标示AppName, 执行器会周期性以AppName为对象进行自动注册。可通过该配置自动发现注册成功的执行器, 供任务调度时使用;
名称: 执行器的名称, 因为AppName限制字母数字等组成,可读性不强, 名称为了提高执行器的可读性;
排序: 执行器的排序, 系统中需要执行器的地方,如任务新增, 将会按照该排序读取可用的执行器列表;
注册方式:调度中心获取执行器地址的方式;
    自动注册:执行器自动进行执行器注册,调度中心通过底层注册表可以动态发现执行器机器地址;
    手动录入:人工手动录入执行器的地址信息,多地址逗号分隔,供调度中心使用;
机器地址:"注册方式"为"手动录入"时有效,支持人工维护执行器的地址信息;

image-1655888564450

2.8 任务调度中心创建调度任务

运行模式选中 “BEAN模式”,JobHandler属性填写任务注解“@XxlJob”中定义的值
image-1655888375399

3、通过HTTP请求配置调度任务

创建一个spring boot项目

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.10.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <scope>provided</scope>
        </dependency>

        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.7</version>
        </dependency>
    </dependencies>

配置文件application.yml

xxl:
#  xxl-job任务调度中心的注册地址
  path: http://127.0.0.1:8080/xxl-job-admin
  login: /login
  add: /jobinfo/add
  update: /jobinfo/update
  remove: /jobinfo/remove
  start: /jobinfo/start
  stop: /jobinfo/stop
  username: admin
  pd: 123456

server:
  port: 8088

XxlJobConfig 用于读取配置文件内容

@Component
public class XxlJobConfig {

    public static String JOB_PATH;
    public static String JOB_LOGIN;
    public static String JOB_ADD;
    public static String JOB_UPDATE;
    public static String JOB_REMOVE;
    public static String JOB_START;
    public static String JOB_STOP;

    public static String XXL_USERNAME;

    public static String XXL_PD;

    @Value("${xxl.path}")
    public void setJobPath(String jobPath) {
        JOB_PATH = jobPath;
    }
    @Value("${xxl.login}")
    public void setJobLogin(String jobLogin) {
        JOB_LOGIN = jobLogin;
    }
    @Value("${xxl.add}")
    public void setJobAdd(String jobAdd) {
        JOB_ADD = jobAdd;
    }
    @Value("${xxl.update}")
    public void setJobUpdate(String jobUpdate) {
        JOB_UPDATE = jobUpdate;
    }
    @Value("${xxl.remove}")
    public void setJobRemove(String jobRemove) {
        JOB_REMOVE = jobRemove;
    }
    @Value("${xxl.start}")
    public void setJobStart(String jobStart) {
        JOB_START = jobStart;
    }
    @Value("${xxl.stop}")
    public void setJobStop(String jobStop) {
        JOB_STOP = jobStop;
    }
    @Value("${xxl.username}")
    public void setXxlUsername(String xxlUsername) {
        XXL_USERNAME = xxlUsername;
    }
    @Value("${xxl.pd}")
    public void setXxlPd(String xxlPd) {
        XXL_PD = xxlPd;
    }
}

OkHttpClientUtil 用于向任务调度中心发送请求

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.lx.job.config.XxlJobConfig;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import java.io.IOException;
import java.net.HttpCookie;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


@Slf4j
public class OkHttpClientUtil {

    static OkHttpClient client = new OkHttpClient();

    /**
     * 发送 post 请求
     * @param url
     * @param paramMap
     * @return
     */
    public static String doPost(String url, Map<String, Object> paramMap) throws IOException  {
        MediaType mediaType = MediaType.parse("application/json");
        // 封装数据体
        FormBody.Builder builder = new FormBody.Builder();
        Iterator<Map.Entry<String,Object>> iterator = paramMap.entrySet().iterator();
        Map.Entry<String,Object> item;
        while (iterator.hasNext()){
            item = iterator.next();
            builder.add(item.getKey(),item.getValue() + "");
        }
        String cookie = OkHttpClientUtil.getCookie();
        RequestBody body = builder.build();
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .addHeader("Cookie", cookie)
                .addHeader("Content-Type", "application/json")
                .build();
        Response response = client.newCall(request).execute();
        return response.body().string();
    }

    /**
     * 模拟登录获取cookie
     * @return
     */
    public static String getCookie(){
        String path = XxlJobConfig.JOB_PATH + XxlJobConfig.JOB_LOGIN;
        Map<String, Object> hashMap = new HashMap();
        hashMap.put("userName", XxlJobConfig.XXL_USERNAME);
        hashMap.put("password", XxlJobConfig.XXL_PD);
        HttpResponse response = HttpRequest.post(path).form(hashMap).execute();
        List<HttpCookie> cookies = response.getCookies();
        StringBuilder sb = new StringBuilder();
        for (HttpCookie cookie : cookies) {
            sb.append(cookie.toString());
        }
        return sb.toString();
    }

}

JobUtil 对于调度任务的操作


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lx.job.config.XxlJobConfig;
import com.lx.job.po.XxlJobPO;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;


@Slf4j
public class JobUtil {

    /**
     * 新增任务
     * @param jobGroup 执行器主键ID
     * @param jobDesc 任务名称
     * @param scheduleType 任务类型 CRON
     * @param cron cron表达式
     * @param executorHandler 执行器
     * @param executorParam 执行参数
     * @return
     * @throws IOException
     */
    public static int createJob(int jobGroup, String jobDesc,String scheduleType, String cron,
                                String executorHandler, String executorParam) throws IOException {
        log.info("------------------------------start---------------------");
        Integer jobId = null;
        Map<String, Object> paramMap = new HashMap<>();
        /*  配置文件读取 */
        paramMap.put("jobGroup", jobGroup);
        /*  参数传递 */
        paramMap.put("jobDesc", jobDesc);
        paramMap.put("author", "admin");
        paramMap.put("scheduleType", scheduleType);
        paramMap.put("scheduleConf", cron);
        paramMap.put("cronGen_display", cron);
        paramMap.put("glueType", "BEAN");
        /* 执行器 */
        paramMap.put("executorHandler", executorHandler);
        // 此处hander需提前在项目中定义
        /*  参数 */
        paramMap.put("executorParam", executorParam);
        paramMap.put("executorRouteStrategy", "RANDOM");
        paramMap.put("misfireStrategy", "DO_NOTHING");
        paramMap.put("executorBlockStrategy", "SERIAL_EXECUTION");
        paramMap.put("executorTimeout", "0");
        paramMap.put("executorFailRetryCount", "0");
        paramMap.put("glueRemark", "GLUE代码初始化");
        String url = XxlJobConfig.JOB_PATH + XxlJobConfig.JOB_ADD;
        String s = OkHttpClientUtil.doPost(url, paramMap);
        JSONObject jsonObject = JSON.parseObject(s);
        int code = jsonObject.getIntValue("code");
        log.info("===================code: {}", code);
        if (code == 200) {
            jobId = jsonObject.getIntValue("content");
        }
        log.info("--------------------------------end---------------------------");
        return jobId;
    }

    /**
     * 修改任务
     * @return
     * @throws IOException
     */
    public static int updateJob(XxlJobPO xxlJobPO) throws IOException{
        log.info("------------------------------start---------------------");
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("id", xxlJobPO.getId());
        /*  配置文件读取 */
        paramMap.put("jobGroup", xxlJobPO.getJobGroup());
        /*  参数传递 */
        paramMap.put("jobDesc", xxlJobPO.getJobDesc());
        paramMap.put("author", "admin");
        paramMap.put("scheduleType", xxlJobPO.getScheduleType());
        paramMap.put("scheduleConf", xxlJobPO.getScheduleConf());
        paramMap.put("cronGen_display", xxlJobPO.getScheduleConf());
        paramMap.put("glueType", "BEAN");
        /* 执行器 */
        paramMap.put("executorHandler", xxlJobPO.getExecutorHandler());
        // 此处hander需提前在项目中定义
        /*  参数 */
        paramMap.put("executorParam", xxlJobPO.getExecutorParam());
        paramMap.put("executorRouteStrategy", "RANDOM");
        paramMap.put("misfireStrategy", "DO_NOTHING");
        paramMap.put("executorBlockStrategy", "SERIAL_EXECUTION");
        paramMap.put("executorTimeout", "0");
        paramMap.put("executorFailRetryCount", "0");
        paramMap.put("glueRemark", "GLUE代码初始化");
        String url = XxlJobConfig.JOB_PATH + XxlJobConfig.JOB_UPDATE;
        String s = OkHttpClientUtil.doPost(url, paramMap);
        JSONObject jsonObject = JSON.parseObject(s);
        int code = jsonObject.getIntValue("code");
        int jobId = 0;
        log.info("===================code: {}", code);
        if (code == 200) {
            jobId = jsonObject.getIntValue("content");
        } else {
            log.error("修改任务失败, 任务编号:{}", jobId);
        }
        return jobId;
    }

    /**
     * 删除任务
     * @param jobId 任务ID
     * @throws IOException
     */
    public static void removeJob(int jobId) throws IOException{
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("id", jobId);
        String removeUrl = XxlJobConfig.JOB_PATH + XxlJobConfig.JOB_REMOVE;
        String responseResult = OkHttpClientUtil.doPost(removeUrl, paramMap);
        JSONObject jsonObject = JSON.parseObject(responseResult);
        int code = jsonObject.getIntValue("code");
        if (code == 200) {
            log.info("删除任务成功,任务编号:{}", jobId);
        } else {
            log.error("删除任务失败,任务编号:{}", jobId);
        }
    }

    /**
     * 启动任务
     * @param jobId
     * @throws IOException
     */
    public static void startJob(int jobId) throws IOException{
        Map<String, Object> parmaMap = new HashMap<>();
        parmaMap.put("id", jobId);
        String startUrl = XxlJobConfig.JOB_PATH + XxlJobConfig.JOB_START;
        String responseResult = OkHttpClientUtil.doPost(startUrl, parmaMap);
        JSONObject jsonObject = JSON.parseObject(responseResult);
        int code = jsonObject.getIntValue("code");
        if (code == 200) {
            log.info("启动任务成功,任务编号:{}", jobId);
        } else {
            log.error("启动任务失败,任务编号:{}", jobId);
        }
    }

    /**
     * 停止任务
     * @param jobId
     * @throws IOException
     */
    public static void stopJob(int jobId) throws IOException{
        Map<String, Object> parmaMap = new HashMap<>();
        parmaMap.put("id", jobId);
        String stopUrl = XxlJobConfig.JOB_PATH + XxlJobConfig.JOB_STOP;
        String responseResult = OkHttpClientUtil.doPost(stopUrl, parmaMap);
        JSONObject jsonObject = JSON.parseObject(responseResult);
        int code = jsonObject.getIntValue("code");
        if (code == 200) {
            log.info("停止任务成功,任务编号:{}", jobId);
        } else {
            log.error("停止任务失败,任务编号:{}", jobId);
        }
    }
}

JobController 接收请求处理参数然后将请求转发至任务调度中心

@Slf4j
@RestController
@RequestMapping("/job")
public class JobController {


    @Autowired
    private JobService jobService;

    /**
     * 新增调度任务
     *
     * @param xxlJobInfoDTO
     * @return
     */
    @PostMapping(value = "/add")
    public R<String> add(@RequestBody XxlJobInfoDTO xxlJobInfoDTO) {
        log.info("新增调度任务传递参数: {}", xxlJobInfoDTO);
        int jobId = jobService.addJob(xxlJobInfoDTO);
        log.info("新增调度任务返回参数: {}", jobId);
        return R.data(String.valueOf(jobId));
    }

    /**
     * 修改调度任务
     *
     * @param xxlJobInfoDTO
     * @return
     */
    @PostMapping(value = "/update")
    public R<String> update(@RequestBody XxlJobInfoDTO xxlJobInfoDTO) {
        log.info("修改调度任务传递参数: {}", xxlJobInfoDTO);
        int jobId = jobService.updateJob(xxlJobInfoDTO);
        log.info("修改调度任务返回参数: {}", jobId);
        return R.data(String.valueOf(jobId));
    }

    /**
     * 删除调度任务
     *
     * @param id
     * @return
     */
    @PostMapping(value = "/remove")
    public R<String> remove(@RequestBody String id) {
        log.info("删除调度任务请求参数: {}", id);
        jobService.removeJob(id);
        log.info("删除调度任务返回结果: {}", id);
        return R.data(id);
    }

    /**
     * 启动调度任务
     *
     * @param id
     * @return
     */
    @PostMapping(value = "/start")
    public R<String> start(@RequestBody String id) {
        log.info("启动调度任务请求参数: {}", id);
        jobService.start(id);
        log.info("启动调度任务返回结果: {}", id);
        return R.data(id);
    }

    /**
     * 停止调度任务
     *
     * @param id
     * @return
     */
    @PostMapping(value = "/stop")
    public R<String> stop(@RequestBody String id) {
        log.info("停止调度任务请求参数: {}", id);
        jobService.stopJob(id);
        log.info("停止调度任务返回结果: {}", id);
        return R.data(id);
    }
}


public interface JobService {

    /**
     * 新增任务
     * @param xxlJobInfoDTO
     * @return
     */
    int addJob(XxlJobInfoDTO xxlJobInfoDTO);

    /**
     * 修改任务
     * @param xxlJobInfoDTO
     * @return
     */
    int updateJob(XxlJobInfoDTO xxlJobInfoDTO);


    /**
     * 删除任务
     * @param executorParam
     * @return
     */
    int removeJob(String executorParam);


    /**
     * 启动任务
     * @param executorParam
     * @return
     */
    int start(String executorParam);


    /**
     * 停止任务
     * @param executorParam
     * @return
     */
    int stopJob(String executorParam);

}
@Slf4j
@Service
public class JobServiceImpl implements JobService {

    @Override
    public int addJob(XxlJobInfoDTO xxlJobInfoDTO) {

        String scheduleType = xxlJobInfoDTO.getScheduleType() != null ? xxlJobInfoDTO.getScheduleType() : "CRON";
        int jobId = 0;
        try {
            jobId = JobUtil.createJob(xxlJobInfoDTO.getJobGroup(), xxlJobInfoDTO.getJobDesc(), scheduleType, xxlJobInfoDTO.getScheduleConf(),
                    xxlJobInfoDTO.getExecutorHandler(), xxlJobInfoDTO.getExecutorParam());
            JobUtil.startJob(jobId);
        } catch (IOException e) {
            log.error("新增定时任务失败: {}", e.getMessage());
        }
        return jobId;
    }

    @Override
    public int updateJob(XxlJobInfoDTO xxlJobInfoDTO) {
        Map<String, Object> params = new HashMap<>();
        params.put("executorParam", xxlJobInfoDTO.getExecutorParam());
        //调用远程数据库插入接口
        XxlJobPO xxlJobPO =new XxlJobPO();
        String scheduleType = xxlJobInfoDTO.getScheduleType() != null ? xxlJobInfoDTO.getScheduleType() : "CRON";
        xxlJobPO.setScheduleType(scheduleType);
        xxlJobPO.setScheduleConf(xxlJobInfoDTO.getScheduleConf());
        int jobId = 0;
        try {
            jobId = JobUtil.updateJob(xxlJobPO);
        } catch (IOException e) {
            log.error("修改定时任务失败", e.getMessage());
        }
        return jobId;
    }

    @Override
    public int removeJob(String executorParam) {
        try {
            JobUtil.removeJob(Integer.parseInt(executorParam));
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return 0;
    }

    @Override
    public int start(String executorParam) {
        try {
            JobUtil.startJob(Integer.parseInt(executorParam));
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return Integer.valueOf(executorParam);
    }


    @Override
    public int stopJob(String executorParam) {
        try {
            JobUtil.stopJob(Integer.parseInt(executorParam));
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return 0;
    }
}

@Data
public class XxlJobPO implements Serializable {

    private static final long serialVersionUID = -6295333500034188754L;
    /**
     * 主键ID
     */
    private int id;

    /**
     * 执行器主键ID
     */
    private int jobGroup;

    private String jobDesc;

    private Date addTime;
    private Date updateTime;
    /**
     * 负责人
     */
    private String author;
    /**
     * 报警邮件
     */
    private String alarmEmail;
    /**
     * 调度类型
     */
    private String scheduleType;
    /**
     * 调度配置,值含义取决于调度类型
     */
    private String scheduleConf;
    /**
     * 调度过期策略
     */
    private String misfireStrategy;
    /**
     * 执行器路由策略
     */
    private String executorRouteStrategy;
    /**
     * 执行器,任务Handler名称
     */
    private String executorHandler;
    /**
     * 执行器,任务参数
     */
    private String executorParam;
    /**
     * 阻塞处理策略
     */
    private String executorBlockStrategy;
    /**
     * 任务执行超时时间,单位秒
     */
    private int executorTimeout;
    /**
     * 失败重试次数
     */
    private int executorFailRetryCount;

    /**
     * LUE类型 com.xxl.job.core.glue.GlueTypeEnum
     */
    private String glueType;
    /**
     * GLUE源代码
     */
    private String glueSource;
    /**
     * GLUE备注
     */
    private String glueRemark;
    /**
     * GLUE更新时间
     */
    private Date glueUpdatetime;

    /**
     * 子任务ID,多个逗号分隔
     */
    private String childJobId;
    /**
     * 调度状态:0-停止,1-运行
     */
    private int triggerStatus;
    /**
     * 上次调度时间
     */
    private long triggerLastTime;
    /**
     * 下次调度时间
     */
    private long triggerNextTime;


}


@Data
public class XxlJobPO implements Serializable {

    private static final long serialVersionUID = -6295333500034188754L;
    /**
     * 主键ID
     */
    private int id;

    /**
     * 执行器主键ID
     */
    private int jobGroup;

    private String jobDesc;

    private Date addTime;
    private Date updateTime;
    /**
     * 负责人
     */
    private String author;
    /**
     * 报警邮件
     */
    private String alarmEmail;
    /**
     * 调度类型
     */
    private String scheduleType;
    /**
     * 调度配置,值含义取决于调度类型
     */
    private String scheduleConf;
    /**
     * 调度过期策略
     */
    private String misfireStrategy;
    /**
     * 执行器路由策略
     */
    private String executorRouteStrategy;
    /**
     * 执行器,任务Handler名称
     */
    private String executorHandler;
    /**
     * 执行器,任务参数
     */
    private String executorParam;
    /**
     * 阻塞处理策略
     */
    private String executorBlockStrategy;
    /**
     * 任务执行超时时间,单位秒
     */
    private int executorTimeout;
    /**
     * 失败重试次数
     */
    private int executorFailRetryCount;

    /**
     * LUE类型 com.xxl.job.core.glue.GlueTypeEnum
     */
    private String glueType;
    /**
     * GLUE源代码
     */
    private String glueSource;
    /**
     * GLUE备注
     */
    private String glueRemark;
    /**
     * GLUE更新时间
     */
    private Date glueUpdatetime;

    /**
     * 子任务ID,多个逗号分隔
     */
    private String childJobId;
    /**
     * 调度状态:0-停止,1-运行
     */
    private int triggerStatus;
    /**
     * 上次调度时间
     */
    private long triggerLastTime;
    /**
     * 下次调度时间
     */
    private long triggerNextTime;


}

封装返回类型

public interface IResultCode extends Serializable {
    String getMessage();

    int getCode();
}
public enum ResultCode implements IResultCode {
    SUCCESS(200, "操作成功"),
    FAILURE(400, "业务异常"),
    UN_AUTHORIZED(401, "请求未授权"),
    NOT_FOUND(404, "404 没找到请求"),
    MSG_NOT_READABLE(400, "消息不能读取"),
    METHOD_NOT_SUPPORTED(405, "不支持当前请求方法"),
    MEDIA_TYPE_NOT_SUPPORTED(415, "不支持当前媒体类型"),
    REQ_REJECT(403, "请求被拒绝"),
    INTERNAL_SERVER_ERROR(500, "服务器异常"),
    PARAM_MISS(400, "缺少必要的请求参数"),
    PARAM_TYPE_ERROR(400, "请求参数类型错误"),
    PARAM_BIND_ERROR(400, "请求参数绑定错误"),
    PARAM_VALID_ERROR(400, "参数校验失败"),
    PWD_NOT_RESET(600, "参数校验失败"),
    BUSINESS_ERROR(-1, "业务异常"),
    TYPE_CONVERT_ERROR(700, "类型转换异常"),
    TYPE_ERROR(800, "非法关键字异常"),
    REMOTE_SQL_ERROR(900, "远程sql执行异常");

    final int code;
    final String message;

    @Override
    public int getCode() {
        return this.code;
    }

    @Override
    public String getMessage() {
        return this.message;
    }

    private ResultCode(final int code, final String message) {
        this.code = code;
        this.message = message;
    }
}
public class R<T> implements Serializable {
    private static final Logger log = LoggerFactory.getLogger(R.class);
    private static final long serialVersionUID = 1L;
    private int code;
    private boolean success;
    private String message;
    @JsonIgnore
    private String messageCode;
    private String timestamp;
    private T data;
    @JsonIgnore
    private static Props props = new Props();


    public static <T> R<T> data(int code, T data, String msg) {
        return new R(code, data, data == null ? "暂无承载数据" : msg);
    }

    public static <T> R<T> data(T data, String msg) {
        return data(200, data, msg);
    }

    public static <T> R<T> data(T data) {
        return data(data, "操作成功");
    }
    private R(IResultCode resultCode, T data) {
        this(resultCode, data, resultCode.getMessage());
    }

    private R(IResultCode resultCode, T data, String message) {
        this(resultCode.getCode(), data, message);
    }

    private R(int code, T data, String message) {
        this.code = code;
        this.data = data;
        this.message = message;
        this.timestamp = String.valueOf(System.currentTimeMillis());
        this.success = ResultCode.SUCCESS.code == code;
    }

    private R(int code, T data, String messageCode, String message) {
        this.code = code;
        this.data = data;
        this.timestamp = String.valueOf(System.currentTimeMillis());
        this.success = ResultCode.SUCCESS.code == code;
        if (Strings.isNotBlank(message)) {
            this.message = message;
        } else if (Strings.isNotBlank(messageCode)) {
            String propVal = props.getProperty(messageCode);
            this.message = Strings.isBlank(propVal) ? messageCode : propVal;
        }

    }
}

Q.E.D.