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

释放双眼,带上耳机,听听看~!

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

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

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

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

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

注意点:

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

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

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


1
2
1set lock:codehole true ex 5 nx
2

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1 //只有当key不存在时,才设置指定key的值,返回true; 否则不设置,返回false,如果key是新key,则设置超时时间(以秒为单位
2 boolean setnx = redisUtils.setnx(KEY_PRE + minute, value, 30 * 60);
3
4redisUtils中的方法封装:
5
6
7
8private JedisCluster jedisCluster;
9private Pool<Jedis> jedisPool;
10
11public boolean setnx(String key, String value, int expireSeconds) {
12        if (this.isCluster) {
13            return "OK".equals(this.jedisCluster.set(key, value, "NX", "EX", (long)expireSeconds));
14        } else {
15            Jedis jedis = (Jedis)this.jedisPool.getResource();
16            Throwable var5 = null;
17
18            boolean var6;
19            try {
20                var6 = "OK".equals(jedis.set(key, value, "NX", "EX", expireSeconds));
21            } catch (Throwable var15) {
22                var5 = var15;
23                throw var15;
24            } finally {
25                if (jedis != null) {
26                    if (var5 != null) {
27                        try {
28                            jedis.close();
29                        } catch (Throwable var14) {
30                            var5.addSuppressed(var14);
31                        }
32                    } else {
33                        jedis.close();
34                    }
35                }
36
37            }
38
39            return var6;
40        }
41    }
42    }
43

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

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

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
1try
2            //每分钟生成一个key,保存30分钟,避免服务器之间时间差异产生重复写入的问题
3            //只有当key不存在时,才设置指定key的值,返回true; 否则不设置,返回false,如果key是新key,则设置超时时间(以秒为单位
4            if (getLock()) {
5                return;
6            }
7
8
9private boolean getLock() {
10        int minute = getMinute();
11        String value = fetchLockValue();
12        boolean setnx = redisUtils.setnx(KEY_PRE + minute, value, 30 * 60);
13        log.info("Redis Lock key : " + KEY_PRE + minute + ",value : " + value);
14        //获取不到锁,退出
15        if (!setnx) {
16            return true;
17        }
18        log.info("获取到锁:{}", KEY_PRE + minute);
19        return false;
20    }
21
22    /**
23     * @return
24     */
25    private String fetchLockValue() {
26        return UUID.randomUUID().toString();
27    }
28
29    /**
30     * 获取当前分钟
31     *
32     * @return
33     */
34    private int getMinute() {
35        Date date = DateUtil.getDate();
36        Calendar calendar = Calendar.getInstance();
37        calendar.setTime(date);
38        return calendar.get(Calendar.MINUTE);
39    }
40

 

给TA打赏
共{{data.count}}人
人已打赏
安全网络

CDN安全市场到2022年价值76.3亿美元

2018-2-1 18:02:50

安全经验

nginx优化

2021-11-28 16:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索