通过@SchedulerLock解决集群部署时定时任务重复执行的问题

使用场景
在集群环境的时候,一个服务可能存在多个实例,定时任务@Scheduled会被多次执行,
在一些业务场景的时候我们需要它只执行 一次,这时候可以使用@SchedulerLock加锁,来达到场景需求
引入依赖
		<dependency>
			<groupId>net.javacrumbs.shedlock</groupId>
			<artifactId>shedlock-spring</artifactId>
			<version>2.2.1</version>
		</dependency>
		<dependency>
			<groupId>net.javacrumbs.shedlock</groupId>
			<artifactId>shedlock-provider-jdbc-template</artifactId>
			<version>2.2.1</version>
		</dependency>

注:ShedLock需要使用外部存储,我这里使用的是mysql8.0

创建数据库表
CREATE TABLE `shedlock` (
  `NAME` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `lock_until` timestamp(3) NULL DEFAULT NULL,
  `locked_at` timestamp(3) NULL DEFAULT NULL,
  `locked_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  PRIMARY KEY (`NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='锁 shedlock';
配置类
package com.sqx.config;

import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.ScheduledLockConfiguration;
import net.javacrumbs.shedlock.spring.ScheduledLockConfigurationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.time.Duration;

/**
 * @description:TODO
 * @author: qiao
 * @create: 2024/4/15 9:41
 */
@Component
public class ShedLockConfig {
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(dataSource);
    }

    @Bean
    public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
        return ScheduledLockConfigurationBuilder.withLockProvider(lockProvider)
                .withPoolSize(10)
                .withDefaultLockAtMostFor(Duration.ofMinutes(10))
                .build();
    }
}

定时任务
package com.sqx.modules.timedtask;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sqx.common.eum.BusinessEnum;
import com.sqx.common.utils.DateUtils;
import com.sqx.modules.app.entity.UserEntity;
import com.sqx.modules.app.entity.UserFollow;
import com.sqx.modules.app.entity.UserMoney;
import com.sqx.modules.app.entity.UserMoneyDetails;
import com.sqx.modules.app.service.UserFollowService;
import com.sqx.modules.app.service.UserMoneyDetailsService;
import com.sqx.modules.app.service.UserMoneyService;
import com.sqx.modules.app.service.UserService;
import com.sqx.modules.autoTask.entity.AutoTask;
import com.sqx.modules.autoTask.service.AutoTaskService;
import com.sqx.modules.common.service.CommonInfoService;
import com.sqx.modules.externalGroup.entity.GroupInfo;
import com.sqx.modules.externalGroup.service.GroupInfoService;
import com.sqx.modules.sys.entity.SysUserEntity;
import com.sqx.modules.talk.entity.dto.TalkDTO;
import com.sqx.modules.talk.service.CommentService;
import com.sqx.modules.talk.service.TalkService;
import com.sqx.modules.talk.utils.DateUtil;
import com.sqx.modules.tickets.entity.UserTickets;
import com.sqx.modules.tickets.entity.UserTicketsRecord;
import com.sqx.modules.tickets.eum.SignStrEnum;
import com.sqx.modules.tickets.service.UserTicketsRecordService;
import com.sqx.modules.tickets.service.UserTicketsService;
import com.sqx.modules.utils.baiduAi.BaiDuAiUtils;
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.core.SchedulerLock;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Component
@Slf4j
public class TimedTask {
    @Autowired
    private UserService userService;
    @Resource
    private UserTicketsService userTicketsService;
    @Resource
    private UserMoneyService userMoneyService;
    @Resource
    private UserMoneyDetailsService userMoneyDetailsService;
    @Resource
    private CommonInfoService commonInfoService;
    @Resource
    private GroupInfoService groupInfoService;
    @Autowired
    private UserTicketsRecordService userTicketsRecordService;

    @Resource
    private AutoTaskService autoTaskService;
    @Resource
    private TalkService talkService;
    @Resource
    private CommentService commentService;
    @Resource
    private UserFollowService userFollowService;

   

//    @Scheduled(cron = "0/10 * * * * ? ", zone = "Asia/Shanghai")  10秒
    /*
    * 每10分钟执行一次
    * lockAtMostFor  任务口节点锁的最大占用时间
    * lockAtLeastFor 任务节点锁的最短占用时间
    * */
    @Scheduled(cron = "0 0/10 * * * ? ", zone = "Asia/Shanghai")
    @SchedulerLock(name = "sendCommentTask", lockAtMostFor = 10000, lockAtLeastFor = 5 * 1000)
    public void sendTalkCommont(){
        try {
            //获取评论任务信息
            AutoTask autoTask = autoTaskService.queryByType(BusinessEnum.TASK_TYPE_COMMONET.getCode());
            if (ObjectUtil.isNotEmpty(autoTask)){
                //获取任务状态 是否为执行状态
                Integer executeStatus = autoTask.getExecuteStatus();
                if (executeStatus.equals(BusinessEnum.EXECUTE_STATUS_Y.getCode())){
                    //执行状态则开始查询autoTask.getTimeInterval()分钟前的动态 然后调用百度AI接口进行评论
                    //获取现在的时间
                    LocalDateTime now = LocalDateTime.now();
                    //获取指定时间X分钟前的时间为结束时间
                    LocalDateTime endTime = now.minusMinutes(autoTask.getTimeInterval());
                    //结束时间的前X分钟为开始时间
                    LocalDateTime startTime = endTime.minusMinutes(autoTask.getTimeInterval());
                    String endTimeStr = endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                    String startTimeStr = startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                    //查询时间内发表的动态
                    List<Map<String, Object>> talkDTOS = talkService.queryList(startTimeStr, endTimeStr);
                    if (ObjectUtil.isNotEmpty(talkDTOS)){
                        for (Map<String, Object> talkDTO : talkDTOS) {
                            //获取动态id  动态文案内容 动态是否被自动评论
                            String content = (String) talkDTO.get("content");
                            Integer talkId = (Integer) talkDTO.get("talkId");
                            Integer isAutoCommont = (Integer) talkDTO.get("isAutoCommont");
                            if (ObjectUtil.isNotEmpty(isAutoCommont) && isAutoCommont.equals(0)){
                                //自动评论为空 状态为未评论 调用百度接口
                                if (StringUtils.isNotEmpty(content)){
                                    String result = BaiDuAiUtils.getResult(content);
                                    if (ObjectUtil.isNotEmpty(result)){
                                        //保存评论及消息通知
                                        commentService.autoTaskSaveComment(talkId,result);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            log.error("自动评论出错啦~"+e.getMessage());
        }
    }
}

@SchedulerLock(name = “sendCommentTask”, lockAtMostFor = 10000, lockAtLeastFor = 5 * 1000)
name 任务名称
lockAtMostFor  任务口节点锁的最大占用时间
lockAtLeastFor 任务节点锁的最短占用时间
***注意:lockAtLeastFor设置的时间大于cron表达式中的时间时,执行时间会按照lockAtLeastFor设置的来执行***
启动类增加注解

@EnableScheduling 开启定时任务
@EnableSchedulerLock(defaultLockAtMostFor = “PT50S”) 开启定时任务锁,
defaultLockAtMostFor——属性表示默认最大锁持有时间,PT50S表示50秒

package com.sqx;

import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@Slf4j
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT50S")
@SpringBootApplication
public class SqxApplication {

	public static void main(String[] args) {
		SpringApplication.run(SqxApplication.class, args);
	}

}
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值