xxl-job,任务调度中心快速上手
前言
XXL-JOB是一个可以在WEB界面配置执行定时任务中间件,支持分布式服务调用,XXL-JOB自身也可以部署多个节点组成集群,本身是一个基于SpringBoot的Java WEB程序,我们可以通过下载GitHub源码进行部署。
一、XXL-JOB 安装教程
进入 xxl-job官网,里面有更详细的教程 XXL-JOB官网
1、下载源码
git clone http://gitee.com/xuxueli0323/xxl-job
通过IDEA打开后目录如下:
2、初始化"调度数据库"
“调度数据库初始化SQL脚本” 位置为:
`/xxl-job/doc/db/tables_xxl_job.sql`
没错,你得执行这个sql文件到你的数据库中,执行完后 会有如下库和表出现在数据库中
3、项目构造
xxl-job-admin:调度中心
xxl-job-core:公共依赖
xxl-job-executor-samples:执行器Sample示例(选择合适的版本执行器,可直接使用,也可以参考其并将现有项目改造成执行器)
:xxl-job-executor-sample-springboot:Springboot版本,通过Springboot管理执行器,本文章以这种方式介绍,官方推荐该方式;
:xxl-job-executor-sample-frameless:无框架版本;
好了~,到这一步你的XXL—JOB算是装好了,下一步让我们来启动它
二、启动调度中心前的准备
调度中心项目:xxl-job-admin
《超关键,起的就是这玩意儿》作用:统一管理任务调度平台上调度任务,负责触发调度执行,并且提供任务管理平台。
先修改一下调度中心xxl-job-admin
的配置文件。
`/xxl-job/xxl-job-admin/src/main/resources/application.properties`
主要修改数据库为自己的刚才执行sql的库
### xxl-job, datasource spring.datasource.url=jdbc:mysql://123.45.678.90:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai spring.datasource.username=xxx用户 spring.datasource.password=xxx密码 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
然后启动项目
`xxl-job-admin/src/main/java/com/xxl/job/admin/XxlJobAdminApplication.java`
调度中心访问地址:http://localhost:8080/xxl-job-admin (该地址执行器将会使用到,作为回调地址)
默认登录账号 “admin/123456”, 登录后运行界面如下图所示。
三、启动执行器项目
1、maven依赖
确认执行器项目的pom文件中引入了 xxl-job-core
的maven依赖;
<dependency> <groupId>com.xuxueli</groupId> <artifactId>xxl-job-core</artifactId> <version>${project.parent.version}</version> <!-- 跟随调度中心的版本 --> </dependency>
2、执行器配置
执行器配置,配置文件地址:
/xxl-job/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties
执行器配置,配置内容说明:
# 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.addresses=http://127.0.0.1:8080/xxl-job-admin ### xxl-job, access token xxl.job.accessToken=default_token ### xxl-job executor appname 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册 ### 执行器分组名称,关系到会注册到哪个组里 xxl.job.executor.appname=learn-xxl-job ### 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=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
主要就是xxl.job.admin.addresses
和 xxl.job.executor.appname
这俩,第一个必须指向正确的调度中心地址
3、创建启动配置文件
因xxl-job没有使用spring-boot-starter,需自行将配置类注入到spring容器中。
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; /** * 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(); */ }
然后启动执行器,成功注册后,在调度中心可以看到
四、编写一个定时任务
1、创建一个任务调度
注意@XxlJob(“sendSMS”)注解中的sendSMS,即任务的唯一名称,之后执行任务调度将会使用。
// 显示在调度中心日志的内容 XxlJobHelper.log("无参数执行一个定时/指定任务"); // 获取任务参数 XxlJobHelper.getJobParam(); // 用于在任务执行失败时向调度中心报告失败信息。调度中心会记录任务的失败状态,并可以根据配置进行相应的处理(如重试、告警等)。 XxlJobHelper.handleFail("参数传递异常");
import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * @author Cyf * @ Date: 2024/12/6 下午5:23 */ @Component public class MyJob { public static final Logger log = LoggerFactory.getLogger(MyJob.class); // 可使用@Resource/@Autowire注入执行器里中的其他服务 // @Autowired // private UserService userService; /** * 无需参数传递 * @throws Exception */ @XxlJob("sendSMS") public void sendSMS() throws Exception { // 记录任务开始的日志 //只会显示再项目日志中 log.info("slf4j的框架"); //只会显示在xxl-job的日志中 XxlJobHelper.log("无参数执行一个定时/指定任务"); } /** * 单个参数传递 * @throws Exception */ @XxlJob("oneParameter") public void sendMessage() throws Exception { XxlJobHelper.log("单个任务参数为:" + XxlJobHelper.getJobParam()); } /** * 多个参数,应用 , 分割 * @throws Exception */ @XxlJob("moreParameters") public void sendMessage2() throws Exception { try { // 获取参数 String param = XxlJobHelper.getJobParam(); String[] methodParams = param.split(","); XxlJobHelper.log("参数1为:" + methodParams[0] + ",参数2为" + methodParams[1]); }catch (Exception e){ XxlJobHelper.handleFail("参数传递异常"); } } }
2、 xxl-job-admin中添加任务
在Cron中配置任务调度的时间周期,可选择CRON或固定速度。JobHandler中需配置@XxlJob注解中的名称。
3、执行任务
4、查看执行日志
项目日志
18:23:05.259 logback [xxl-job, JobThread-9-1733566985234] INFO c.x.j.e.service.jobhandler.MyJob - slf4j的框架
4、定制化拦截器
可以通过Spring Aop拦截@XxlJob注解,去处理一些通用业务逻辑。
例如追加TraceId 进行日志定位
import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.MDC; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.util.StopWatch; import java.io.PrintWriter; import java.io.StringWriter; import java.time.LocalDateTime; import java.util.UUID; /** * @author 为每个执行器增加traceId */ @Slf4j @Order(1) @Aspect @Component public class XxlJobAspect { @Pointcut("@annotation(com.xxl.job.core.handler.annotation.XxlJob)") public void pointCut() { } @Around("pointCut() && @annotation(xxlJob)") public Object doAround(ProceedingJoinPoint point, XxlJob xxlJob) { String traceId = UUID.randomUUID().toString(); MDC.put("traceId", traceId); String jobName = xxlJob.value(); StopWatch sw = new StopWatch(); sw.start(); log.info("定时任务[{}]开始,开始时间:{},输入参数:{}", jobName, LocalDateTime.now(), XxlJobHelper.getJobParam()); Object proceed; try { proceed = point.proceed(); } catch (Throwable e) { log.warn("定时任务[{}]执行失败", jobName, e); failure(e, traceId); return null; } sw.stop(); log.info("定时任务[{}]结束!执行时间:{} ms", jobName, sw.getTotalTimeMillis()); success(traceId); return proceed; } private void failure(Throwable e, String traceId) { //将异常信息输出到xxl-job日志中 XxlJobHelper.handleFail("traceId=" + traceId + ",<br>exception=" + getStackTrace(e)); MDC.remove("traceId"); } private void success(String traceId) { XxlJobHelper.handleSuccess("traceId=" + traceId); MDC.remove("traceId"); } /** * 该方法来捕获异常的堆栈跟踪信息,并将其转换为字符串 * @param e 异常信息 * @return 堆栈跟踪字符串 */ private String getStackTrace(Throwable e) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); e.printStackTrace(printWriter); return stringWriter.toString(); } }
在日志中添加TraceId,快速定位任务链路
<?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"/> <!-- 定义输出格式 --> <!-- %X{traceId} 添加traceId到日志中 --> <property name="PATTERN" value="%d{HH:mm:ss.SSS} %contextName [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n" /> <!-- ConsoleAppender:表示日志将输出到控制台。 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!-- 引入格式 --> <pattern>${PATTERN}</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="file"/> <!-- 引用了前面定义的 STDOUT appender,表示所有符合 info 级别及以上的日志消息都将通过 STDOUT 输出到控制台。--> <appender-ref ref="STDOUT"/> </root> </configuration>
然后重启项目,再次执行任务将打印如下日志
18:23:05.254 logback [xxl-job, JobThread-9-1733566985234] [531a6504-13bc-4bc6-91de-ff64b235110d] INFO c.x.j.e.core.config.XxlJobAspect - 定时任务[sendSMS]开始,开始时间:2024-12-07T18:23:05.254,输入参数: 18:23:05.259 logback [xxl-job, JobThread-9-1733566985234] [531a6504-13bc-4bc6-91de-ff64b235110d] INFO c.x.j.e.service.jobhandler.MyJob - slf4j的框架 18:23:05.260 logback [xxl-job, JobThread-9-1733566985234] [531a6504-13bc-4bc6-91de-ff64b235110d] INFO c.x.j.e.core.config.XxlJobAspect - 定时任务[sendSMS]结束!执行时间:10 ms
[531a6504-13bc-4bc6-91de-ff64b235110d] 为该次请求中的traceId,在调度日志中也会有:
结束语
个人感觉这是个很强大的一款任务调度中心,从0到1帮助小白快速上手,如果此文章对你有帮助,希望留个赞再走 v
本文作者:PromiseForYou
本文链接:https://www.cnblogs.com/promiseforyou/p/18592471
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库