官方文档
因为我们项目需要用到定时任务,而且必须要实现分布式或者可以做到多种方式的执行,一方面是为了业务需要,同时也是为了执行的安全性,于是就默默的用了轻量级的xxl-job。并且它是支持异构语言(多语言),我的一个python项目里面也用到了定时任务,所以可以一并执行。多余的不做过多介绍,直接看大佬的开源项目文档就行,地址为:https://www.xuxueli.com/xxl-job/
部署过程
在部署这个组件之前需要了解下它源码的结构
- xxl-job-admin:调度中心(会依赖于xxl-job-core)
- xxl-job-core:公共依赖
- xxl-job-executor-samples:执行器Sample示例(就是我们项目,会依赖于xxl-job-core)
xxl-job-core
这个是admin任务管理后台和执行器都需要的依赖,在部署之前需要先执行下它的sql脚本,位置如下图,我这里是把它打成了一个starter组件,供admin和执行器app调用。
xxl-job-admin
这个admin后台主要作用:统一管理任务调度平台上调度任务,负责触发调度执行,并且提供任务管理平台。
对于admin的部署,我这里是下载源码然后编译打包成镜像部署的,当然在打包编译前,你需要配置一下它的application.yaml文件的配置,比如配置数据库连接、邮件提醒等。yaml
### 调度中心JDBC链接:链接地址请保持和 2.1章节 所创建的调度数据库的地址一致
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=root_pwd
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
dockerfile
```dockerfile FROM openjdk:8u201-jdk-alpine3.9 MAINTAINER xuxueli
ENV PARAMS=””
ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
jenkins流水线部署只能在工作空间部署,所以加了一层路径
ADD simone-support/xxl-job-admin/target/xxl-job-admin-*.jar /app.jar
ENTRYPOINT [“sh”,”-c”,”java -jar $JAVA_OPTS /app.jar $PARAMS”]
部署完成后,调度中心访问地址:[http://ip:port/xxl-job-admin](http://localhost:8080/xxl-job-admin) (该地址执行器将会使用到,作为回调地址)<br />默认登录账号 “admin/123456”
<a name="GKzz6"></a>
### 启用定时任务
如果你配置完了定时任务后,需要上线的话需要点击启用喔,否则它是不会自动执行你的任务的哦<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/771792/1637999939345-2eb788c0-acf2-45a3-ba49-eb98d77e9d85.png#clientId=u1cdf8a11-5adb-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=435&id=u1533b362&margin=%5Bobject%20Object%5D&name=image.png&originHeight=435&originWidth=1655&originalType=binary&ratio=1&rotation=0&showTitle=false&size=42389&status=done&style=none&taskId=u1bff19aa-44ae-472d-8f47-25c1182e567&title=&width=1655)
<a name="cE34N"></a>
## xxl-job-executor-samples(执行器)
<a name="szUSw"></a>
### 执行器配置
```yaml
xxl:
job:
# 执行器通讯TOKEN [选填]:非空时启用
accessToken: xxx
executor:
# 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
appname: xxl-job-executor-simoneAdmin
# 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
address:
# 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
ip:
# 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个xxl-job registry执行器时,注意要配置不同执行器端口;
port: 10009
# 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
logpath: /data/applogs/xxl-job/simoneAdmin
# 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
logretentiondays: 30
初始化配置文件
package com.jimushow.simone.app.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.Configuration;
@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;
}
}
admin端添加执行器
虽然我们在yaml配置文件中配置了执行器的一些参数,但是并不会自动新增在执行器列表页面,需要我们自身手动去新增
执行器示例任务
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class ScheduleDemo {
/**
* 前提:本项目就是一个执行器,必须初始化 XxlJobConfig 相应的配置
* 1、任务开发:在Spring Bean实例中,开发Job方法;
* 2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。
* 3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志;
* 4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果;
*/
@XxlJob(value = "scheduleAdminDemo")
public void scheduleAdminDemo() throws Exception {
XxlJobHelper.log(XxlJobHelper.getJobParam());
XxlJobHelper.log("---------欢迎来到90年代");
for (int i = 0; i < 3; i++) {
XxlJobHelper.log("欢迎来到90年代:" + i);
TimeUnit.SECONDS.sleep(2);
}
}
}
于此同时需要我们在任务调度中心admin后台中心同时配置该任务,并且jibHandler与我们任务注解的value保持一致。同时配置这个定时任务的corn表达式和路由策略。而且还支持配置任务参数,这个对我们方法需要额外的参数很有用,在实际开发中应用也有用到。
路由策略说明如下
- 路由策略:当执行器集群部署时,提供丰富的路由策略,包括;
FIRST(第一个):固定选择第一个机器;
LAST(最后一个):固定选择最后一个机器;
ROUND(轮询):;
RANDOM(随机):随机选择在线的机器;
CONSISTENT_HASH(一致性HASH):每个任务按照Hash算法固定选择某一台机器,且所有任务均匀散列在不同机器上。
LEAST_FREQUENTLY_USED(最不经常使用):使用频率最低的机器优先被选举;
LEAST_RECENTLY_USED(最近最久未使用):最久未使用的机器优先被选举;
FAILOVER(故障转移):按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度;
BUSYOVER(忙碌转移):按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度;
SHARDING_BROADCAST(分片广播):广播触发对应集群中所有机器执行一次任务,同时系统自动传递分片参数;可根据分片参数开发分片任务;
执行器控制台输出
2021-11-27 15:06:53.780 INFO 13704 --- [Pool-1156631929] c.xxl.job.core.executor.XxlJobExecutor : >>>>>>>>>>> xxl-job regist JobThread success, jobId:2, handler:com.xxl.job.core.handler.impl.MethodJobHandler@42017162[class com.jimushow.simone.app.schedule.ScheduleDemo#scheduleDemo]
2021-11-27 15:06:53.785 INFO 13704 --- [ Thread-92] c.j.simone.app.schedule.ScheduleDemo : 开始执行定时任务
2021-11-37 15:06:53.785 INFO 13704 --- [ Thread-92] c.j.simone.app.schedule.ScheduleDemo : 结束执行定时任务
2021-11-27 15:08:37.142 INFO 13704 --- [ Thread-92] com.xxl.job.core.thread.JobThread : >>>>>>>>>>> xxl-job JobThread stoped, hashCode:Thread[Thread-92,10,main]
xxl-job页面admin执行日志输出
调度机器就是我们admin的ip地址,我这里是通过k8s部署,所以ip为容器内部ip:10.244.0.95,如果你是集群部署则注册的地址肯定是多个,如果你是需要多个机器只执行一次路由策略记得选择单机执行,我这里选择的是随机。如果是轮询的话则也是每次执行一台机器,然后保证逐个轮询到。
2021-12-01 10:44:14 [com.xxl.job.core.thread.JobThread#run]-[130]-[Thread-56]
----------- xxl-job job execute start -----------
----------- Param:{
"from": "admin",
"to": "20010018",
"type": 1
}
2021-12-01 10:44:14 [com.jimushow.simone.admin.schedule.ScheduleXxlJob#scheduleAdminDemo]-[58]-[Thread-56] {
"from": "admin",
"to": "20010018",
"type": 1
}
2021-12-01 10:44:14 [com.jimushow.simone.admin.schedule.ScheduleXxlJob#scheduleAdminDemo]-[59]-[Thread-56] ---------欢迎来到90年代
2021-12-01 10:44:14 [com.jimushow.simone.admin.schedule.ScheduleXxlJob#scheduleAdminDemo]-[61]-[Thread-56] 欢迎来到90年代:0
2021-12-01 10:44:16 [com.jimushow.simone.admin.schedule.ScheduleXxlJob#scheduleAdminDemo]-[61]-[Thread-56] 欢迎来到90年代:1
2021-12-01 10:44:18 [com.jimushow.simone.admin.schedule.ScheduleXxlJob#scheduleAdminDemo]-[61]-[Thread-56] 欢迎来到90年代:2
2021-12-01 10:44:20 [com.xxl.job.core.thread.JobThread#run]-[176]-[Thread-56]
----------- xxl-job job execute end(finish) -----------
----------- Result: handleCode=200, handleMsg = null
2021-12-01 10:44:20 [com.xxl.job.core.thread.TriggerCallbackThread#callbackLog]-[197]-[xxl-job, executor TriggerCallbackThread]
----------- xxl-job job callback finish.
如果你执行失败,控制台会显示rpc 各种错误,很有可能就是你注册的自动注册的地址网络存在问题,或者你手动注册的地址书写错误,因为我执行器配置的端口是9999,所以我这里的地址为:http://192.168.1.105:9999/ 如果你的执行器地址存在多个(集群服务)则用,分隔即可