查看原文
其他

在 Springboot3.3 中使用 XXL-JOB 怎样解决任务重叠问题?

编程疏影 路条编程
2024-09-07



在 Springboot3.3 中使用 XXL-JOB 怎样解决任务重叠问题?

在现代分布式系统中,定时任务的调度和管理是一个至关重要的环节。任务调度系统需要处理大量的定时任务,这些任务可能需要在特定的时间间隔内重复执行。XXL-JOB 是一款轻量级的分布式任务调度平台,旨在简化任务调度的实现和管理。然而,任务重叠问题常常成为系统设计中的一个挑战。

运行效果:

若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。

任务重叠问题指的是同一任务在预定时间内被多次触发,导致任务执行重叠。这种情况可能会引发一系列问题,包括但不限于:

  1. 数据一致性问题:当任务涉及对共享数据的写操作时,重叠执行可能导致数据不一致。例如,任务 A 和任务 B 都在同一时间修改了同一数据记录,最终的结果可能会丢失其中一个任务的修改。

  2. 资源浪费:任务重叠可能会导致系统资源的浪费。例如,如果任务需要大量计算资源或网络带宽,多次执行同一任务会占用更多的资源,影响系统的整体性能。

  3. 业务逻辑错误:某些任务可能需要确保在某一时间点只有一个实例在运行,如果任务重叠执行,可能会导致业务逻辑错误或数据处理异常。

  4. 性能问题:任务重叠可能会引发性能瓶颈,尤其是在高负载环境下。如果系统未能有效管理任务执行,可能会导致系统响应时间延迟和处理能力下降。

为了解决这些问题,我们需要采用有效的策略来防止任务重叠。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 管理后台,设置任务的触发策略为“串行执行”可以有效避免任务重叠。具体配置步骤如下:

  1. 登录 XXL-JOB 管理后台。

  2. 选择“任务管理” -> “新增任务”。

使用分布式锁

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 时解决任务重叠问题有所帮助。


今天就讲到这里,如果有问题需要咨询,大家可以直接留言或扫下方二维码来知识星球找我,我们会尽力为你解答。


AI资源聚合站已经正式上线,该平台不仅仅是一个AI资源聚合站,更是一个为追求知识深度和广度的人们打造的智慧聚集地。通过访问 AI 资源聚合网站 https://ai-ziyuan.techwisdom.cn/,你将进入一个全方位涵盖人工智能和语言模型领域的宝藏库。


作者:路条编程(转载请获本公众号授权,并注明作者与出处)
继续滑动看下一个
路条编程
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存