SpringBoot入门建站全系列(三十二)接入xxl-job分布式任务调度平台

一、概述

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

但是我们到底用它来干什么呢?

我们都知道,Quartz是Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制,在《SpringBoot入门建站全系列(十五)内置定时任务及Quartz定时任务使用》 一篇中,已经讲述了Quartz的使用方法。

而XXL-JOB正是基于Quartz,并将Quartz的功能进行封装,作为执行器,使其可以和一个统一的调度中心进行交互,将任务的调度管理交给调度中心处理,任务的执行交给Quartz执行。(XXL-JOB 2.1.0版本之后不再使用Quartz,但是本篇特意找到了使用Quartz的较新版本,2.0.1)。这样,执行任务的每个机器相当于一个个客户端,而调度中心则是各个客户端的总管(调度中心也可以集群化部署,但要公用一个数据库)。


一、数据库的初始化


把tables_xxl_job.sql的sql执行下,注意里面的数据库是xxl-job,可以改成自己的数据库。

执行完成后,就出现这些表了:

springboot3 分布式调度 spring分布式任务_xxl-job

二、调度中心的安装


注意,如果直接下载master,就是下载最新版本的xxl-job,可能不是你想要的。

2.0.1版本的xxl-job-admin改为SpringBoot应用了。

解压后,将xxl-job下的xxl-job-admin项目导入到工作台,如图所示:

springboot3 分布式调度 spring分布式任务_xxl-job_02

我们要修改的就是application.properties文件,可以将端口修改下,数据库地址密码等肯定要修改地;邮箱地址也是要修改地;登录用户名等随意。

改完之后,打包,运行jar包。

运行成功后打开: http://127.0.0.1:8080/xxl-job-admin/ 即可。

三、执行器

执行器就是一个运行任务的客户端。需要注册到调度中心去。

本文假设你已经引入spring-boot-starter-web。已经是个SpringBoot项目了,如果不会搭建,可以打开这篇文章看一看《SpringBoot入门建站全系列(一)项目建立》。

首先,在调度中心新建执行器,这个步骤啥时候做其实都可以,如果想让执行器自动注册,需要在执行器启动前在调度中心把执行器新建好。

3.1 调度中心新建执行器

选择自动注册,需要在执行器启动前在调度中心把执行器新建好。手动注册需要手动填入机器和端口列表。

springboot3 分布式调度 spring分布式任务_xxl-job_03

建好后,执行器启动之后,会自动注册进来:

springboot3 分布式调度 spring分布式任务_分布式任务调度_04

3.2 Maven依赖

需要引入xxl-job-core.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>com.xuxueli</groupId>
	<artifactId>xxl-job-core</artifactId>
	<version>2.0.1</version>
</dependency>
3.3 配置文件

在application.properties 中需要配置数据库相关信息的信息,如:

### 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin

### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
xxl.job.executor.appname=pomitcn

### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
xxl.job.executor.ip=

### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
xxl.job.executor.port=9999

### 执行器通讯TOKEN [选填]:非空时启用;
xxl.job.accessToken=

### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
xxl.job.executor.logpath=

### 执行器日志保存天数 [选填] :值大于3时生效,启用执行器Log文件定期清理功能,否则不生效;
xxl.job.executor.logretentiondays=-1
3.4 执行器配置

需要配置handler的位置、xxl.job的相关参数等。

package com.cff.springbootwork.xxljob.config;

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.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * xxl-job config
 *
 */
@Configuration
@ComponentScan(basePackages = "com.cff.springbootwork.xxljob.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 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;
    }

}
3.5 新建Job执行Handler

xxl-job提供了多种任务执行方式:GLUE模式(包含JAVA(groovy)、shell、php、nodejs、python等多种方式)和Bean模式;

一般情况下我们是使用Bean模式来定义任务,因为这个是正常的JAVA开发人员的思维。而GLUE模式,是将代码写在表里,动态执行。

这里,我先讲下怎么使用Bean模式。

开发步骤(官方说明):

  1. 继承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”;
  2. 注册到Spring容器:添加“@Component”注解,被Spring容器扫描为Bean实例;
  3. 注册到执行器工厂:添加“@JobHandler(value=“自定义jobhandler名称”)”注解,注解value值对应的是调度中心新建任务的JobHandler属性的值。
  4. 执行日志:需要通过 “XxlJobLogger.log” 打印执行日志;
package com.cff.springbootwork.xxljob.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 com.xxl.job.core.log.XxlJobLogger;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;


/**
 * 任务Handler示例(Bean模式)
 *
 * 开发步骤:
 * 1、继承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”;
 * 2、注册到Spring容器:添加“@Component”注解,被Spring容器扫描为Bean实例;
 * 3、注册到执行器工厂:添加“@JobHandler(value="自定义jobhandler名称")”注解,注解value值对应的是调度中心新建任务的JobHandler属性的值。
 * 4、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
 *
 * @author xuxueli 2015-12-19 19:43:36
 */
@JobHandler(value="demoJobHandler")
@Component
public class DemoJobHandler extends IJobHandler {

	@Override
	public ReturnT<String> execute(String param) throws Exception {
		XxlJobLogger.log("XXL-JOB, Hello World.");
		System.out.println("test");
		for (int i = 0; i < 5; i++) {
			XxlJobLogger.log("beat at:" + i);
			TimeUnit.SECONDS.sleep(2);
		}
		return SUCCESS;
	}

}

四、测试任务执行

在调度中心的任务管理–> 新建任务:

springboot3 分布式调度 spring分布式任务_分布式任务调度_05

建好以后,选择执行器,可以看到当前任务状态是stop:

springboot3 分布式调度 spring分布式任务_springboot3 分布式调度_06

可以点执行,也可以点启动

执行: 只执行一次
启动: 启动定时任务,安装cron表达式来执行。

我这里点执行之后,任务执行一次,点击日志查看日志记录:

springboot3 分布式调度 spring分布式任务_springboot_07