在 Springboot3.3 中使用 XXL-JOB 怎样解决任务重叠问题?
在 Springboot3.3 中使用 XXL-JOB 怎样解决任务重叠问题?
在现代分布式系统中,定时任务的调度和管理是一个至关重要的环节。任务调度系统需要处理大量的定时任务,这些任务可能需要在特定的时间间隔内重复执行。XXL-JOB 是一款轻量级的分布式任务调度平台,旨在简化任务调度的实现和管理。然而,任务重叠问题常常成为系统设计中的一个挑战。
运行效果:
若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。
任务重叠问题指的是同一任务在预定时间内被多次触发,导致任务执行重叠。这种情况可能会引发一系列问题,包括但不限于:
数据一致性问题:当任务涉及对共享数据的写操作时,重叠执行可能导致数据不一致。例如,任务 A 和任务 B 都在同一时间修改了同一数据记录,最终的结果可能会丢失其中一个任务的修改。
资源浪费:任务重叠可能会导致系统资源的浪费。例如,如果任务需要大量计算资源或网络带宽,多次执行同一任务会占用更多的资源,影响系统的整体性能。
业务逻辑错误:某些任务可能需要确保在某一时间点只有一个实例在运行,如果任务重叠执行,可能会导致业务逻辑错误或数据处理异常。
性能问题:任务重叠可能会引发性能瓶颈,尤其是在高负载环境下。如果系统未能有效管理任务执行,可能会导致系统响应时间延迟和处理能力下降。
为了解决这些问题,我们需要采用有效的策略来防止任务重叠。XXL-JOB 提供了多种调度策略和配置选项来应对这一挑战。通过合理配置任务执行策略、使用分布式锁等手段,可以有效地避免任务重叠,从而提升系统的稳定性和可靠性。
在本文中,我们将深入探讨如何使用 XXL-JOB 来解决任务重叠问题。我们将介绍 XXL-JOB 的基础配置,展示如何通过配置任务调度策略来防止任务重叠,并提供实际代码示例来展示如何在任务逻辑中实现防重叠机制。通过这些方法和技巧,我们可以更好地管理任务调度,确保系统的高效运行。
项目依赖配置
首先,需要在 pom.xml
文件中添加 XXL-JOB 的相关依赖。以下是一个示例配置:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.icoderoad</groupId>
<artifactId>xxl-job-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>xxl-job-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<xxl-job-core.version>2.4.1</xxl-job-core.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- XXL-JOB 核心依赖 -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job-core.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件设置
在 application.yml
中进行 XXL-JOB 的基础配置:
spring:
data:
redis:
host: localhost
port: 6379
password: 123456
timeout: 2000
xxl:
job:
admin:
addresses: http://localhost:8080/xxl-job-admin
executor:
# 执行器的名称
name: my-job-executor
#执行器IP 默认自动获取
ip: localhost
#执行器端口 小于等于0 自动获取 ,默认 9999 ,配置多个执行器时,需要配置不同的执行器端口
port: 9999
logPath: /var/log/xxl-job
# 执行器的日志保留天数
logRetentionDays: 30
防止任务重叠
安装Xxl-job:
可以使用docker拉取镜像部署和源码编译两种方式,这里选择源码编译安装。
代码拉取地址:
https://github.com/xuxueli/xxl-job/tree/2.4.1
官方开发文档:
https://github.com/xuxueli/xxl-job/blob/2.4.1/doc/XXL-JOB%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3.md
2、打开项目
使用Maven打开项目,下载相关的jar包依赖。配置相关配置文件,主要修改数据库配置
/xxl-job/xxl-job-admin/src/main/resources/application.properties
这里可以参考官方开发文档进行配置。
3、初始化数据库
项目中包含数据库文件路径
/xxl-job/doc/db/tables_xxl_job.sql
4、打包部署项目
使用maven进行打包,排除test。
mvn clean package -DskipTests
执行package:
[INFO]
[INFO] --- spring-boot:2.7.18:repackage (default) @ xxl-job-executor-sample-springboot ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for xxl-job 2.4.2-SNAPSHOT:
[INFO]
[INFO] xxl-job ............................................ SUCCESS [ 0.209 s]
[INFO] xxl-job-core ....................................... SUCCESS [ 4.947 s]
[INFO] xxl-job-admin ...................................... SUCCESS [ 3.561 s]
[INFO] xxl-job-executor-samples ........................... SUCCESS [ 0.009 s]
[INFO] xxl-job-executor-sample-frameless .................. SUCCESS [ 0.641 s]
[INFO] xxl-job-executor-sample-springboot ................. SUCCESS [ 0.279 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10.110 s
[INFO] Finished at: 2024-08-30T14:45:04+08:00
打包成功。
5、启动编译过后的Jar包文件
cd /xxl-job/xxl-job-admin/target/
java -jar ./xxl-job-admin-2.4.2-SNAPSHOT.jar
14:50:47.681 logback [main] INFO o.s.b.a.w.s.WelcomePageHandlerMapping - Adding welcome page template: index
14:50:47.927 logback [xxl-job, admin JobLogReportHelper] INFO com.zaxxer.hikari.HikariDataSource - HikariCP - Start completed.
14:50:48.889 logback [main] INFO o.s.b.a.e.web.EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator'
14:50:48.949 logback [main] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
14:50:48.977 logback [main] INFO o.a.c.c.C.[.[.[/xxl-job-admin] - Initializing Spring DispatcherServlet 'dispatcherServlet'
14:50:48.977 logback [main] INFO o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
14:50:48.981 logback [main] INFO o.s.web.servlet.DispatcherServlet - Completed initialization in 4 ms
14:50:48.983 logback [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path '/xxl-job-admin'
14:50:49.010 logback [main] INFO c.x.job.admin.XxlJobAdminApplication - Started XxlJobAdminApplication in 8.2 seconds (JVM running for 9.026)
14:50:52.006 logback [xxl-job, admin JobScheduleHelper#scheduleThread] INFO c.x.j.a.c.thread.JobScheduleHelper - >>>>>>>>> init xxl-job admin scheduler success.
输出以上内容,xxl-job启动成功。
调度中心访问地址:http://localhost:8080/xxl-job-admin (该地址执行器将会使用到,作为回调地址)
默认登录账号 “admin/123456”, 登录后运行界面如下图所示。
配置任务触发策略
在 XXL-JOB 管理后台,设置任务的触发策略为“串行执行”可以有效避免任务重叠。具体配置步骤如下:
登录 XXL-JOB 管理后台。
选择“任务管理” -> “新增任务”。
使用分布式锁
Redis 配置类
package com.icoderoad.xxl_job_demo.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import lombok.Data;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.data.redis")
public class JedisConfig {
private String host;
private int port;
private String password;
private int timeout;
@Bean
public JedisPool jedisPool() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(10);
poolConfig.setMaxIdle(5);
poolConfig.setMinIdle(1);
return new JedisPool(poolConfig, host, port, timeout, password);
}
}
在 Spring Boot 项目中创建一个 MyJobHandler
类,该类通过 Redis 分布式锁来防止任务重叠执行。
package com.icoderoad.xxl_job_demo.handler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.xxl.job.core.handler.annotation.XxlJob;
import jakarta.annotation.PostConstruct;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;
@Component
public class MyJobHandler {
@Autowired
private JedisPool jedisPool;
private Jedis jedis;
@PostConstruct
public void init() {
// 初始化 Jedis 实例
jedis = jedisPool.getResource();
// 可以在这里进行其他初始化操作
System.out.println("Jedis 实例已初始化。");
}
@XxlJob("myJobHandler")
public void execute() throws Exception {
String lockKey = "my-job-lock";
String lockValue = "lock";
SetParams setParams = new SetParams().nx().px(60000); // NX: 不存在时才设置, PX: 设置超时时间60秒
String result = jedis.set(lockKey, lockValue, setParams);
if ("OK".equals(result)) {
try {
// 执行任务逻辑
System.out.println("任务开始执行...");
// 任务逻辑代码块
} finally {
// 释放锁
jedis.del(lockKey);
System.out.println("任务执行完成,锁已释放。");
}
} else {
// 如果锁已经存在,说明另一个任务正在执行,跳过本次执行
System.out.println("任务已在执行,跳过本次执行。");
}
}
}
控制器中的触发逻辑
在你的控制器中添加一个用于触发任务的 POST 方法:
package com.icoderoad.xxl_job_demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.icoderoad.xxl_job_demo.handler.MyJobHandler;
@Controller
@RequestMapping("/job")
public class JobController {
@Autowired
private MyJobHandler myJobHandler;
@PostMapping("/trigger")
@ResponseBody
public String triggerJob() {
// 这里手动触发 XXL-JOB 任务 (通常情况下,你会让调度器自行触发)
try {
myJobHandler.execute();
return "任务已成功触发!";
} catch (Exception e) {
return "任务触发失败:" + e.getMessage();
}
}
}
视图控制器
package com.icoderoad.xxl_job_demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class IndexController {
@GetMapping("/")
public String index() {
return "index";
}
}
前端页面示例
在前端页面中添加触发任务的功能。使用上面提到的 index.html
页面示例,通过按钮触发后端任务执行:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>XXL-JOB Dashboard</title>
<!-- 引入 Bootstrap CSS -->
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css">
<!-- 引入 jQuery -->
<script src="http://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- 引入 Bootstrap JS -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container">
<h1>XXL-JOB Dashboard</h1>
<button id="triggerBtn" class="btn btn-primary">触发任务</button>
<p id="resultMessage"></p>
</div>
<script>
$(document).ready(function() {
$('#triggerBtn').click(function() {
$.ajax({
type: "POST",
url: "/job/trigger",
success: function(response) {
$('#resultMessage').text(response);
},
error: function() {
$('#resultMessage').text("任务触发失败,发生错误!");
}
});
});
});
</script>
</body>
</html>
启动服务 ,通过浏览器访问 http://localhost:8082/ ,点击触发任务按钮,控制台输出如下所示:
2024-08-30T15:49:09.150+08:00 INFO 78329 --- [nio-8082-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2024-08-30T15:49:09.153+08:00 INFO 78329 --- [nio-8082-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3 ms
任务开始执行...
任务执行完成,锁已释放。
任务开始执行...
任务执行完成,锁已释放。
总结
通过 XXL-JOB 的配置和分布式锁的应用,可以有效地解决任务重叠问题。结合正确的任务调度策略和编程方式,可以确保任务的稳定性和可靠性。希望本文对大家在使用 XXL-JOB 时解决任务重叠问题有所帮助。