zoukankan      html  css  js  c++  java
  • 分布式锁

    分布式锁的使用场景是什么呢?

    之前红包需求的时候,有一个场景要用到分布式锁。同一个openid,最多只能抢到一个子红包。

    1、先判断这个openid在不在该红包对应的已抢openid hash中

    String hget(String key, String field);

    2、如果在,就返回。如果不在,则加分布式锁,保证同一个openid,最多只有一个请求能抢到红包。这里是怕有人用自己的openid刷接口,从而导致他自己抢了很多个红包。

    SET key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]

    只有这个命令,能一边设不存在时才set,一边设置过期时间。

    SETNX key value,只能在不存在时才set,不能设置过期时间。

    key是什么?key是openid+redPacketId。

    value是什么?value是一个uuid即可,后面还要依靠这个uuid去解锁呢。对的,分布式锁加上之后,需要我们在业务完成之后,手动解锁。即del key,但是不能乱删,不能是没有获取到锁的客户端把锁解了,只能是获取到锁的客户端才能解锁。这就是uuid的好处,每个请求生成的uuid是不一样的,只有uuid是key的值时,才能解锁。

    伪代码如下:

        public Object getChildRedPacket() {
            JedisCommands jedisCommands = null;
            Jedis jedis = null;
            JedisCluster jedisCluster = null;
    
            String openid = "d";
            String redPacketId = "ss";
            String lockKey = openid + redPacketId;
            SetParams setParams = new SetParams();
            setParams.nx();
            setParams.ex(60 * 60);
    
            String uuid = UUID.randomUUID().toString();
            String money = jedisCommands.hget(redPacketId, openid);
            if (StringUtils.isNotBlank(money)) {
                return ImmutableMap.of("money", money);
            } else {
                try {
                    // 获得锁
                    if ("ok".equalsIgnoreCase(jedisCommands.set(lockKey, uuid, setParams))) {
                        money = jedisCommands.lpop(redPacketId);
                        if (StringUtils.isNotBlank(money)) {
                            jedisCommands.hset(redPacketId, openid, money);
                            return ImmutableMap.of("money", money);
                        } else {
                            return ImmutableMap.of("msg", "已抢完");
                        }
                    }
                } finally {
                    // 释放锁
                    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return end";
    //                jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(uuid));
                    jedisCluster.eval(script, Collections.singletonList(lockKey), Collections.singletonList(uuid));
                }
            }
            return null;
        }

    Jedis常规的set、get、hset、hget方法都是从JedisCommands接口继承的,JedisCluster常规的set、get、hset、hget方法都是从JedisClusterCommands接口继承的。

    Jedis的eval方法是从ScriptingCommands接口继承的,JedisCluster的eval方法是从JedisClusterScriptingCommands接口继承的。

    注意以上代码,在lua脚本中用了uuid的值,这就是约束了只有获得锁的请求能解锁,没有获得锁的请求不能解锁,且释放锁代码放在finally块中。

    加锁时的过期时间怎么定呢?只要是比业务代码执行所需时间长就好了,长点无所谓。为啥无所谓呢?因为锁是一个openid对应一个redPacketId,锁不释放不会影响另一个openid抢这个红包,也不会影响这个openid抢另一个红包,只会影响这个openid再一次抢这个红包。参考https://mp.weixin.qq.com/s/ceRwZdwOgmzRGqF9HZztKg

  • 相关阅读:
    BZOJ 1093: [ZJOI2007]最大半连通子图
    BZOJ 1406: [AHOI2007]密码箱
    BZOJ 1073: [SCOI2007]kshort
    BZOJ 1857: [Scoi2010]传送带
    AC日记——天天爱跑步 洛谷 P1600
    AC日记——[Sdoi2010]粟粟的书架 bzoj 1926
    AC日记——The Shortest Path in Nya Graph hdu 4725
    AC日记——文化之旅 洛谷 P1078
    AC日记——【模板】分块/带修改莫队(数颜色) 洛谷 P1903
    AC日记——大爷的字符串题 洛谷 P3709
  • 原文地址:https://www.cnblogs.com/koushr/p/11048506.html
Copyright © 2011-2022 走看看