• 如何使用ShedLock实现高效可靠的分布式定时任务
  • 发布于 1周前
  • 51 热度
    0 评论
背景
在现代的微服务架构中,分布式系统已经成为主流。随着业务的复杂度增加,对于定时任务的需求也日益增多。传统的单体应用中的定时任务实现方式,在分布式环境中可能会导致任务重复执行的问题。
实现
传统定时任务
/**
 * 每5分钟跑一次
 */
@Scheduled(cron = "0 */5 * * * ?")
public void job1() {
    log.info("time=" + DateTime.now().toString("YYYY-MM-dd HH:mm:ss") + " do job1...");
}
在分布式环境中容易导致任务重复执行,也许会有人提出使用Quartz,Quartz更适合动态的定时任务。为了解决这一问题,ShedLock提供了一种简单而有效的解决方案,它能够确保在分布式环境下定时任务只被执行一次。

ShedLock是一个轻量级的库,用于防止同一时刻多个节点执行相同的定时任务,它通过在支持的数据库中创建锁来实现这一点,当一个节点获取到锁后,其他节点将无法在同一时间点获取同一个锁,从而避免了任务的重复执行,ShedLock支持多种数据存储,包括但不限于Redis、MongoDB、MySQL等。


ShedLock
核心概念:
锁:ShedLock的核心是锁的概念,每个定时任务对应一个锁。
锁管理器:负责与数据存储交互,进行锁的获取和释放。
任务调度:使用Spring的@Scheduled注解或者Quartz等调度框架来定义任务执行的时间。

具体实现
1.创建所需表:
create table shedlock(
 name VARCHAR(64) not null,
 lock_until TIMESTAMP(3) not null,
 locked_at TIMESTAMP(3) not null default CURRENT_TIMESTAMP(3),
 locked_by VARCHAR(255) not null,
primary key (name));
2.引入依赖:
<!-- 堆代码 duidaima.com -->
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>4.23.0</version>
</dependency>
 <!--每个外部存储实例所需依赖包不一样,这里是jdbc-->
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>4.23.0</version>
</dependency>
3.配置LockProvider:
// 标识该类为配置类
@Configuration
//开启定时器
@EnableScheduling
// 开启定时任务锁,指定一个默认的锁的时间30秒
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class ShedlockJdbcConfig {

    /**
     * 配置锁的提供者
     */
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(
                JdbcTemplateLockProvider.Configuration.builder()
                        .withJdbcTemplate(new JdbcTemplate(dataSource))
                        .usingDbTime()
                        .build()
        );
    }
}
4.创建定时任务:
/**
* 每1分钟跑一次
* lockAtLeastFor:分布式锁定义的是:每次任务要锁住20秒,20秒是持有锁的最小时间,必须等20秒后才释放锁,并且确保在20秒钟内,该任务不会运行超过1次;
* lockAtMostFor:锁最大持有时间30秒,表示最多锁定30秒钟,主要用于防止执行任务的节点挂掉(即使这个节点挂掉,在30秒钟后,锁也被释放),一般将其设置为明显大于任务的最大执行时长;如果任务运行时间超过该值(即任务30秒钟没有执行完),则该任务可能被重复执行。
*/
@Scheduled(cron = "0 */1 * * * ?")
@SchedulerLock(name = "job1",lockAtLeastFor = "20000", lockAtMostFor = "30000")
public void job1() {
    log.info("time=" + DateTime.now().toString("YYYY-MM-dd HH:mm:ss") + " do job1...");
}
相比较于Quartz,ShedLock实现更加简单一些,不需要配置很多及创建多张表。
ShedLock 适合需要在分布式环境中确保定时任务唯一性执行的场景,集成简单,使用方便。
Quartz 适合需要复杂调度策略、高可用性和任务持久化的场景,功能强大但集成复杂度较高。
用户评论