Redisson分布式鎖的簡單使用


一:前言

我在實際環境中遇到了這樣一種問題,分布式生成id的問題!因為業務邏輯的問題,我有個生成id的方法,是根據業務標識+id當做唯一的值!
而uuid是遞增生成的,從1開始一直遞增,那么在同一台機器上運行代碼,加上同步方法(synchronized),這個生成id的方法就是ok!

但是因為業務擴展或者說為了安全,項目運行在兩台機器上,此時單個的同步方法(synchronized)就不能防止id的重復了!!!

要解決上面的這個問題,其他有如下解決辦法!
(1):每台機器生產Id的代碼,key+id 可以在前加上機器編號區分,key + id --- >機器唯一編號 + key + id
(2):使用數據庫行鎖(單個數據庫的是時候),在需要插入id的表加上行鎖,防止數據重復導致程序異常!
(3):使用分布式鎖

二:分布式鎖簡介
網上有很多的講解分布式鎖的文章,但是細細分析很多的代碼還是有很多的問題的,如下代碼片段摘自博文:
https://my.oschina.net/91jason/blog/517996?p=1
http://blog.csdn.net/u010359884/article/details/50310387

public void lock(long timeout) {
        long nano = System.nanoTime();
        timeout *= 1000000;
        final Random r = new Random();
        try {
            while ((System.nanoTime() - nano) < timeout) {
                if (redisTemplate.getConnectionFactory().getConnection().setNX(key.getBytes(), LOCKED.getBytes())) {
                    redisTemplate.expire(key, EXPIRE, TimeUnit.SECONDS);
                    locked = true;
                    logger.debug("add RedisLock[" + key + "].");
                    break;
                }
                Thread.sleep(3, r.nextInt(500));
            }
        } catch (Exception e) {
        }
    }
	

博主也說了:如果長時間獲取不到,就會獲取鎖失敗,相當於沒加鎖!
這里還有可能發生其他問題:
(1)並發情況,expire主動釋放鎖的時候,可能釋放的是別人的鎖(不懂請查詢資料)
(2)Redis服務掛掉,鎖失敗,相當於沒加鎖!
注:使用的時候要注意上面問題!!!
還有一種摘自博文:
http://www.cnblogs.com/0201zcr/p/5942748.html
這個博問分析的:

 while (timeout >= 0) {
            long expires = System.currentTimeMillis() + expireMsecs + 1;
            String expiresStr = String.valueOf(expires); //鎖到期時間
            if (this.setNX(lockKey, expiresStr)) {
                // lock acquired
                locked = true;
                return true;
            }

            String currentValueStr = this.get(lockKey); //redis里的時間
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                //判斷是否為空,不為空的情況下,如果被其他線程設置了值,則第二個條件判斷是過不去的
                // lock is expired

                String oldValueStr = this.getSet(lockKey, expiresStr);
                //獲取上一個鎖到期時間,並設置現在的鎖到期時間,
                //只有一個線程才能獲取上一個線上的設置時間,因為jedis.getSet是同步的
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    //防止誤刪(覆蓋,因為key是相同的)了他人的鎖——這里達不到效果,這里值會被覆蓋,但是因為什么相差了很少的時間,所以可以接受

                    //[分布式的情況下]:如過這個時候,多個線程恰好都到了這里,但是只有一個線程的設置值和當前值相同,他才有權利獲取鎖
                    // lock acquired
                    locked = true;
                    return true;
                }
            }
            timeout -= DEFAULT_ACQUIRY_RESOLUTION_MILLIS;

            /*
                延遲100 毫秒,  這里使用隨機時間可能會好一點,可以防止飢餓進程的出現,即,當同時到達多個進程,
                只會有一個進程獲得鎖,其他的都用同樣的頻率進行嘗試,后面有來了一些進行,也以同樣的頻率申請鎖,這將可能導致前面來的鎖得不到滿足.
                使用隨機的等待時間可以一定程度上保證公平性
             */
            Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);

        }
        

這個相比第一個完善了誤刪除key的問題,但是要合理的設置超時時間,否則的話,也會使鎖失效。

三:Redisson分布式鎖的介紹和簡單的使用
Redisson的介紹可以到:https://github.com/redisson/redisson/wiki/1.-概述 這里去了解!
我這里說一下使用時候要注意的問題:

1:文檔里面說明了支持Redis 2.8以上版本,支持Java1.6+以上版本。根據自己的環境選擇合適的版本!

2:2.8.1的redisson 需要使用 netty的jar包, 否則報錯:
Hopper: java.lang.NoClassDefFoundError: io/netty/channel/EventLoopGroup。

3:2.8.1的redisson需要jackson 2.5+版本,否則報錯bjectMapper.addMixIn method not fond。

我寫了一個簡單的例子,自己也做了一下測試,使用的Redis主從+哨兵模式!
demo的目錄結構,具體的源碼我放到github上面,地址:https://github.com/dufyun/kuyu/tree/master/redissondemo

這里寫圖片描述

注:這里一定要先安裝Redis服務,如果沒有安裝Redis服務,請參考這篇:http://blog.csdn.net/u010648555/article/details/69944668
如果Redis服務安裝到服務器上面,請修改代碼中的Redis地址和端口!否則運行不起了!

運行這個類UUidGeneratorLockTest就可以看到效果!測試結果我也在readme.txt進行了總結!

如果在測試和學習的過程中有疑問,可以隨時和我聯系,也可以加左側的群互相探討!謝謝!

四:總結
這個時代,信息爆炸,各種技術博文之間互相復制,真正好的文章還是需要鑒別的!
我也要反思,自己之前也寫過一些博文,也是遇到問題了,去網上搜一些解決方案,很多方案確實是理論上可以解決當前遇到的問題,當時去深究,就會發現很多的不完整性!

多思考,多測試!讓代碼能夠更加高效和安全!

歡迎訪問我的csdn博客,我們一同成長!

"不管做什么,只要堅持下去就會看到不一樣!在路上,不卑不亢!"

博客首頁http://blog.csdn.net/u010648555


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM