做一個積極的人
編碼、改bug、提升自己
我有一個樂園,面向程式設計,春暖花開!
=================================================
對人工智慧感興趣的夥伴,分享一個我朋友的人工智慧教程。零基礎!通俗易懂!風趣幽默!大家可以看看是否對自己有幫助,點選這裡檢視教程。
=================================================
一:前言
我在實際環境中遇到了這樣一種問題,分散式生成id的問題!因為業務邏輯的問題,我有個生成id的方法,是根據業務標識+id 當做唯一的值! 而uuid是遞增生成的,從1開始一直遞增,那麼在同一臺機器上執行程式碼,加上同步方法(synchronized),這個生成id的方法就是ok!
但是因為業務擴充套件或者說為了安全,專案執行在兩臺機器上,此時單個的同步方法(synchronized或者Lock)就不能防止id的重複了!!!
要解決上面的這個問題,其他有如下解決辦法! (1):每臺機器生產Id的程式碼,key+id 可以在前加上機器編號區分,key + id --- >機器唯一編號 + key + id (2):使用資料庫行鎖(單個資料庫的是時候,如何是分散式資料庫也會出現問題),在需要插入id的表加上行鎖,防止資料重複導致程式異常! (3):使用分散式鎖
二:分散式鎖簡介
網上有很多的講解分散式鎖的文章,但是細細分析很多的程式碼還是有很多的問題的,如下程式碼片段摘自博文:
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服務掛掉,鎖失敗,相當於沒加鎖!最好使用主從+哨兵提高 高可用
注:使用的時候要注意上面問題!!!
還有一種摘自博文: www.cnblogs.com/0201zcr/p/5… 這個博問分析的:
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的介紹可以到:github.com/redisson/re… 這裡去了解!
我這裡說一下使用時候要注意的問題:
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上面,地址:github.com/dufyun/lear…
注:這裡一定要先安裝Redis服務,如果沒有安裝Redis服務,請參考這篇:blog.csdn.net/u010648555/… 如果Redis服務安裝到伺服器上面,請修改程式碼中的Redis地址和埠!否則執行不起了!
執行這個類UUidGeneratorLockTest就可以看到效果!測試結果我也在readme.txt進行了總結!
如果在測試和學習的過程中有疑問,可以隨時和我聯絡,也可以加左側的群或者QQ互相探討!謝謝!
四:總結
這個時代,資訊爆炸,各種技術博文之間互相參考,真正的問題可能沒有暴露出來,真正好的文章還是需要鑑別的!(我自己也會參考一些文章,但是我都會進行一些自己驗證,一個是為準確性,一個加深自己的對知識的認識)
我也要反思,自己之前也寫過一些博文,也是遇到問題了,去網上搜一些解決方案,很多方案確實是理論上可以解決當前遇到的問題,當時去深究,就會發現很多的不完整性!
多思考,多測試!讓程式碼能夠更加高效、健壯和安全!
五:參考博文
Redis實現分散式鎖全域性鎖—Redis客戶端Redisson中分散式鎖RLock實現
分散式鎖的幾種實現方式
謝謝你的閱讀,如果您覺得這篇博文對你有幫助,請點贊或者喜歡,讓更多的人看到!祝你每天開心愉快!
不管做什麼,只要堅持下去就會看到不一樣!在路上,不卑不亢!
願你我在人生的路上能都變成最好的自己,能夠成為一個獨擋一面的人
© 每天都在變得更好的阿飛雲