官方文档
https://www.xuxueli.com/xxl-job/#5.4.3 调度中心HA(集群)
调度中心HA(集群)
负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块;
支持可视化、简单且动态的管理调度信息,包括任务新建,更新,删除,GLUE开发和任务报警等,所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器Failover。
- DB配置保持一致;
- 集群机器时钟保持一致(单机集群忽视);
- 建议:推荐通过nginx为调度中心集群做负载均衡,分配域名。调度中心访问、执行器回调配置、调用API服务等操作均通过该域名进行。
执行器HA(集群)
负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;
接收“调度中心”的执行请求、终止请求和日志请求等。
- 执行器回调地址(xxl.job.admin.addresses)需要保持一致;执行器根据该配置进行执行器自动注册等操作。
- 同一个执行器集群内AppName(xxl.job.executor.appname)需要保持一致;调度中心根据该配置动态发现不同集群的在线执行器列表。
任务HA(Failover)
执行器如若集群部署,调度中心将会感知到在线的所有执行器,如“127.0.0.1:9997, 127.0.0.1:9998, 127.0.0.1:9999”。
当任务”路由策略”选择”故障转移(FAILOVER)”时,当调度中心每次发起调度请求时,会按照顺序对执行器发出心跳检测请求,第一个检测为存活状态的执行器将会被选定并发送调度请求。
调度成功后,可在日志监控界面查看“调度备注”,如下;
“调度备注”可以看出本地调度运行轨迹,执行器的”注册方式”、”地址列表”和任务的”路由策略”。”故障转移(FAILOVER)”路由策略下,调度中心首先对第一个地址进行心跳检测,心跳失败因此自动跳过,第二个依然心跳检测失败……
直至心跳检测第三个地址“127.0.0.1:9999”成功,选定为“目标执行器”;然后对“目标执行器”发送调度请求,调度流程结束,等待执行器回调执行结果。
故障转移 & 失败重试
一次完整任务流程包括”调度(调度中心) + 执行(执行器)”两个阶段。
“故障转移”发生在调度阶段,在执行器集群部署时,如果某一台执行器发生故障,该策略支持自动进行Failover切换到一台正常的执行器机器并且完成调度请求流程。
“失败重试”发生在”调度 + 执行”两个阶段,支持通过自定义任务失败重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试;
部署
镜像准备
- xxl-job-admin
docker build -t xuxueli/xxl-job-admin:2.4.2 .
- 微服务集成xxl-job
- 修改pom.xml
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0</version>
</dependency>
- 增加配置类
package com.icss.ebu.spms.sioc.task.config;
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;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
/**
* 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();
*/
}
- application.yml增加配置
xxl:
job:
admin:
addresses: http://10.88.40.40:18080/xxl-job-admin,http://10.88.40.40:28080/xxl-job-admin
accessToken: default_token
executor:
appname: 应用名
address:
ip:
port: 9998
logpath: ./data/applogs/xxl-job/jobhandler
logretentiondays: 30
- 任务改造
@XxlJob("test")
public void test() {
log.info("定时 开始 !");
try {
System.out.println("to do something...");
} catch (Exception e) {
log.error("定时 异常!", e);
}
log.info("定时 结束 !");
}
- 应用服务镜像准备
FROM openjdk:8-jre-slim
MAINTAINER lizemin
ENV PARAMS=""
ENV JAVA_OPTS=" -Dfile.encoding=UTF-8 "
ENV TZ=PRC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ADD target/*.jar /app.jar
ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /app.jar $PARAMS"]
docker-compose
version: '3.8'
services:
nginx:
image: nginx:latest
container_name: nginx
ports:
- "19200:80"
volumes:
- "E:/Docker/xxl-job/nginx/conf/nginx.conf:/etc/nginx/nginx.conf"
- "E:/Docker/xxl-job/nginx/conf.d:/etc/nginx/conf.d"
- "E:/Docker/xxl-job/nginx/log:/var/log/nginx"
- "E:/Docker/xxl-job/nginx/html:/usr/share/nginx/html"
xxlJobAdmin1:
image: xuxueli/xxl-job-admin:2.4.2
container_name: xxlJobAdmin1
ports:
- "18080:8080"
volumes:
- "E:/Docker/xxl-job/log-1:/data/applogs"
xxlJobAdmin2:
image: xuxueli/xxl-job-admin:2.4.2
container_name: xxlJobAdmin2
ports:
- "28080:8080"
volumes:
- "E:/Docker/xxl-job/log-2:/data/applogs"
task1:
image: 应用名:1.0.0
container_name: task1
task2:
image: 应用名:1.0.0
container_name: task2
task3:
image: 应用名:1.0.0
container_name: task3
task4:
image: 应用名:1.0.0
container_name: task4
nginx配置
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
upstream xxlJobAdmin{
server xxlJobAdmin1:8080;
server xxlJobAdmin2:8080;
}
server {
listen 80;
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /xxl-job-admin {
proxy_pass http://xxlJobAdmin;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header Access-Control-Allow-Origin *;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
验证
- 访问
nginx
登录xxl-job-admin
http://10.88.40.40:19200/xxl-job-admin/toLogin
账号密码:admin/123456
- 执行器验证
-
任务管理
-
故障转移验证
评论区