<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 22, comments - 32, trackbacks - 0, articles - 73
      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    分布式鎖實(shí)現(xiàn)-redis,zk

    Posted on 2021-03-24 20:11 為自己代言 閱讀(214) 評(píng)論(0)  編輯  收藏 所屬分類: java/J2EE
    1:分布鎖有好多實(shí)現(xiàn)方式
    •  基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)
          這個(gè)實(shí)現(xiàn)方式比較復(fù)雜,考慮因素比較多,比如:超時(shí),非公平鎖,非重入等會(huì)有各種各樣的問(wèn)題,在解決問(wèn)題的過(guò)程中會(huì)使整個(gè)方案變得越來(lái)越復(fù)雜。操作數(shù)據(jù)庫(kù)需要一定的開(kāi)銷,性能問(wèn)題需要考慮      
    • 基于redis實(shí)現(xiàn)(這個(gè)對(duì)于不太敏感的場(chǎng)景可以使用,由于redis集群和單機(jī),還有客戶端,版本等多方面因素考慮情況比較多)
           性能好。使用緩存實(shí)現(xiàn)分布式鎖的缺點(diǎn) 其數(shù)據(jù)庫(kù)一樣
    • 基于zookeeper實(shí)現(xiàn)(這個(gè)是最終也是最好最可靠的)
           創(chuàng)建臨時(shí)節(jié)點(diǎn),可以解決單機(jī),鎖無(wú)法釋放,非阻塞,不可沖入,非公平的問(wèn)題
     
        總結(jié)
    從理解的難易程度角度(從低到高)

    數(shù)據(jù)庫(kù) > 緩存 > Zookeeper

    從實(shí)現(xiàn)的復(fù)雜性角度(從低到高)

    Zookeeper > 緩存 > 數(shù)據(jù)庫(kù)

    從性能角度(從高到低)

    緩存 > Zookeeper >= 數(shù)據(jù)庫(kù)

    從可靠性角度(從高到低)

    Zookeeper > 緩存 > 數(shù)據(jù)庫(kù)
    下面講基于redis實(shí)現(xiàn)分布鎖代碼:RedisTemplate 客戶端 lettuce


    @Service
    public class RedisDistributedLockUtils {

        @Autowired
        
    private RedisTemplate redisTemplate;

        
    private static final Long RELEASE_SUCCESS = 1L;

        
    private static final long DEFAULT_TIMEOUT = 1000 * 10;
        
    //因?yàn)橐褂胠ua 腳本是因?yàn)?nbsp;redis 執(zhí)行l(wèi)ua腳本是原子操作
        private static final String UNLOCK_LUA= "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

        
    /**
         * 實(shí)時(shí)獲取鎖
         *
         * 嘗試獲取分布式鎖 將redis版本升級(jí)到2.1以上(spring-boot-starter-data-redis 版本 2.X以上),然后使用setIfAbsent 不存在
         * 當(dāng)setIfAbsent成功之后斷開(kāi)連接,下面設(shè)置過(guò)期時(shí)間的代碼 stringRedisTemplate.expire(key,timeout);是無(wú)法執(zhí)行的,這時(shí)候就會(huì)有大量沒(méi)有過(guò)期時(shí)間的數(shù)據(jù)存在數(shù)據(jù)庫(kù)
         * 
    @param lockKey    鎖
         * 
    @param requestId  請(qǐng)求標(biāo)識(shí)
         * 
    @param expireTime 超期時(shí)間
         * 
    @return 是否獲取成功
         
    */
        
    public boolean trySetDistributedLock(String lockKey, String requestId, long expireTime) {
            
    return redisTemplate.opsForValue().setIfAbsent(lockKey, requestId,0 == expireTime ? DEFAULT_TIMEOUT : expireTime, TimeUnit.MILLISECONDS);
        }

        
    /**
         * 以阻塞方式的獲取鎖
         * 
    @param key
         * 
    @param value
         * 
    @param timeout
         * 
    @return
         
    */
        
    public boolean setDistributedLock(String key, String value, long timeout) {
            Boolean lock 
    = false;
            
    long start = System.currentTimeMillis();
            
    while (!lock && (System.currentTimeMillis() - start < timeout)) {
                
    //執(zhí)行set命令
                lock = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);
                
    //不頻繁去獲取鎖
                try {
                    
    if (!lock) {
                        Thread.sleep(
    60);
                    }
                } 
    catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
    return lock;
        }

        
    public boolean releaseLock(String key, String value) {
            
    // 使用Lua腳本:先判斷是否是自己設(shè)置的鎖,再執(zhí)行刪除
            
    // 使用lua腳本刪除redis中匹配value的key,可以避免由于方法執(zhí)行時(shí)間過(guò)長(zhǎng)而redis鎖自動(dòng)過(guò)期失效的時(shí)候誤刪其他線程的鎖
            
    // spring自帶的執(zhí)行腳本方法中,集群模式直接拋出不支持執(zhí)行腳本的異常EvalSha is not supported in cluster environment
            
    // 所以只能拿到原redis的connection來(lái)執(zhí)行腳本

            List
    <String> keys = new ArrayList<>();
            keys.add(key);
            List
    <String> args = new ArrayList<>();
            args.add(value);
            Long result 
    = (Long)redisTemplate.execute(new RedisCallback<Long>() {
                @Override
                
    public Long doInRedis(RedisConnection connection) throws DataAccessException {
                    Object nativeConnection 
    = connection.getNativeConnection();
                    
    // 集群模式和單機(jī)模式雖然執(zhí)行腳本的方法一樣,但是沒(méi)有共同的接口,所以只能分開(kāi)執(zhí)行
                    
    // 集群模式
                    if (nativeConnection instanceof JedisCluster) {
                        
    return (Long)((JedisCluster)nativeConnection).eval(UNLOCK_LUA, keys, args);
                    }
                    
    //客戶端是Jedis時(shí)候(單機(jī)模式)
                    else if (nativeConnection instanceof Jedis) {
                        
    return (Long)((Jedis)nativeConnection).eval(UNLOCK_LUA, keys, args);
                    }
                    
    //這里使用 redisTemplate 中l(wèi)ettuce 客戶端
                    else{
                        DefaultRedisScript
    <Long> redisScript = new DefaultRedisScript<>();
                        redisScript.setScriptText(UNLOCK_LUA);
                        redisScript.setResultType(Long.
    class);
                        
    return (Long)redisTemplate.execute(redisScript, keys, value);
                    }
                }
            });
            
    //返回最終結(jié)果
            return RELEASE_SUCCESS.equals(result);
        }
    }
    基于zookeeper實(shí)現(xiàn)下期補(bǔ)上:


    介紹分布式鎖文章寫(xiě)的比較詳細(xì):
    https://blog.csdn.net/u010963948/article/details/79006572
    主站蜘蛛池模板: 国产性爱在线观看亚洲黄色一级片| 亚洲av综合av一区二区三区| 亚洲一区二区三区91| 老妇激情毛片免费| xxxxx免费视频| 亚洲乱色熟女一区二区三区丝袜| 亚洲爆乳AAA无码专区| 国产偷国产偷亚洲高清日韩| 99热在线观看免费| 亚洲精品午夜无码电影网| 在线观看亚洲免费视频| 好男人视频社区精品免费| 久久夜色精品国产亚洲AV动态图 | 国产一区二区三区免费观看在线| 国产精品免费视频播放器| 亚洲一卡二卡三卡四卡无卡麻豆 | 亚洲毛片av日韩av无码| 亚洲精品久久无码| 亚洲综合国产一区二区三区| 国产乱子伦精品免费视频| 午夜亚洲国产成人不卡在线| 亚洲AV香蕉一区区二区三区| 免费国产高清视频| 永久免费精品影视网站| 免费在线观看视频a| 99久久免费观看| 一个人看的hd免费视频| 亚洲精品国产精品乱码不99| 免费看美女被靠到爽的视频| 亚洲AV日韩综合一区| 久久国产亚洲精品无码| 99久久久精品免费观看国产| 亚洲不卡1卡2卡三卡2021麻豆| 亚洲人成网站在线观看播放| 国产视频精品免费| 毛片基地免费观看| 亚洲熟妇AV一区二区三区浪潮| 永久免费视频v片www| av无码国产在线看免费网站| 免费看黄的成人APP| 免费无码AV一区二区|