xxl-job初学转载,不断更新

发布时间 2023-05-23 00:46:04作者: spiderMan1-1

参考:https://blog.csdn.net/xhmico/article/details/122324950

官网与源码下载地址

官网:https://www.xuxueli.com/xxl-job/
源码下载地址:https://github.com/xuxueli/xxl-job/releases

准备工作

1.下载源码

根据上面提供的 源码下载地址 链接进去下载源码就完事
windows 下使用下载这个就行了
image
下载源码之后解压
image

2.IDEA导入项目

image
导入之后是这样的
image
可以看到作者提供的源码里面主要分了三个模块
xxl-job-admin
xxl-job-core
xxl-job-executor-samples
我们先看看 xxl-job-admin 这个模块
image
在启动这个模块之前先要做数据的准备工作

3.数据准备

先将所需要的表准备好,这里直接用作者提供的 sql 就行
image
复制 sql ,到你自己的数据库中执行
image
修改配置文件
这里改成你自己数据库的配置(你可以看看其它的配置信息,里面也有修改发送邮件等配置)
image

4.登陆任务调度中心

运行 xxl-job-admin 的启动类
image
启动完成后就可以登陆上去看看

地址:http://localhost:8080/xxl-job-admin

进去是这样子的,但是需要账号跟密码
image
密码是通过md5加密而来的,真实的账号:admin 密码:123456
登陆之后就能看任务调度中心的控制页面了
image

5.测试定时任务

这里作者提供了 xxl-job 整合其它框架或者软件,这里我们先学习整合 spring-boot 的来,毕竟这个对于 java 开发是最重要的
image
可以看到整合了 spring-boot 的这个项目中的 pom 文件里是有引入这个 xxl-job-core 的依赖的

<!-- xxl-job-core -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>${project.parent.version}</version>
</dependency>

所以在我们自己的项目中如果到时候要用这个定时器,也需要加入这个依赖
作者这里也提供了 docker 的配置,需要在自己服务器上运行的可以使用
image
再来看看这个项目的配置文件
image

# web port
server.port=8081
# no web
#spring.main.web-environment=false

# log config
logging.config=classpath:logback.xml

### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
# 这里主要是配置你任务调度中心的地址(就是控制平台的),也就是xxl-job-admin的地址,如果你是本地起的就不需要改了,否则改成你服务器上的地址
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin

### xxl-job, access token
xxl.job.accessToken=

### xxl-job executor appname
xxl.job.executor.appname=xxl-job-executor-sample
### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null
xxl.job.executor.address=
### xxl-job executor server-info
xxl.job.executor.ip=
# 这个就是你定时器执行的端口,我这边改成使用服务自带的端口,然后再配置类中做个偏移处理,这样端口比较好维护点
xxl.job.executor.port=${server.port}
#xxl.job.executor.port=9999
### xxl-job executor log-path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job executor log-retention-days
xxl.job.executor.logretentiondays=30

说明:
addresses: 任务调度中心部署根地址(必填)
appname:这个名称可以自己定义,但一般为项目名称-job,此名称用于在任务调度中心中,配置执行器时的名称标识
ip:执行器ip,可以不填,会自动识别注册
porrt:执行器端口号
logpath:执行器运行日志文件存储磁盘路径
logrententiondays:执行器日志保存天数,值大于3时生效,启用执行器log文件定期清理功能,否则不生效
AccessToken:执行器通讯token,非空时启用

在看看配置类
image

@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;
    }

}

修改执行器端口偏移(这个看你自己的喜好,你可以自己去指定端口,我是直接拿服务的端口加上1000而的来执行器端口的)
image
直接运行下这个 xxl-job-executor-sample-spring 服务的启动类
image
启动之后去 任务调度中心 的 执行器管理 就能看到自动注入的执行器了,端口是9081,非常的 nice
image
再来回过头看看 任务调度中心 的 任务管理 ,之前我想大家都有看到这里有个默认的定时任务吧
image
这个定时任务其实就是 xxl-job-executor-sample-springboot 中的任务,可以在 SampleXxlJob.java 类中找到
image
那就来执行下这个定时器任务呗
再看看这个测试任务1 ,它的 cron 表达式是这样的0 0 0 * * ? *
可以看到这个是每天凌晨 0 点执行,这可不成,那就改一改吧
在右侧 操作 - 编辑 可修改定时规则
image
再去修改下这个项目的 demoJobHandler 定时任务,给它打印点东西,便于观察
image
重启该项目,清空控制台
启动下该定时任务
在右侧 操作 - 启动 启动该定时任务
image
到点了,就去控制台看看执行情况
image
可以看到控制台有六次打印(因为设置的就是 11:35 开始每 10 秒 执行一次)
也可以去查询这个定时任务的日志
image

2.整合到自己的项目中

现在对于怎么使用这个 xxl-job 应该都差不多了吧,现在我就把它整合到自己的项目中去,新建springboot项目
(以下内容需要根据项目自身的情况去配置,仅供参考)
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mike-parent</artifactId>
        <groupId>com.mike</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mike-xxl-job</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- 我的公共模块 -->
        <dependency>
            <groupId>com.mike</groupId>
            <artifactId>mike-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- nacos 服务发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- spring-boot-starter-web (spring-webmvc + tomcat) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- lombok 依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <outputDirectory>${project.parent.basedir}/jars</outputDirectory>
                    <finalName>${project.artifactId}</finalName>
                    <!-- 指定该Main Class为全局的唯一入口 -->
                    <mainClass>com.mike.mo.job.XxlJobApplication</mainClass>
                    <layout>ZIP</layout>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal><!--可以把依赖的包都打包到生成的Jar包中-->
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
  port: 6804
logging:
  config: classpath:logback.xml
  file: /www/mike/project-mike/logs/mike-xxl-job.log
  level:
    com.alibaba.nacos: error
spring:
  application:
    name: mike-xxl-job
  main:
    allow-bean-definition-overriding: true
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        namespace: f90ad91c-ac39-4a2d-8fb3-fcd33ded9123
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/duojiala?characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    hikari:
      username: 'root'
      password: 'root'
      driver-class-name: 'com.mysql.cj.jdbc.Driver'
      max-lifetime: 120000

xxl-job:
  admin:
    addresses: http://127.0.0.1:8080/xxl-job-admin
  executor:
    appName: mike-xxl-job
    ip:
    port: ${server.port}
    logPath: /data/applogs/xxl-job/jobhandler
    logRetentionDays: 30
  accessToken:

logback.xml (这个是直接复制作者的,我也没研究过),也粘在下面

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">

    <contextName>logback</contextName>
    <property name="log.path" value="/data/applogs/xxl-job/xxl-job-executor-sample-springboot.log"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="console"/>
        <appender-ref ref="file"/>
    </root>

</configuration>

MikeXxlJob.java

@Component
public class MikeXxlJob {
    private final static Logger logger = LoggerFactory.getLogger(MikeXxlJob.class);

    /**
     * 我的第一个定时器任务
     */
    @XxlJob("funJobHandler")
    public void funJobHandler() {
        System.out.println("啊~我执行了~~~~");
    }
}

我将 xxl-job 的配置整合成一个bean对象,跟之前的 properties 配置文件方式有点不太一样
XxlJobBeanConfig.java

@Component
@ConfigurationProperties(prefix = "xxl-job")
@Data
public class XxlJobBeanConfig {
    private Admin admin;
    private Executor executor;
    private String accessToken;

    @Data
    public static class Executor {
        private String appName;
        private String ip;
        private int port;
        private String logPath;
        private int logRetentionDays;
    }

    @Data
    public static class Admin {
        private String addresses;
    }
}

XxlJobConfig.java xxl-job 定时器配置类

@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class XxlJobConfig {
    private final Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);

    private final XxlJobBeanConfig bean;

    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(bean.getAdmin().getAddresses());
        xxlJobSpringExecutor.setAppname(bean.getExecutor().getAppName());
        xxlJobSpringExecutor.setAddress(bean.getAdmin().getAddresses());
        xxlJobSpringExecutor.setIp(bean.getExecutor().getIp());
        xxlJobSpringExecutor.setPort(bean.getExecutor().getPort()+1000); //端口向后偏移1000
        xxlJobSpringExecutor.setAccessToken(bean.getAccessToken());
        xxlJobSpringExecutor.setLogPath(bean.getExecutor().getLogPath());
        xxlJobSpringExecutor.setLogRetentionDays(bean.getExecutor().getLogRetentionDays());

        return xxlJobSpringExecutor;
    }
}

以上就是我这个定时器模块的全部代码了,现在就来运行下启动类
image
回到 任务调度中心 - 执行器管理 ,刷新好像也没看到刚刚启动的服务也没有注册到这个管理当中,那就自己 新增 吧
image
注意:这里的机器地址是yml中执行器的地址,也就是本项目的地址,端口号为设定的端口号,后谜案要加/
去 任务管理 新增定时任务
image
设置好定时规则,启动新增的这个定时任务
image
时间到点,我的定时任务也执行了

查看下调度日志

image


进一步学习

*(1)yml文件说明
xxl.job.admin.addresses 调度中心的部署地址。若调度中心采用集群部署,存在多个地址,则用逗号分隔。执行器将会使用该地址进行”执行器心跳注册”和”任务结果回调”。

xxl.job.executor.appname 执行器的应用名称,它是执行器心跳注册的分组依据。

xxl.job.executor.ip 执行器的IP地址,用于”调度中心请求并触发任务”和”执行器注册”。执行器IP默认为空,表示自动获取IP。多网卡时可手动设置指定IP,手动设置IP时将会绑定Host。

xxl.job.executor.port 执行器的端口号,默认值为9999。单机部署多个执行器时,注意要配置不同的执行器端口。

xxl.job.accessToken 执行器的通信令牌,非空时启用。

xxl.job.executor.logpath 执行器输出的日志文件的存储路径,需要拥有该路径的读写权限。

xxl.job.executor.logretentiondays 执行器日志文件的定期清理功能,指定日志保存天数,日志文件过期自动删除。限制至少保存3天,否则功能不生效。
*(2)新增执行器时,需要填写的信息说明

  • AppName:这是用来唯一标识每个执行器集群的应用名称,执行器会周期性地以AppName为参数进行自动注册。可通过该配置自动发现注册成功的执行器,供任务调度时使用。
  • 名称:执行器的名称,因为AppName限制字母数字等组成,可读性不强,名称可以提高执行器的可读性。
  • 排序:执行器的排序,系统中需要执行器的地方,如任务新增,将会按照该排序读取可用的执行器列表。
  • 注册方式:调度中心获取执行器地址的方式,有以下两种:
  • 自动注册:执行器自动进行执行器注册,调度中心通过底层注册表可以动态发现执行器机器地址。
  • 手动录入:人工手动录入执行器的地址信息,多地址逗号分隔,供调度中心使用。
  • 机器地址:只有在“注册方式”为“手动录入”时可编辑,支持人工维护执行器的地址信息。
    注意:AppName的取值和示例工程的application.properties文件中的xxl.job.executor.appname字段的取值相同,注册方式选择自动注册。新增完成之后,就可以在执行器列表中看到新建的执行器, 而我在写入的时候将applcation.properties替换为了bootstrap.yml,但内容不变;
    *(3) 新增任务时,需要填写的信息说明
  • 执行器:任务绑定的执行器,任务触发调度时将会自动发现注册成功的执行器,实现任务自动发现功能;另一方面,也可以方便地进行任务分组。每个任务必须绑定一个执行器,可以在“执行器管理”页面进行设置。
  • 任务描述:任务的描述信息,便于任务管理。
  • 路由策略:当执行器集群部署时,提供丰富的路由策略,包括:
    FIRST(第一个):固定选择第一个机器。
    LAST(最后一个):固定选择最后一个机器。
    ROUND(轮询):轮流选择每台机器。
    RANDOM(随机):随机选择在线的机器。
    CONSISTENT_HASH(一致性HASH):每个任务按照Hash算法固定选择某一台机器,且所有任务均匀散列在不同机器上。
    LEAST_FREQUENTLY_USED(最不经常使用):使用频率最低的机器优先被选举。
    LEAST_RECENTLY_USED(最近最久未使用):最久为使用的机器优先被选举。
    FAILOVER(故障转移):按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度。
    BUSYOVER(忙碌转移):按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度。
    SHARDING_BROADCAST(分片广播):广播触发对应集群中所有机器执行一次任务,同时传递分片参数;可根据分片参数开发分片任务。
  • Cron:触发任务执行的Cron表达式,具体可百度。
  • 运行模式:
    BEAN模式:任务以JobHandler的方式维护在执行器端;需要结合 “JobHandler”属性匹配执行器中的任务;
    GLUE模式(Java):任务以源码方式维护在调度中心;该模式的任务实际上是一段继承自IJobHandler的Java类代码并以“groovy”源码的方式维护,它在执行器项目中运行,可使用@Resource/@Autowire注入执行器里中的其他服务;
    GLUE模式(Shell):任务以源码方式维护在调度中心;该模式的任务实际上是一段“shell”脚本;
    GLUE模式(Python):任务以源码方式维护在调度中心;该模式的任务实际上是一段“python”脚本;
    GLUE模式(NodeJS):任务以源码方式维护在调度中心;该模式的任务实际上是一段“nodejs”脚本;
  • JobHandler:
    只有在运行模式为“BEAN模式”时生效,对应执行器中新开发的JobHandler类的“@JobHandler”注解自定义的value值。
  • 子任务:
    每个任务都拥有一个唯一的任务ID(任务ID可以从任务列表获取),当本任务执行结束并且执行成功时,将会触发子任务ID所对应的任务的一次主动调度。
  • 阻塞处理策略:调度过于密集,执行器来不及处理时的处理策略:
    失败告警(默认):调度失败和执行失败时,都将会触发失败报警,默认会发送报警邮件。
    失败重试:调度失败时,除了进行失败告警之外,将会自动重试一次;注意在执行失败时不会重试,而是根据回调返回值判断是否重试。
  • 任务参数:任务执行所需的参数,多个参数时用逗号分隔,任务执行时将会把多个参数转换成数组传入。
  • 报警邮件:任务调度失败时邮件通知的邮箱地址,支持配置多邮箱地址,配置多个邮箱地址时用逗号分隔。
  • 负责人:任务的负责人。
    image
    *(4)BEAN模式:
    任务逻辑以JobHandler的形式存在于“执行器”所在项目中,如我们刚刚所演示的Hello,World 的入门案例
    上述代码有三点需要注意:
    必须使用XXL-JOB的@JobHandler注解(第1行),指定JobHandler的名称为“demoJobHandler”,在调度中心新建任务的JobHandler字段的取值要与此相同。
    必须继承IJobHandler抽象类(第3行),并且实现它的execute()方法,这是实现任务逻辑的方法。
    IJobHandler抽象类还有init()方法和destroy()方法,这两个方法是空方法,在任务实例初始化和销毁时调用,任务实现类可以选择性地覆盖这两个方法。
    *(5)GLUE(Java)模式任务,

任务以源码方式维护在调度中心,支持通过Web IDE在线更新,实时编译和生效,因此不需要指定JobHandler。开发流程如下:
Step-1 新建调度任务

参考上文“任务调度属性”对新建的任务进行参数配置,运行模式选择“GLUE模式(Java)”,如下图所示:
image
Step-2 开发任务代码
在任务列表中选中指定的GLUE(Java)任务,点击该任务右侧的“GLUE”按钮,将会前往GLUE任务的Web IDE界面,在该界面支持对任务代码进行开发(也可以在IDE中开发完成后,复制粘贴到编辑中)。

版本回溯功能:在GLUE任务的Web IDE界面,选择右上角下拉框“版本回溯”,会列出该GLUE任务的更新历史(支持30个版本的版本回溯),选择相应版本即可显示该版本代码,保存后GLUE代码即回退到对应的历史版本。GLUE任务代码和Web IDE界面,如下图所示:
image
*(5)分片广播任务
执行器集群部署时,任务路由策略选择“分片广播”的情况下,一次任务调度将会广播触发对应集群中所有执行器执行一次任务,同时传递分片参数,可以根据分片参数开发分片任务。

“分片广播”以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。

“分片广播”和普通任务开发流程一致,不同之处在于可以获取分片参数,通过分片参数进行分片业务处理。开发流程如下:
Step-1 开发JobHandler代码
在示例工程的com.example.demo.jobhandler包中,新建ShardingJobHandler任务类,关键代码如下所示:

@JobHandler(value="shardingJobHandler")
@Service
public class ShardingJobHandler extends IJobHandler {
 @Override
 public ReturnT<String> execute(String param) throws Exception {
 // 分片参数
 ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
 XxlJobLogger.log("分片参数:当前分片序号 = {0}, 总分片数 = {1}", shardingVO.getIndex(), shardingVO.getTotal());
 // 业务逻辑
 for (int i = 0; i < shardingVO.getTotal(); i++) {
 if (i == shardingVO.getIndex()) {
 XxlJobLogger.log("第 {0} 片, 命中分片开始处理", i);
 } else {
 XxlJobLogger.log("第 {0} 片, 忽略", i);
 }
 }
 return SUCCESS;
 }
}

述代码的第9行获取分片参数,第10行获取分片参数的两个属性:
shardingVO.getIndex() 当前分片序号(从0开始),执行器集群列表中当前执行器的序号。
shardingVO.getTotal() 总分片数,执行器集群的总机器数量。
Step-2 新建调度任务

参考上文“任务调度属性”对新建的任务进行参数配置,运行模式选择“BEAN模式”,路由策略选择“分片广播”,JobHandler属性填写任务注解@JobHandler中定义的值,如下图所示:
image
分片广播的路由策略不仅适用于BEAN运行模式,而且也适用于GLUE(Java)运行模式。这项功能适用于以下业务场景:
分片任务场景
10个执行器的集群来处理10w条数据,每台机器只需要处理1w条数据,耗时降低10倍。
广播任务场景
广播执行器机器运行shell脚本、广播集群节点进行缓存更新等

远程调用XXL-JOB

import com.demo.MySpringBootApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;

/**
 * @create_by: zhanglei
 * @craete_time 2019/7/2
 */
@SpringBootTest(classes = MySpringBootApplication.class)
@RunWith(SpringRunner.class)
public class TestDemo {

    @Test
    public void test() {
        System.out.println("hello,world");
    }

    @Autowired
    private RestTemplate restTemplate;

    /* Cookie 是根据用户名密码生成的,基本不变,可直接保存数据库或者Redis,然后读取即不必反复登录 */
    /* Cookie 如果后期Cookie有失效时间了,则可用定时任务定时刷新或者失效重登重新保存即可 */

    /**
     * 模拟登录并拿到Cookie
     */
    @Test
    public void login(){
        HttpHeaders headers = new HttpHeaders();
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("userName", "admin");
        map.add("password","123456");
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/login", request, String.class);
        System.out.println(response.getHeaders().get("Set-Cookie").get(0));                //      XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly
    }


/*   组操作---> 对执行器进行操作  */

    /**
     * 保存组Group
     */
    @Test
    public void saveGroup(){
        HttpHeaders headers = new HttpHeaders();
        List<String> cookies = new ArrayList<>();
        /* 登录获取Cookie 这里是直接给Cookie,可使用下方的login方法拿到Cookie给入*/
        cookies.add("XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly");
        headers.put(HttpHeaders.COOKIE,cookies);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("appName", "xxl-job-executor-cdmtc-record");        //应用名称
        map.add("title", "测试执行器");      //执行器名称
        map.add("order", "1");          //排序方式
        map.add("addressType", "1");        //注册方式 :  0为
        map.add("addressList", "10.4.7.214:9999,10.4.7.214:9999");          //多地址逗号分隔
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/jobgroup/save", request, String.class);
        System.out.println(response.getBody());        // {"code":200,"msg":null,"content":null}   返回此,且数据库增加数据即为成功
    }

    /**
     * 修改组
     */
    @Test
    public void updateGroup(){
        HttpHeaders headers = new HttpHeaders();
        List<String> cookies = new ArrayList<>();
        /* 登录获取Cookie 这里是直接给Cookie,可使用下方的login方法拿到Cookie给入*/
        cookies.add("XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly");
        headers.put(HttpHeaders.COOKIE,cookies);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("id","4");          //修改的,id一定不能为空
        map.add("appName", "xxl-job-executor-cdmtc-record");        //应用名称
        map.add("title", "测试执行器323223");      //执行器名称
        map.add("order", "1");          //排序方式
        map.add("addressType", "1");        //注册方式 :  0为
        map.add("addressList", "10.4.7.214:9999");          //多地址逗号分隔
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/jobgroup/update", request, String.class);
        System.out.println(response.getBody());         //{"code":200,"msg":null,"content":null}
    }

    /**
     * 删除组
     */
    @Test
    public void removeGroup(){
        HttpHeaders headers = new HttpHeaders();
        List<String> cookies = new ArrayList<>();
        /* 登录获取Cookie 这里是直接给Cookie,可使用下方的login方法拿到Cookie给入*/
        cookies.add("XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly");
        headers.put(HttpHeaders.COOKIE,cookies);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("id","4");          //删除的,id一定不能为空
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/jobgroup/remove", request, String.class);
        System.out.println(response.getBody());         //{"code":200,"msg":null,"content":null}
    }

    /* 定时任务操作:查询,新增,编辑,启动,停止,删除等*/
    /**
     * 获取指定的执行器下的任务列表
     */
    @Test
    public void pageList(){
        HttpHeaders headers = new HttpHeaders();
        List<String> cookies = new ArrayList<>();
        /* 登录获取Cookie 这里是直接给Cookie,可使用下方的login方法拿到Cookie给入*/
        cookies.add("XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly");
        headers.put(HttpHeaders.COOKIE,cookies);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("jobGroup", "2");
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/jobinfo/pageList", request, String.class);
        System.out.println(response.getBody());             //{"recordsFiltered":4,"data":[{"id":13,"jobGroup":2,"jobCron":"0/1 * * * * ? ","jobDesc":"测试HelloWorld","addTime":1561687650000,"updateTime":1562037928000,"author":"zhanglei","alarmEmail":"1326209681@qq.com","executorRouteStrategy":"FIRST","executorHandler":"firstJobHandler","executorParam":"456464564","executorBlockStrategy":"SERIAL_EXECUTION","executorTimeout":0,"executorFailRetryCount":0,"glueType":"BEAN","glueSource":"","glueRemark":"GLUE代码初始化","glueUpdatetime":1561687650000,"childJobId":"","jobStatus":"NONE"},{"id":12,"jobGroup":2,"jobCron":"0/1 * * * * ? ","jobDesc":"测试HelloWorld","addTime":1561612429000,"updateTime":1561612429000,"author":"zhanglei","alarmEmail":"","executorRouteStrategy":"FIRST","executorHandler":"firstJobHandler","executorParam":"","executorBlockStrategy":"SERIAL_EXECUTION","executorTimeout":0,"executorFailRetryCount":0,"glueType":"BEAN","glueSource":"","glueRemark":"GLUE代码初始化","glueUpdatetime":1561612429000,"childJobId":"","jobStatus":"NONE"},{"id":4,"jobGroup":2,"jobCron":"0/1 * * * * ? ","jobDesc":"测试任务1","addTime":1561538414000,"updateTime":1561538431000,"author":"XXL","alarmEmail":"","executorRouteStrategy":"FIRST","executorHandler":"firstJobHandler","executorParam":"123","executorBlockStrategy":"SERIAL_EXECUTION","executorTimeout":100,"executorFailRetryCount":0,"glueType":"BEAN","glueSource":"","glueRemark":"GLUE代码初始化","glueUpdatetime":1561538414000,"childJobId":"","jobStatus":"NONE"},{"id":2,"jobGroup":2,"jobCron":"0/1 * * * * ? ","jobDesc":"测试任务1","addTime":1561532680000,"updateTime":1561612757000,"author":"XXL","alarmEmail":"","executorRouteStrategy":"FIRST","executorHandler":"demoJobHandler","executorParam":"123","executorBlockStrategy":"SERIAL_EXECUTION","executorTimeout":101,"executorFailRetryCount":1,"glueType":"BEAN","glueSource":"","glueRemark":"GLUE代码初始化","glueUpdatetime":1561532680000,"childJobId":"","jobStatus":"NONE"}],"recordsTotal":4}
    }

    /**
     * 增加定时任务配置
     */
    @Test
    public void addInfo(){
        HttpHeaders headers = new HttpHeaders();
        List<String> cookies = new ArrayList<>();
        /* 登录获取Cookie 这里是直接给Cookie,可使用下方的login方法拿到Cookie给入*/
        cookies.add("XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly");
        headers.put(HttpHeaders.COOKIE,cookies);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("jobGroup","1");        //执行器主键id
        map.add("jobCron","0/1 * * * * ? ");        //表达式
        map.add("jobDesc","测试任务我是最新的测试任务啊啊啊啊啊啊");         //任务描述
        map.add("author","zhanglei");           //负责人
        map.add("alarmEmail","1326209681@qq.com");     //报警邮件
        map.add("executorRouteStrategy","FIRST");            //执行器路由策略
        map.add("executorHandler","测试JobHandler");              //执行器,任务Handler名称
        map.add("executorParam","121454");            //执行器,任务参数
        map.add("executorBlockStrategy","SERIAL_EXECUTION");        //阻塞处理策略
        map.add("executorTimeout","101");          //任务执行超时时间,单位秒
        map.add("executorFailRetryCount","1");       //失败重试次数
        map.add("glueType","BEAN");                 //GLUE类型	#com.xxl.job.core.glue.GlueTypeEnum
        map.add("glueSource","");               //GLUE源代码
        map.add("glueRemark","GLUE代码初始化");               //GLUE备注
        map.add("childJobId","");               //子任务ID,多个逗号分隔
//        map.add("jobStatus","");                //任务状态 【base on quartz】
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/jobinfo/add", request, String.class);
        System.out.println(response.getBody());             //{"code":200,"msg":null,"content":"15"}
    }

    /**
     * 修改定时任务配置
     */
    @Test
    public void updateInfo(){
        HttpHeaders headers = new HttpHeaders();
        List<String> cookies = new ArrayList<>();
        /* 登录获取Cookie 这里是直接给Cookie,可使用下方的login方法拿到Cookie给入*/
        cookies.add("XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly");
        headers.put(HttpHeaders.COOKIE,cookies);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("id","14");             //注意:修改必须带主键
        map.add("jobGroup","1");        //执行器主键id
        map.add("jobCron","0/1 * * * * ? ");        //表达式
        map.add("jobDesc","测试任务我是最新的测试任务啊啊啊啊啊啊");         //任务描述
        map.add("author","zhanglei");           //负责人
        map.add("alarmEmail","1326209681@qq.com");     //报警邮件
        map.add("executorRouteStrategy","FIRST");            //执行器路由策略
        map.add("executorHandler","测试JobHandler");              //执行器,任务Handler名称
        map.add("executorParam","121454");            //执行器,任务参数
        map.add("executorBlockStrategy","SERIAL_EXECUTION");        //阻塞处理策略
        map.add("executorTimeout","101");          //任务执行超时时间,单位秒
        map.add("executorFailRetryCount","1");       //失败重试次数
        map.add("glueType","BEAN");                 //GLUE类型	#com.xxl.job.core.glue.GlueTypeEnum
        map.add("glueSource","");               //GLUE源代码
        map.add("glueRemark","GLUE代码初始化");               //GLUE备注
        map.add("childJobId","");               //子任务ID,多个逗号分隔
//        map.add("jobStatus","");                //任务状态 【base on quartz】
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/jobinfo/update", request, String.class);
        System.out.println(response.getBody());             //{"code":200,"msg":null,"content":null}
    }

    /**
     * 删除定时任务配置
     */
    @Test
    public void removeInfo(){
        HttpHeaders headers = new HttpHeaders();
        List<String> cookies = new ArrayList<>();
        /* 登录获取Cookie 这里是直接给Cookie,可使用下方的login方法拿到Cookie给入*/
        cookies.add("XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly");
        headers.put(HttpHeaders.COOKIE,cookies);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("id","15");             //注意:删除必须带主键
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/jobinfo/remove", request, String.class);
        System.out.println(response.getBody());             //{"code":200,"msg":null,"content":null}
    }

    /**
     * 启动定时任务
     */
    @Test
    public void startInfo(){
        HttpHeaders headers = new HttpHeaders();
        List<String> cookies = new ArrayList<>();
        /* 登录获取Cookie 这里是直接给Cookie,可使用下方的login方法拿到Cookie给入*/
        cookies.add("XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly");
        headers.put(HttpHeaders.COOKIE,cookies);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("id","13");             //启动的任务id
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/jobinfo/start", request, String.class);
        System.out.println(response.getBody());             //{"code":200,"msg":null,"content":null}
    }


    /**
     * 停止定时任务
     */
    @Test
    public void stopInfo(){
        HttpHeaders headers = new HttpHeaders();
        List<String> cookies = new ArrayList<>();
        /* 登录获取Cookie 这里是直接给Cookie,可使用下方的login方法拿到Cookie给入*/
        cookies.add("XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly");
        headers.put(HttpHeaders.COOKIE,cookies);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("id","13");             //启动的任务id
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/jobinfo/stop", request, String.class);
        System.out.println(response.getBody());             //{"code":200,"msg":null,"content":null}
    }

    /**
     * 执行一次定时任务
     */
    @Test
    public void startOne(){
        HttpHeaders headers = new HttpHeaders();
        List<String> cookies = new ArrayList<>();
        /* 登录获取Cookie 这里是直接给Cookie,可使用下方的login方法拿到Cookie给入*/
        cookies.add("XXL_JOB_LOGIN_IDENTITY=6333303830376536353837616465323835626137616465396638383162336437; Path=/; HttpOnly");
        headers.put(HttpHeaders.COOKIE,cookies);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("id","13");             //启动的任务id
        map.add("executorParam","13");             //启动的任务参数
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
        ResponseEntity<String> response = restTemplate.postForEntity("http://localhost:8888/xxl-job-admin/jobinfo/trigger", request, String.class);
        System.out.println(response.getBody());             //{"code":200,"msg":null,"content":null}
    }


}