redislock-spring-boot-starter
Github地址:https://github.com/peikaiqiang/redislock-spring-boot-starter
簡介
redislock-spring-boot-starter 是基于redis實現(xiàn)的分布式注解鎖驱闷,原理很簡單者娱,就是在redis里面setnx一個key,如果這個key不存在,則加鎖成功役衡,多線程環(huán)境下再次進入這個方法就會加鎖失敗咙轩。項目以spring-boot-starter的形式發(fā)布眶明,引入方便惩坑。既可以以加注解的方式實現(xiàn)方法如入口層面的加鎖,也可以注入相應(yīng)的加鎖類朝扼,以嵌入代碼的形式加鎖赃阀。作者更推薦使用注解的形式,注解切面的order是-1擎颖,也就是在spring事務(wù)切面的外層榛斯。開箱即用,只需要配置spring redis即可搂捧,該項目已經(jīng)在作者的兩家公司應(yīng)用了驮俗,覺得不錯記得點贊!
關(guān)于timeout的設(shè)置
小于50毫秒則會直接休眠timeout的時間允跑。大于50時王凑,則第一次休眠50毫秒,此后休眠時間逐次翻倍吮蛹,最后一次休眠的時間為剩余待休眠時間荤崇,即保證總休眠時間等于timeout的值。
關(guān)于key到期后的安全問題
假如第一個線程執(zhí)行方法超時潮针,redis key到期后,第二個線程可以進入重新加鎖倚喂。第一個超時方法執(zhí)行完后是不會把第二個線程加的鎖釋放掉的每篷,程序已經(jīng)做了處理。怎么處理第二個線程進入的問題端圈,框架沒有處理焦读,依賴使用者做好業(yè)務(wù)冪等處理。全局超時時間是90秒舱权,可以根據(jù)業(yè)務(wù)自行調(diào)整矗晃,也可以在單個注解上設(shè)置單次的超時時間。另外相比手動加鎖宴倍,不會有遺漏釋放鎖的風(fēng)險张症。
命名更新
項目名稱之前是RedisLock仓技,考慮到用spring-boot-starter比較方便,所以使用官方推薦的命名方法俗他。原項目1.0.0版本的jar包maven中央庫脖捻,強烈建議更換新項目的maven dependency。
配置
- SpringBootApplication 引入注解 @EnableRedisLock
@EnableRedisLock
@SpringBootApplication
public class Application() {
}
- pom 引入redis
<dependency>
<groupId>com.warmmen</groupId>
<artifactId>redislock-spring-boot-starter</artifactId>
</dependency>
- redis 配置兆衅,具體參考springboot官方文檔
spring:
redis:
host: localhost
port: 6379
password:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 10000
配置文件屬性
redis-lock.timeout 全局默認(rèn)阻塞獲取鎖的超時時間地沮,單位毫秒,默認(rèn)500羡亩。
redis-lock.prefix 所有加鎖的key的前綴摩疑,默認(rèn)為 "REDIS_LOCK_" 。
redis-lock.defaultError 加鎖失敗拋出的異常信息畏铆,默認(rèn) "The lock has been occupied.re"未荒。
redis-lock.liveTime 鎖持續(xù)的時間,單位毫秒及志,默認(rèn)90000片排。鎖失效后,其他線程可以進入該鎖速侈,設(shè)置需謹(jǐn)慎率寡。
注解方式
- @RedisLock 方法注解
方法加鎖的標(biāo)志,方法必須實現(xiàn)jdk接口倚搬,原理同事務(wù)冶共。
value,redis中存的key值每界。
suffix 后綴捅僵,redis中存的key值附加的后綴。
blocked 是否阻塞獲取鎖眨层,默認(rèn)否庙楚,非阻塞獲取會立馬返回結(jié)果。如果為true趴樱,則在第一次獲取鎖失敗后馒闷,會多次休眠嘗試重新獲取鎖。
error 獲取鎖失敗時拋出的異常信息叁征,默認(rèn)拋出的信息: "The lock has been occupied."
timeout 阻塞獲取鎖的超時時間纳账,單位毫秒,如不設(shè)值捺疼,則取全局的默認(rèn)超時時間疏虫,默認(rèn)500毫秒。也可以在 application.yml配置文件中修改redis-lock.timeout的值。如有設(shè)值卧秘,則該注解修飾的方法以timeout值作為超時時間呢袱。
- @RedisLockRequest 方法注解
獲取Http請求參數(shù)的值
value 參數(shù)的名稱
- @RedisLockParam 參數(shù)注解
獲取參數(shù)的值
value
當(dāng)value為空時:如果參數(shù)實現(xiàn)了RedisLockable接口,則獲取RedisLockable接口的key()斯议,否則返回參數(shù)的toString()作為redis鎖的key产捞。
當(dāng)value非空時:以SpEL表達(dá)式獲取參數(shù)中的字段值,假如參數(shù)是Map類型哼御,value=“name”, 則返回Map中key為name的值坯临;如果參數(shù)是object,則返回name字段的toString(), 假如name字段還是個object恋昼,也可以用 “name.filed” 取出name中field字段的值看靠,以此類推(Map 也是相同原則)。
- RedisLockable 接口
自定義獲取參數(shù)的鎖Key
java 代碼方式
@Autowired
DefaultRedisLock defaultRedisLock;
void doSomething() {
try {
boolean success = defaultRedisLock.tryLock("DO_SOMETHING", 100);
if (success) {
// do something
}
} finally {
// unlock in finally
defaultRedisLock.unlock("DO_SOMETHING");
}
}
tryLock 和 lock 區(qū)別液肌?
lock加鎖失敗會拋出RedisLockException異常挟炬,tryLock不會。
tryLock 可以加阻塞時間嗦哆。
鎖key的拼接順序
- redis-lock.prefix 的值谤祖。
- @RedisLockRequest 配置的請求參數(shù)中獲取,配置多個時老速,按配置的順序獲取粥喜。
- @RedisLockParam中獲取,多個時以下劃線(_)分隔橘券。
- suffix 的值额湘。
如果第2,3步取值都為空旁舰,則會拋出異常锋华。
例子
@RedisLock(vakue = "Test_Method", suffix = "Suf", blocked = true, timeout = 1000, error = "加鎖失敗,請重試箭窜!")
@RedisLockRequest({"arg1", "arg2"})
public void test(@RedisLockParam param1,
@RedisLockParam("name") Map<String, Object> param2)
如果獲取鎖成功毯焕,則key的值為:REDIS_LOCK_Test_Method_arg1_arg2_param1_name_Suf,鎖的持續(xù)時間時90秒
如果獲取鎖失敗绽快,拋出異常:"加鎖失敗芥丧,請重試!"