SpringBoot利用redis分布式锁解决集群下定时任务重复执行

一个应用里有利用定时任务统计数据并插入数据库的需求,在单机情况下,不用考虑任何重复执行问题
但在集群下部署多个应用,定时任务就会出现重复执行的问题,为了避免资源浪费和脏数据的问题出现,借助redis分布式锁解决

redis分布式锁原理如下:
基于Redis实现的分布式锁其实很简单,底层就是使用redis的setnx指令来实现的加锁,我们来看看官方对setnx的定义:
SETNX key value
将 key 的值设为 value ,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。
SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。
返回值:
设置成功,返回 1 。
设置失败,返回 0 。
以上内容来自于:http://redisdoc.com/string/setnx.html
既然setnx这么强大,那么我们是不是可以高枕无忧直接使用了? 当然了,我们还要考虑一些极端场景。
比如如果一台机器在运行状态中突然宕机没有设置锁的过期时间无法自动释放锁,那么另一台应用会一直认为有机器占用着分布式锁执行任务,此时实际是没有执行的,而导致死锁问题。
死锁问题解决方法:
在这里我们给分布式锁设置一个过期时间,能够在开始执行任务后自动释放分布式锁
原理图如下:

代码如下:

package com.xjb.redis.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


@Component
public class RedisUtils {

    private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);

    @Autowired
    private RedisTemplate redisTemplate;
     /**
     * 上锁
     * 将键值对设定一个指定的时间timeout.
     *
     * @param key
     * @param timeout 键值对缓存的时间,单位是秒
     * @return 设置成功返回true,否则返回false
     */
    public  boolean tryLock(String key, Object value, long timeout) {
        //底层原理就是Redis的setnx方法
        boolean isSuccess = redisTemplate.opsForValue().setIfAbsent(key, value);
        if (isSuccess) {
            //设置分布式锁的过期时间
            redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
        return isSuccess;
    }

测试类:

package com.xjb.cron;

import com.xjb.redis.utils.RedisUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.net.UnknownHostException;

@Component
public class RedisLockTest {

    private static Logger logger = LoggerFactory.getLogger(RedisLockTest.class);
    @Autowired
    RedisUtils redisUtils;

    String redisKey = "demo-RedisLockTest-isRun";

    //单位为秒  默认三分钟
    private long redis_default_expire_time = 60 * 3;


     @Scheduled(cron = "0 */1 * * * ?")
    public void init() throws InterruptedException {


        //-------------上分布式锁开始-----------------

        InetAddress addr = null;
        try {
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        //获取本机ip
        String ip = addr.getHostAddress();
        //默认上锁时间为五小时
        //此key存放的值为任务执行的ip,
        // redis_default_expire_time 不能设置为永久,避免死锁
        boolean lock = redisUtils.tryLock(redisKey, ip, redis_default_expire_time);
        logger.info("============本次聚类定时任务开始==============");
        if (lock) {
            logger.info("============获得分布式锁成功=======================");
            //TODO 开始执行任务 执行结束后需要释放锁
            run();
            //释放锁
            redisUtils.del(redisKey);
            logger.info("============释放分布式锁成功=======================");

        } else {
            logger.info("============获得分布式锁失败=======================");
            ip = (String) redisUtils.get(redisKey);
            logger.info("============{}机器上占用分布式锁,聚类任务正在执行=======================", ip);
            logger.info("============本次聚类定时任务结束==============");
            return;
        }
    }

    public void run() throws InterruptedException {

        logger.info("执行中");
        Thread.sleep(1000 * 60 * 2);
        logger.info("执行结束");

    }
}
 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
回答: 在Spring Boot中集成Redis分布式锁可以使用Redisson来实现。Redisson是一个基于Redis的分布式对象和服务框架,内部已经实现了Redis分布式锁,使用起来更加方便和稳定。通过Redisson,可以使用RedLock算法来实现分布式锁,确保锁的正确性和可靠性。在Redis集群环境下,可能存在锁失效或死锁的问题,但使用Redisson的分布式锁可以解决这个问题。此外,分布式锁是为了解决分布式系统中控制共享资源访问的问题,因为在分布式系统中,多线程、多进程分布在不同机器上,传统的并发控制锁策略失效。因此,通过集成Redis分布式锁,可以有效地控制共享资源的访问。 #### 引用[.reference_title] - *1* *2* [springboot集成redis 分布式锁(redistemplate,lua,redisson)](https://blog.csdn.net/jun2571/article/details/130382023)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Springboot集成Redis——实现分布式锁](https://blog.csdn.net/tang_seven/article/details/126769580)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值