SpringBoot定时任务:使用shedlock解决SpringBoot分布式定时任务

第一步:引入shedlock包
maven中pom文件添加如下配置:

<dependency>
   <groupId>net.javacrumbs.shedlock</groupId>
   <artifactId>shedlock-spring</artifactId>
   <version>4.33.0</version>   使用其他版本
</dependency>

第二步:添加shedlock-provider-jdbc-template依赖(以JDBC为例)
maven中pom文件添加如下配置:

<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>4.33.0</version>
</dependency>

若使用的是其他类型的数据库,需要添加的依赖也不同,以MongoDB为例:MongoDB的依赖如下:

<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-mongo</artifactId>
<version>4.14.0</version>
</dependency>

第三步:向数据库中插入表shedlock(必须)
建表语句如下:

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

第四步:添加配置类

package com.grg.tg.erp.config;

/**
 * 加载定时任务库表
 */
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;

import javax.sql.DataSource;

@EnableScheduling
@Configuration(proxyBeanMethods = false)
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class ShedlockAutoConfiguration {
    private static final String TABLE_NAME = "spring_scheduler_shedlock";

    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(
                JdbcTemplateLockProvider.Configuration.builder()
                        .withJdbcTemplate(new JdbcTemplate(dataSource))
                        .withTableName(TABLE_NAME)
                        .usingDbTime()
                        .build()
        );
    }
}

 

第五步:添加@SchedulerLock到定时器业务方法入口

@SchedulerLock(name = "scheduledTask", lockAtMostFor = ?, lockAtLeastFor = ?)


name属性:锁名称,必须指定,每次只能执行一个具有相同名字的任务,锁名称应该是全局唯一的;
lockAtMostFor属性:设置锁的最大持有时间,为了解决如果持有锁的节点挂了,无法释放锁,其他节点无法进行下一次任务;
lockAtMostForString属性:成功执行任务的节点所能拥有的独占锁的最长时间的字符串表达,例如“PT14M”表示为14分钟
lockAtLeastFor属性:指定保留锁的最短时间。主要目的是在任务非常短的且节点之间存在时钟差异的情况下防止多个节点执行。这个属性是锁的持有时间。设置了多少就一定会持有多长时间,再此期间,下一次任务执行时,其他节点包括它本身是不会执行任务的
lockAtLeastForString属性:成功执行任务的节点所能拥有的独占锁的最短时间的字符串表达,例如“PT14M”表示为14分钟

第六步:测试
本次遇到的问题是使用Scheduled定时发送邮件,在分布式系统中会根据节点数使每个节点发送重复的邮件,这样明显不符合业务要求。本次测试的定时任务代码如下:

    //每天23:59:59 执行  将每天库存记录保存
    @Scheduled(cron = "59 59 23 * * ? ")
    @SchedulerLock(name = "spring_sssschedulr_shedlocks")
    public void SaticScheduleTask() {
        try{
        List<ImmeditStockVo> calculateInventory = stockHouseService.getCalculateInventory();//获取当日操作库存记录及库存
        List<DailyInventoryDTO> list = new ArrayList<>();
        calculateInventory.forEach(e -> {
            list.add(new DailyInventoryDTO()
                    .setPartsId(e.getPartsId())
                    .setStoreId(e.getStoreId())
                    .setWhouseId(e.getWhouseId())
                    .setInventoryQuantityDay(e.getSamedayStockNumber())
                    .setStatisticalDate(e.getSamedayInventoryTime()));
        });
        stockHouseService.saveDailyInventory(list); //统计每天最终库存
        logger.info("执行当日库存记录定时任务"+LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    }catch (Exception e){
            logger.error("执行当日库存记录定时任务异常"+e);
            e.printStackTrace();
        }
    }

 

posted @ 2024-03-01 11:27  皮军旗  阅读(504)  评论(0编辑  收藏  举报