介面做冪等的方式很多,我們應用使用分布式鎖+插入明細來做冪等。但是發現冪等失效了,最終確認是業務執行尚未結束,還沒有插入明細。但是客戶端第二個訪問就來到了,此時呢,分布式鎖的時間也失效了。
也就是兩個問題:1是業務執行為什麼很慢,這個就有很多種情況暫不考慮。考慮第二種情況,能不能加長分布式鎖的時間。由此仔細看了看redisson的分布式鎖。
先來乙個redisson的分布式鎖測試類
public void testreentrantlock(string lockname)throws exception catch (ioexception e)
lock.lock();
system.out.println(lock.getname());
//此處當為業務邏輯
thread.sleep(3000);
lock.unlock();
}public redissonclient getredisson() throws ioexception
public static void main(string args)throws exception
觀察測試類:有兩個需要關注的入口,getlock 和lock兩個方法。
先來看第乙個:getlock方法屬於redisson類裡的方法。
public rlock getlock(string name)
new redissonlock的建構函式,基本上都是一些屬性的賦值。先關注幾個屬性: id,internallockleasetime 還有entryname,commandexcutor
public redissonlock(commandasyncexecutor commandexecutor, string name)
首先,觀察到id是connectionmanager類的乙個屬性,點進去發現是乙個uuid型別,可以明確知道了,這是乙個uuid隨機值。
然後internallockleasetime 是config類下的乙個lockwatchdogtimeout 。既然是config類的屬性,那麼證明這個屬性可以配置。從字面上理解,這個就是看門狗的乙個超時時間。
還有乙個entryname 是id加上name拼接而成,基本可以確認是往redis存入的key,因為name是我們傳過去的。至於其他屬性,等遇到再分析吧。(往後證明key是我們存入的業務表示,而uuid+執行緒id是要給hash結構的field)
接著看lock方法,最終我們走到了lock方法,引數有leasetime,時間單位,以及乙個布林值,布林值先不管它。
private void lock(long leasetime, timeunit unit, boolean interruptibly) throws interruptedexception
if (ttl >= 0l) catch (interruptedexception var13)
this.getentry(threadid).getlatch().tryacquire(ttl, timeunit.milliseconds);
}} else if (interruptibly) else
}} finally
}}
接下來有兩個內容:先模擬程式走一遍,進入tryacquire方法,如果看過aqs一類的原始碼的話,基本就確認這個是獲取鎖的方法。那麼會根據返回的ttl ,根據字面意思我們知道這是redis裡key的剩餘生存時間。很顯然,當為null的時候表示獲取到鎖,不為null的話,就需要進一步操作,先不管它。接著往方法裡跳,根據tryacquireasync的方法名以及返回型別。我們可以知道redisson是用了非同步的方式去獲取鎖,然後外面又是乙個get方法保證同步獲取返回的ttl結果。
private long tryacquire(long leasetime, timeunit unit, long threadid)
private rfuturetryacquireasync(long leasetime, timeunit unit, long threadid) else
}});
return ttlremainingfuture;
}}
進入方法之後,顯然有兩個分支,乙個是當leasetime為-1的情況,乙個是不為-1的情況。
咱們先看不是 -1的時候,進入trylockinnerasync方法,進入之後,我們主要關注下 eval的表示式
rfuturetrylockinnerasync(long leasetime, timeunit unit, long threadid, redisstrictcommandcommand) );
}
eval表示式主要的意思就是:如果不存在該key,那麼就直接hset 賦值。如果存在該key,並且也是該field的話,直接自增(跑不了,這裡有利於實現重入鎖自增)。所以使用redisson一定要保證redis版本支援eval表示式。
redisson存入redis的鎖結構: key:就是我們自己業務命名的字串。而field呢其實是乙個uuid加上冒號再加上乙個當前執行緒id。這樣可以在分布式情況下保證唯一。這樣基本上redisson如何加鎖的主要流程我們就清楚了。
接下來再返回tryacquireasync方法裡 leasetime不是-1的**塊
private rfuturetryacquireasync(long leasetime, timeunit unit, long threadid) else
}});
return ttlremainingfuture;
}}
我們可以看到當不是-1時,除了使用trylockinnerasync加鎖意外,還有要給rfuture物件的乙個omcomplete方法,很明顯我們要關注下 scheduleexpirationrenewal方法。根據方法名我們就知道這個東西和定時任務有關了。不出意外這就是看門狗程式的內容了。那我們進入scheduleexpirationrenewal方法。
private void scheduleexpirationrenewal(long threadid) else
}
根據**內容,我們知道引數時當前執行緒的id,然後會把執行緒id加入到乙個entry裡面。具體幹啥我也不清楚。最終要的是有乙個renewexpiratino方法,接著再進入該方法。看門狗的面目馬上就出來了。
private void renewexpiration() else
}});}}
}}, this.internallockleasetime / 3l, timeunit.milliseconds);
ee.settimeout(task);
}}
首先分析第一行,會根據entryname從乙個map裡獲取乙個expirationentry物件,然後建立乙個timeout型別的task。然後可以看出來這個task就是乙個任務。每次都會延時 internallockleasetime/3 。具體如何實現延時的,目前還沒找到原始碼。每次更新成功之後,有會重複呼叫自身的renewexpiration方法。這樣就能夠實現不斷續約了。 分布式鎖 redisson
1 匯入依賴 org.redissongroupid redissonartifactid 3.11.1version dependency 2 配置redisson configuration public class redissonconfig spring name cjs redisson...
redis分布式鎖redisson
原文 關於redisson的源 請參考官網 redisson官方講解參考 wiki e7 9b ae e5 bd 95 首先需要引入redisson org.springframework.bootgroupid spring boot starter data redisartifactid de...
Redisson實現分布式鎖
引入包 org.redissongroupid redissonartifactid 3.10.0version dependency redissonconfig類 package com.xiepanpan.locks.lockstest.config import org.redisson.r...