Spring Boot 整合 ShedLock 处理定时任务重复执行的问题

news2025/1/22 5:42:38

在这里插入图片描述

🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志
🎐 个人CSND主页——Micro麦可乐的博客
🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战
🌺《RabbitMQ》专栏主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战
🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解
💕《Jenkins实战》专栏主要介绍Jenkins+Docker的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程
🌞《Spring Boot》专栏主要介绍我们日常工作项目中经常应用到的功能以及技巧,代码样例完整
如果文章能够给大家带来一定的帮助!欢迎关注、评论互动~

Spring Boot 整合 ShedLock 处理定时任务重复执行的问题

  • 前言
  • 什么是 ShedLock?
    • ShedLock 的工作原理:
  • 定时任务重复执行的问题
  • 使用 ShedLock 解决定时任务重复执行问题
    • ❶ 添加依赖
    • ❷ 配置数据库
    • ❸ 配置 ShedLock
    • ❹ 创建定时任务
    • ❺ 配置数据库连接
    • ❻ 启动应用测试
  • 总结

前言

在分布式系统中,定时任务的执行往往需要考虑到多个实例的并发执行问题。假设一个定时任务会在多个节点上并发执行,可能导致重复执行,甚至引发数据异常或系统不一致问题。为了解决这一问题,ShedLock 是一个简单而有效的解决方案,它可以确保在分布式环境中,只有一个节点在某一时刻执行指定的定时任务。


什么是 ShedLock?

ShedLock 是一个轻量级的 Java 库,用于解决分布式系统中定时任务的重复执行问题。它的核心思想是在数据库中加锁,确保在分布式环境下,只有一个节点能够在指定时间执行某个任务。ShedLock 可以与 Spring SchedulerQuartz 等定时任务框架结合使用。

github开源地址:https://github.com/lukas-krecan/ShedLock 目前拥有 3.7K Star,小伙伴们也可以针对文档更系统性的学习如何应用ShedLock。

ShedLock 的工作原理:

❶ 定时任务执行时,ShedLock 会尝试在数据库中为该任务获取锁。
❷ 如果当前节点成功获取到锁,则执行定时任务。
❸ 如果当前节点未能获取到锁(其他节点已获取锁),则该任务跳过执行,等待下次调度。


定时任务重复执行的问题

在分布式应用中,定时任务往往通过多个实例运行,每个实例都会按照自己的调度计划执行任务。例如,假设有一个定时任务负责同步某个外部系统的数据。如果该任务在多个实例上同时执行,就会导致重复的数据同步,进而造成数据不一致、重复处理等问题。

典型场景
我们来模拟一个场景,假设在一个电商系统中,定时任务负责将库存数据同步到第三方库存管理系统。如果该任务在多个节点上同时执行,可能会导致:

1、重复的库存更新请求。
2、数据不同步或数据丢失。
3、系统负载过高,造成性能瓶颈。


使用 ShedLock 解决定时任务重复执行问题

接下来,博主将通过 Spring Boot 和 ShedLock 来演示如何解决定时任务的重复执行问题。希望能给大家一个参考!

❶ 添加依赖

首先确保我们的的 pom.xml 中包含必要的依赖。需要添加 Spring Boot Starter、Spring Scheduling 和 ShedLock 相关的依赖。

其中博主使用的是Mysql作为ShedLock初始化数据库,引入shedlock-provider-jdbc-template,大家可以根据自己的实际情况,根据作者的文档选择,如下图:
在这里插入图片描述

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Scheduler -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-scheduled</artifactId>
    </dependency>
    
    <!-- ShedLock Core -->
    <dependency>
        <groupId>net.javacrumbs.shedlock</groupId>
        <artifactId>shedlock-spring</artifactId>
        <version>6.2.0</version>
    </dependency>

    <!-- ShedLock JDBC -->
    <dependency>
        <groupId>net.javacrumbs.shedlock</groupId>
        <artifactId>shedlock-provider-jdbc-template</artifactId>
        <version>6.2.0</version>
    </dependency>
    
    <!-- MySQL JDBC Driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

对应版本兼容性依赖:

ShedLock版本最低JVM版本Tested with
6.x.x17Spring 6.2, 6.1
Spring Boot 3.4, 3.3
Micronaut 4
5.x.x17Spring 6.1, 6.0
Spring Boot 3.4, 3.3, 3.2
Micronaut 3, 4
4.x.x8Spring 6.0, 5.3
Spring Boot 3.0, 2.7, 2.6
3.x.x8Spring 5.2, 5.1
Spring Boot 2.2, 2.1
2.x.x8Spring 5.1, 5.0
Spring Boot 2.1
1.x.x8Spring 5.0
Spring Boot 2.0

❷ 配置数据库

ShedLock 需要在数据库中存储锁的信息。这里博主使用 MySQL 来存储锁。首先,创建一个名为 shedlock 的表来存储锁数据。

CREATE TABLE shedlock (
    name VARCHAR(64) PRIMARY KEY,
    lock_until TIMESTAMP(3) NOT NULL,
    locked_at TIMESTAMP(3) NOT NULL,
    locked_by VARCHAR(255) NOT NULL
);

该表的字段意义如下:

  • name: 锁的名称,用于区分不同的任务。
  • lock_until: 锁的过期时间,任务完成后应释放锁。
  • locked_at: 锁的创建时间。
  • locked_by: 锁被哪个节点持有。

❸ 配置 ShedLock

接下来,我们在 Spring Boot 配置类中进行 ShedLock 的配置。以下是一个基本的配置类:

import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "30m") //默认锁最大过期时间为 30 秒
public class ShedLockConfig {
}

注解说明:
@EnableSchedulerLock: 启用 ShedLock 功能,defaultLockAtMostFor 参数指定锁的最大过期时间。如果任务执行超过此时间,锁将会被释放。

@EnableScheduling: 启用 Spring 的调度功能。

❹ 创建定时任务

接下来我们创建一个定时任务,并为其添加 ShedLock 锁,确保在分布式环境中只有一个实例会执行该任务。

package com.example;

import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class DataSyncService {

    /**
     * 使用 @SchedulerLock 注解来确保该任务只会在一个节点上执行
     */
    @Scheduled(fixedRate = 5000)  // 每 5 秒执行一次
    @SchedulerLock(name = "syncInventoryTask", lockAtMostFor = "5m", lockAtLeastFor = "5m")
    public void syncInventory() {
        System.out.println("同步库存数据...");
        // 模拟库存同步操作
        try {
            Thread.sleep(3000);  // 模拟执行耗时 3 秒
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("库存同步完成!");
    }
}

上述代码中,我们在在 syncInventory 方法上,我们使用了 @SchedulerLock 注解,指定锁的名称 syncInventoryTask,并设置锁的最大持续时间为 5 秒。

  • name: 锁的名称,确保每个定时任务有唯一的名称。
  • lockAtMostFor: 锁的最大过期时间,防止任务因为异常而无法释放锁。
  • lockAtLeastFor: 锁的最小持续时间,确保锁至少保持一定时间。

❺ 配置数据库连接

最后 application.properties 或 application.yml 配置我们的数据库连接信息

spring.datasource.url=jdbc:mysql://localhost:3306/your_database
spring.datasource.username=root
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update

❻ 启动应用测试

最后我们启动 Spring Boot 应用,查看控制台输出。你将会看到 syncInventory 任务每 5 秒钟执行一次,但只有一个节点能够在任何时刻成功执行该任务,避免了多节点并发执行造成的问题。


总结

相信小伙伴们通过本文的示例,我们演示了如何使用 ShedLock 解决分布式系统中定时任务重复执行的问题。ShedLock 提供了一种简单有效的方式来确保在多实例部署的环境中,定时任务不会被重复执行,避免了数据不一致等问题。

  • 应用场景: 分布式定时任务、任务调度、数据同步等场景。
  • 优势: 简单易用、无需复杂配置、适配多种存储方式(如 JDBC、MongoDB、Redis等)

如果本文对您有所帮助,希望 一键三连 给博主一点点鼓励,如果您有任何疑问或建议,请随时留言讨论!


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2280188.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSDN年度回顾:技术征途上的坚实步伐

嘿&#xff0c;时光过得可真快呀&#xff0c;就像那匹跑得飞快的白马&#xff0c;嗖的一下&#xff0c;2024 年的日历就这么悄无声息地翻到了最后一页。这会儿我回头看看在 CSDN 上度过的这一年&#xff0c;心里那叫一个感慨万千&#xff0c;满满的都是喜悦&#xff0c;就像心里…

解决 PostgreSQL 中创建 TimescaleDB 扩展的字符串错误

解决 PostgreSQL 中创建 TimescaleDB 扩展的字符串错误 在使用 PostgreSQL 数据库并尝试创建 TimescaleDB 扩展时&#xff0c;你可能会遇到一些棘手的错误。今天&#xff0c;我们就来探讨一个常见的错误信息及相应的解决方法&#xff1a; CREATE EXTENSION IF NOT EXISTS tim…

【语言处理和机器学习】概述篇(基础小白入门篇)

前言 自学笔记&#xff0c;分享给语言学/语言教育学方向的&#xff0c;但对语言数据处理感兴趣但是尚未入门&#xff0c;却需要在论文中用到的小伙伴&#xff0c;欢迎大佬们补充或绕道。ps&#xff1a;本文不涉及公式讲解&#xff08;文科生小白友好体质&#xff09;&#xff…

【ESP32】ESP32连接JY61P并通过WIFI发送给电脑

前言 手头上有个ESP32&#xff0c;发现有wifi功能&#xff0c;希望连接JY61P并通过WIFI把姿态数据发送给电脑 1.采用Arduino IDE编译器&#xff1b;需要安装ESP32的开发板管理器&#xff1b; 2.电脑接受数据是基于python的&#xff1b; 1. ESP32 连接手机WIFI #include <…

将 Docker 安装到 Windows 的 D 盘的方法

1.官网下载Docker Desktop Installer.exe&#xff0c;官网网址&#xff1a;Get Started | Docker 2.以管理员身份打开 Windows 终端 3.切换到 你Docker Desktop Installer.exe下载到的地址 4.在运行代码前&#xff0c;需要提前手动创建好D:\Program Files\Docker和D:\Program F…

DeepSeek-R1-GRPO理解

一、GRPO GRPO&#xff08;Group Relative Policy Optimization&#xff09;是一种强化学习&#xff08;Reinforcement Learning, RL&#xff09;算法&#xff0c;专门用于训练大型语言模型&#xff08;LLMs&#xff09;在复杂任务&#xff08;如数学推理、代码生成等&#xf…

Unreal Engine 5 C++ Advanced Action RPG 十章笔记

第十章 Survival Game Mode 2-Game Mode Test Map 设置游戏规则进行游戏玩法 生成敌人玩家是否死亡敌人死亡是否需要刷出更多 肯定:难度增加否定:玩家胜利 流程 新的游戏模式类游戏状态新的数据表来指定总共有多少波敌人生成逻辑UI告诉当前玩家的敌人波数 3-Survival Game M…

接口(1)

大家好&#xff0c;今天我们来看看接口的概念&#xff0c;接口跟类的使用是有一些区别的&#xff0c;接口中都是抽象方法&#xff0c;简单介绍一下后&#xff0c;我们正式来学习。 2、接口 接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用. 在java中,…

docker离线安装及部署各类中间件(x86系统架构)

前言&#xff1a;此文主要针对需要在x86内网服务器搭建系统的情况 一、docker离线安装 1、下载docker镜像 https://download.docker.com/linux/static/stable/x86_64/ 版本&#xff1a;docker-23.0.6.tgz 2、将docker-23.0.6.tgz 文件上传到服务器上面&#xff0c;这里放在…

debian中apt的配置与解析

引言 在系统使用过程中&#xff0c;我们可能会遭遇 apt update 操作出现问题&#xff0c;或者 apt upgrade 速度迟缓的情况。这往往是由于所使用软件源本身存在诸如服务器性能不佳、维护不及时等质量问题&#xff0c;同时&#xff0c;软件源服务器与我们所处地理位置的距离较远…

python创建一个httpServer网页上传文件到httpServer

一、代码 1.server.py import os from http.server import SimpleHTTPRequestHandler, HTTPServer import cgi # 自定义请求处理类 class MyRequestHandler(SimpleHTTPRequestHandler):# 处理GET请求def do_GET(self):if self.path /:# 响应200状态码self.send_response(2…

Git处理冲突详解

文章目录 Git处理冲突详解一、引言二、冲突产生的原因三、解决冲突的步骤1. 手动解决冲突1.1 查看冲突文件1.2 编辑冲突文件1.3 提交解决冲突 2. 使用合并工具解决冲突 四、使用示例五、总结 Git处理冲突详解 一、引言 在团队协作开发中&#xff0c;Git冲突是不可避免的。当多…

如何降低振荡器的相位噪音

相位噪音&#xff08;Phase Noise&#xff09;是振荡器设计中一个重要的性能指标&#xff0c;尤其在高精度和高频应用中。相位噪音不仅影响信号的质量&#xff0c;还可能对后续系统的性能造成显著影响。因此&#xff0c;如何有效降低振荡器的相位噪音成为了科研与工业界关注的热…

一文读懂 RocketMQ:从概念到架构与应用原理概述

文章目录 概述架构说明核心组件核心概念 namesvrproducer默认实现producer启动消息发送 broker-mq核心基本模型集群模型内部模型存储机制高可用 consumerpush类型push流程pull类型 概述 随着分布式技术在业内的快速应用&#xff0c;mq&#xff08;消息队列&#xff09;做为不可…

算法日记8:StarryCoding60(单调栈)

一、题目 二、解题思路&#xff1a; 题意为让我们找到每个元素的左边第一个比它小的元素&#xff0c;若不存在则输出-1 2.1法一&#xff1a;暴力&#xff08;0n2&#xff09; 首先&#xff0c;我们可以想到最朴素的算法&#xff1a;直接暴力两层for达成目标核心代码如下&…

map和set的使用(一)详解

文章目录 序列式容器和关联式容器map和set的介绍set构造和迭代器遍历和insertfinderaseswapclearcountlower_bound和upper_boundmultiset和set的对比 set的二个题目题目解析算法原理代码介绍一个找差集的算法同步算法题目解析算法原理代码 map构造遍历initiaizer_list 序列式容…

CSS布局与响应式

学习链接 Grid网格布局 前端五大主流网页布局 flex布局看这一篇就够了 grid布局看这一篇就够了 用六个案例学会响应式布局 伸缩盒响应式页面布局实战 实现响应式布局的五种方式 - csdn 如何完成响应式布局&#xff0c;有几种方法&#xff1f;看这个就够了 响应式布局总…

P6周:VGG-16算法-Pytorch实现人脸识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 我的环境 语言环境&#xff1a;Python 3.8.12 编译器&#xff1a;jupyter notebook 深度学习环境&#xff1a;torch 1.12.0cu113 一、前期准备 1.设置GPU im…

Git:问题解决办法 及 Tips 总结

目录 查看历史记录及比较区别查看所有提交的历史记录查看提交的详细历史记录查看提交之间的差异点击文件操作历史&#xff0c;筛选出所有改动过此文件的提交任意两个提交之间的比较&#xff08;开发及查BUG常用&#xff09; 在需要版本回滚的情况下处理方法短时间内无法彻查BUG…

【优选算法】----移动零

好久没写博客的兄弟姐妹们~ 今天来写一篇算法过过水吧~ --------------------------------------begin---------------------------------------- 题目要求&#xff1a; 给的难度是简单哦 题目解析&#xff1a; 这道题的思路可以借助双指针的思想&#xff0c;定义dest和c…