定时任务在集群部署的下防重复调度问题, redis 分布式锁

业务场景: 程序中有个一分钟执行一次的策略遍历与判断的任务,为了防止多台机器重复执行,需要设置分布式锁

查询资料,这个技术方案比较成熟

1.使用数据库 2.使用redis 3.使用zk

下面介绍下单机redis的使用分布式锁的思路,整体思路较为完善,其他资料较残缺

注意点:

1.设置随机value值 ,防止A释放锁出现释放了其他客户端B在使用的锁的问题

2.设置失效时间 ,防止死锁

3.Redis 2.8 版本中作者加入了 set 指令的扩展参数,使得 setnx 和 expire 指令可以一起执行

set lock:codehole true ex 5 nx

4,释放锁在finally中执行 ,确保锁得到释放

5.删除锁要原子性,原因看附属文第一篇

 //只有当key不存在时,才设置指定key的值,返回true; 否则不设置,返回false,如果key是新key,则设置超时时间(以秒为单位
 boolean setnx = redisUtils.setnx(KEY_PRE + minute, value, 30 * 60);

redisUtils中的方法封装:

 

private JedisCluster jedisCluster;
private Pool<Jedis> jedisPool;

public boolean setnx(String key, String value, int expireSeconds) {
        if (this.isCluster) {
            return "OK".equals(this.jedisCluster.set(key, value, "NX", "EX", (long)expireSeconds));
        } else {
            Jedis jedis = (Jedis)this.jedisPool.getResource();
            Throwable var5 = null;

            boolean var6;
            try {
                var6 = "OK".equals(jedis.set(key, value, "NX", "EX", expireSeconds));
            } catch (Throwable var15) {
                var5 = var15;
                throw var15;
            } finally {
                if (jedis != null) {
                    if (var5 != null) {
                        try {
                            jedis.close();
                        } catch (Throwable var14) {
                            var5.addSuppressed(var14);
                        }
                    } else {
                        jedis.close();
                    }
                }

            }

            return var6;
        }
    }
    }

上述sebnx 与exp在代码里也并不是原子性,稍晚点修改           其实和上面思路相同

我的业务中并没有这样使用.因为如果三台主机时间不同步,前后差十几秒或者几分钟,同样不可避免的本来这一分钟的已经被执行了,锁也释放了,但是另一台机器稍晚点才到达这个时间节点,也会执行,不可避免重复写入的问题,

解决方案:在redis 中设置key-value但不删除,保存半小时,每一分钟生成一个唯一,生成了这一分钟就会只有一个机器去执行

try 
            //每分钟生成一个key,保存30分钟,避免服务器之间时间差异产生重复写入的问题
            //只有当key不存在时,才设置指定key的值,返回true; 否则不设置,返回false,如果key是新key,则设置超时时间(以秒为单位
            if (getLock()) {
                return;
            }


private boolean getLock() {
        int minute = getMinute();
        String value = fetchLockValue();
        boolean setnx = redisUtils.setnx(KEY_PRE + minute, value, 30 * 60);
        log.info("Redis Lock key : " + KEY_PRE + minute + ",value : " + value);
        //获取不到锁,退出
        if (!setnx) {
            return true;
        }
        log.info("获取到锁:{}", KEY_PRE + minute);
        return false;
    }

    /**
     * @return
     */
    private String fetchLockValue() {
        return UUID.randomUUID().toString();
    }

    /**
     * 获取当前分钟
     *
     * @return
     */
    private int getMinute() {
        Date date = DateUtil.getDate();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.MINUTE);
    }

 

 

 

 

 

附 几篇比较好的帖子

在 Java 中利用 redis 实现一个分布式锁服务

http://www.sohu.com/a/214257481_714863

几种分布式锁的实现方式

https://juejin.im/post/5cff593c6fb9a07ec56e6ed4

基于AOP和Redis实现的简易版分布式锁

https://juejin.im/post/5c0ce11351882505d8406abc

80% 人不知道的 Redis 分布式锁的正确实现方式(Java 版)靠谱推荐

https://blog.csdn.net/X5fnncxzq4/article/details/92857006

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值