SpringBoot2.3.4整合xxl-job实现分布式定时任务

概述

xxl-job是一个分布式任务调度平台,其核心设计目标时开发迅速、学习简单、轻量级、易扩展。现已开放源码并接入多家公司线上产品线,开箱即用
更多详情参阅xxl-job官网
xxl-job是一个十分优秀的开源任务调度平台,已经有很多知名的公司都接入了自己的系统。本文基于SpringBoot2.3.4整合xxl-job实现分布式定时任务。

下载xxl-job源码

码云地址
github地址
本文下载最新稳定版本2.2.0作为整合版本
SpringBoot2.3.4整合xxl-job实现分布式定时任务_第1张图片

搭建环境

在doc/db目录下找到sql文件tables_xxl_job.sql,并在数据库执行
可以将xxl-job-admin项目打包成jar执行或者导入IDEA中本地执行,本文方便整合和测试,导入IDEA中,加载相应依赖,修改配置文件数据库连接信息
xxl-job数据库连接
执行XxlJobApplication启动类,在浏览器访问http://localhost:8094/xxl-job-admin
输入用户名/密码:admin/123456
SpringBoot2.3.4整合xxl-job实现分布式定时任务_第2张图片
SpringBoot2.3.4整合xxl-job实现分布式定时任务_第3张图片

SpringBoot整合xxl-job

在pom.xml中引入依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-testartifactId>
    <scope>testscope>
dependency>
<dependency>
    <groupId>com.xuxueligroupId>
    <artifactId>xxl-job-coreartifactId>
    <version>2.2.0version>
dependency>

application.yml配置文件中添加配置信息

server:
  port: 8093
logging:
  config: classpath:logback-spring.xml
xxl:
  job:
    admin:
      addresses: http://127.0.0.1:8094/xxl-job-admin #调度中心地址
    accessToken: #执行器通讯token
    executor:
      appname: springboot-xxl-job #执行器名称,执行器心跳注册分组依据,为空时关闭自动注册
      address: #执行器注册地址
      ip: #执行器IP
      port: 9999 #执行器端口号
      logpath: /data/applogs/xxl-job/jobhandler #执行器运行日志文件存储路径
      logretentiondays: 30 #日志文件保存天数

新建xxl-job配置类

@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 xxlJobSpringExecutor() {
        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;
    }
}

编写执行器

@Component
public class XxlJobHandler {

    private Logger logger = LoggerFactory.getLogger(XxlJobHandler.class);

    /**
     * 简单任务(Bean模式)
     * @param param
     * @return
     * @throws Exception
     */
    @XxlJob("jobHandler")
    public ReturnT<String> jobHandler(String param) throws Exception {
        logger.info("XXL-JOB, Hello World");
        for (int i = 0; i < 5; i++) {
            logger.info("beat at:" + i);
            TimeUnit.SECONDS.sleep(2);
        }
        return ReturnT.SUCCESS;
    }

    /**
     * 分片广播任务
     * @param param
     * @return
     * @throws Exception
     */
    @XxlJob("shardingJobHandler")
    public ReturnT<String> shardingJobHandler(String param) throws Exception {
        ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
        logger.info("分片参数:当前分片序号 = {},总分片数 = {}", shardingVO.getIndex(), shardingVO.getTotal());
        for (int i = 0; i < shardingVO.getTotal(); i++) {
            if (i == shardingVO.getIndex()) {
                logger.info("第{}片,命中分片开始处理!", i);
            } else {
                logger.info("第{}片,忽略!", i);
            }
        }
        return ReturnT.SUCCESS;
    }

    /**
     * 命令行执行
     * @param param
     * @return
     * @throws Exception
     */
    @XxlJob("commandJobHandler")
    public ReturnT<String> commandJobHandler(String param) throws Exception {
        String command = param;
        int exitValue = -1;
        BufferedReader bufferedReader = null;
        try {
            Process process = Runtime.getRuntime().exec(command);
            BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream());
            bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                logger.info(line);
            }
            process.waitFor();
            exitValue = process.exitValue();
        } catch (Exception e) {
            logger.info("出现异常!", e);
        } finally {
            if (bufferedReader != null) {
                bufferedReader.close();
            }
        }
        if (exitValue == 0) {
            return IJobHandler.SUCCESS;
        } else {
            return new ReturnT<String>(IJobHandler.FAIL.getCode(), "command exit value(" + exitValue + ") is failed");
        }
    }

    /**
     * 跨平台Http任务
     * @param param
     * @return
     * @throws Exception
     */
    @XxlJob("httpJobHandler")
    public ReturnT<String> httpJobHandler(String param) throws Exception {
        if (param == null || param.trim().length() == 0) {
            logger.info("param[" + param + "] invalid.");
            return ReturnT.FAIL;
        }
        String[] httpParams = param.split("\n");
        String url = null;
        String method = null;
        String data = null;
        for (String httpParam : httpParams) {
            if (httpParam.startsWith("url:")) {
                url = httpParam.substring(httpParam.indexOf("url:") + 4).trim();
            }
            if (httpParam.startsWith("method:")) {
                method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase();
            }
            if (httpParam.startsWith("data:")) {
                data = httpParam.substring(httpParam.indexOf("data:") + 5).trim();
            }
        }
        if (url == null || url.trim().length() == 0) {
            logger.info("url[" + url + "] invalid.");
            return ReturnT.FAIL;
        }
        if (method == null || !Arrays.asList("GET", "POST").contains(method)) {
            logger.info("method[" + method + "] invalid.");
            return ReturnT.FAIL;
        }
        HttpURLConnection connection = null;
        BufferedReader bufferedReader = null;
        try {
            URL realUrl = new URL(url);
            connection = (HttpURLConnection) realUrl.openConnection();
            connection.setRequestMethod(method);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setReadTimeout(5 * 1000);
            connection.setConnectTimeout(3 * 1000);
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8");
            connection.connect();
            if (data != null && data.trim().length() > 0) {
                DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
                dataOutputStream.write(data.getBytes("UTF-8"));
                dataOutputStream.flush();
                dataOutputStream.close();
            }
            int statusCode = connection.getResponseCode();
            if (statusCode != 200) {
                throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid.");
            }
            bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
            StringBuilder result = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                result.append(line);
            }
            String responseMsg = result.toString();
            logger.info(responseMsg);
            return ReturnT.SUCCESS;
        } catch (Exception e) {
            logger.info("执行异常!", e);
            return ReturnT.FAIL;
        } finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
                if (connection != null) {
                    connection.disconnect();
                }
            } catch (Exception E) {
                logger.info("执行异常!", E);
            }
        }
    }

    /**
     * 生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑
     * @param param
     * @return
     * @throws Exception
     */
    @XxlJob(value = "jobHandler2", init = "init", destroy = "destroy")
    public ReturnT<String> jobHandler2(String param) throws Exception {
        logger.info("XXL-JOB, Hello World.");
        return ReturnT.SUCCESS;
    }

    public void init() {
        logger.info("init");
    }

    public void destroy() {
        logger.info("destory");
    }
}

调度中心新增执行器

SpringBoot2.3.4整合xxl-job实现分布式定时任务_第4张图片

新增定时任务

SpringBoot2.3.4整合xxl-job实现分布式定时任务_第5张图片

执行定时任务

SpringBoot2.3.4整合xxl-job实现分布式定时任务_第6张图片

查看后台执行日志

SpringBoot2.3.4整合xxl-job实现分布式定时任务_第7张图片
xxl-job使用起来十分简单,官网提供了详细的文档,详细了解更多xxl-job的其他知识参阅xxl-job中文网

你可能感兴趣的:(SpringBoot,spring,boot,分布式,jobs,定时任务)