概述
提到分布式鎖大家都會(huì)想到如下兩種:
-
基于
Redisson
組件,使用redlock算法實(shí)現(xiàn) -
基于
Apache Curator
,利用Zookeeper的臨時(shí)順序節(jié)點(diǎn)模型實(shí)現(xiàn)
今天我們來說說第三種,使用Spring Integration
實(shí)現(xiàn)。
Spring Integration
在基于Spring的應(yīng)用程序中實(shí)現(xiàn)輕量級(jí)消息傳遞,并支持通過聲明適配器與外部系統(tǒng)集成。
Spring Integration的主要目標(biāo)是提供一個(gè)簡(jiǎn)單的模型來構(gòu)建企業(yè)集成解決方案,同時(shí)保持關(guān)注點(diǎn)的分離,這對(duì)于生成可維護(hù),可測(cè)試的代碼至關(guān)重要。我們熟知的 Spring Cloud Stream的底層就是Spring Integration。
官方地址:https://github.com/spring-projects/spring-integration
Spring Integration提供的全局鎖目前為如下存儲(chǔ)提供了實(shí)現(xiàn):
- Gemfire
- JDBC
- Redis
- Zookeeper
它們使用相同的API抽象,這意味著,不論使用哪種存儲(chǔ),你的編碼體驗(yàn)是一樣的。試想一下你目前是基于zookeeper實(shí)現(xiàn)的分布式鎖,哪天你想換成redis的實(shí)現(xiàn),我們只需要修改相關(guān)依賴和配置就可以了,無需修改代碼。下面是你使用Spring Integration
實(shí)現(xiàn)分布式鎖時(shí)需要關(guān)注的方法:
方法名 | 描述 |
---|---|
lock() |
Acquires the lock. 加鎖,如果已經(jīng)被其他線程鎖住或者當(dāng)前線程不能獲取鎖則阻塞 |
lockInterruptibly() |
Acquires the lock unless the current thread is interrupted. 加鎖,除非當(dāng)前線程被打斷。 |
tryLock() |
Acquires the lock only if it is free at the time of invocation. 嘗試加鎖,如果已經(jīng)有其他鎖鎖住,獲取當(dāng)前線程不能加鎖,則返回false,加鎖失??;加鎖成功則返回true |
tryLock(long time, TimeUnit unit) |
Acquires the lock if it is free within the given waiting time and the current thread has not been interrupted. 嘗試在指定時(shí)間內(nèi)加鎖,如果已經(jīng)有其他鎖鎖住,獲取當(dāng)前線程不能加鎖,則返回false,加鎖失??;加鎖成功則返回true |
unlock() |
Releases the lock. 解鎖 |
實(shí)戰(zhàn)
話不多說,我們看看使用Spring Integration
如何基于redis和zookeeper快速實(shí)現(xiàn)分布式鎖,至于Gemfire 和 Jdbc的實(shí)現(xiàn)大家自行實(shí)踐。
基于Redis實(shí)現(xiàn)
- 引入相關(guān)組件
org.springframework.boot
spring-boot-starter-integration
org.springframework.integration
spring-integration-redis
org.springframework.boot
spring-boot-starter-data-redis
- 在application.yml中添加redis的配置
spring:
redis:
host:172.31.0.149
port:7111
-
建立配置類,注入
RedisLockRegistry
@Configuration
publicclassRedisLockConfiguration{
@Bean
publicRedisLockRegistryredisLockRegistry(RedisConnectionFactoryredisConnectionFactory){
returnnewRedisLockRegistry(redisConnectionFactory,"redis-lock");
}
}
- 編寫測(cè)試代碼
@RestController
@RequestMapping("lock")
@Log4j2
publicclassDistributedLockController{
@Autowired
privateRedisLockRegistryredisLockRegistry;
@GetMapping("/redis")
publicvoidtest1(){
Locklock=redisLockRegistry.obtain("redis");
try{
//嘗試在指定時(shí)間內(nèi)加鎖,如果已經(jīng)有其他鎖鎖住,獲取當(dāng)前線程不能加鎖,則返回false,加鎖失敗;加鎖成功則返回true
if(lock.tryLock(3,TimeUnit.SECONDS)){
log.info("lockisready");
TimeUnit.SECONDS.sleep(5);
}
}catch(InterruptedExceptione){
log.error("obtainlockerror",e);
}finally{
lock.unlock();
}
}
}
-
測(cè)試
啟動(dòng)多個(gè)實(shí)例,分別訪問/lock/redis
端點(diǎn),一個(gè)正常秩序業(yè)務(wù)邏輯,另外一個(gè)實(shí)例訪問出現(xiàn)如下錯(cuò)誤說明第二個(gè)實(shí)例沒有拿到鎖,證明了分布式鎖的存在。
注意,如果使用新版Springboot進(jìn)行集成時(shí)需要使用Redis4版本,否則會(huì)出現(xiàn)下面的異常告警,主要是
unlock()
釋放鎖時(shí)使用了UNLINK命令,這個(gè)需要Redis4版本才能支持。
2020-05-141124,781WARNRedisLockRegistry:339-TheUNLINKcommandhasfailed(notsupportedontheRedisserver?);fallingbacktotheregularDELETEcommand
org.springframework.data.redis.RedisSystemException:Errorinexecution;nestedexceptionisio.lettuce.core.RedisCommandExecutionException:ERRunknowncommand'UNLINK'
基于Zookeeper實(shí)現(xiàn)
- 引入組件
org.springframework.boot
spring-boot-starter-integration
org.springframework.integration
spring-integration-zookeeper
- 在application.yml中添加zookeeper的配置
zookeeper:
host:172.31.0.43:2181
-
建立配置類,注入
ZookeeperLockRegistry
@Configuration
publicclassZookeeperLockConfiguration{
@Value("${zookeeper.host}")
privateStringzkUrl;
@Bean
publicCuratorFrameworkFactoryBeancuratorFrameworkFactoryBean(){
returnnewCuratorFrameworkFactoryBean(zkUrl);
}
@Bean
publicZookeeperLockRegistryzookeeperLockRegistry(CuratorFrameworkcuratorFramework){
returnnewZookeeperLockRegistry(curatorFramework,"/zookeeper-lock");
}
}
- 編寫測(cè)試代碼
@RestController
@RequestMapping("lock")
@Log4j2
publicclassDistributedLockController{
@Autowired
privateZookeeperLockRegistryzookeeperLockRegistry;
@GetMapping("/zookeeper")
publicvoidtest2(){
Locklock=zookeeperLockRegistry.obtain("zookeeper");
try{
//嘗試在指定時(shí)間內(nèi)加鎖,如果已經(jīng)有其他鎖鎖住,獲取當(dāng)前線程不能加鎖,則返回false,加鎖失?。患渔i成功則返回true
if(lock.tryLock(3,TimeUnit.SECONDS)){
log.info("lockisready");
TimeUnit.SECONDS.sleep(5);
}
}catch(InterruptedExceptione){
log.error("obtainlockerror",e);
}finally{
lock.unlock();
}
}
}
測(cè)試啟動(dòng)多個(gè)實(shí)例,分別訪問
/lock/zookeeper
端點(diǎn),一個(gè)正常秩序業(yè)務(wù)邏輯,另外一個(gè)實(shí)例訪問出現(xiàn)如下錯(cuò)誤
原文標(biāo)題:這樣實(shí)現(xiàn)分布式鎖,才叫優(yōu)雅!
文章出處:【微信公眾號(hào):數(shù)據(jù)分析與開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
-
分布式
+關(guān)注
關(guān)注
1文章
998瀏覽量
75430 -
spring
+關(guān)注
關(guān)注
0文章
340瀏覽量
15093
原文標(biāo)題:這樣實(shí)現(xiàn)分布式鎖,才叫優(yōu)雅!
文章出處:【微信號(hào):DBDevs,微信公眾號(hào):數(shù)據(jù)分析與開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Redis 分布式鎖的正確實(shí)現(xiàn)方式
分布式鎖的基本原理和案例實(shí)現(xiàn)

分布式鎖的設(shè)計(jì)與實(shí)現(xiàn)
深入理解redis分布式鎖

Redis實(shí)現(xiàn)分布式鎖的幾種方案
什么是分布式鎖 Redis的五種分布式鎖方案

tldb提供分布式鎖使用方法

評(píng)論