提到分佈式鎖你們都會想到以下兩種:java
Redisson
組件,使用redlock算法實現Apache Curator
,利用Zookeeper的臨時順序節點模型實現今天咱們來講說第三種,使用 Spring Integration
實現,也是我我的比較推薦的一種。git
Spring Integration
在基於Spring的應用程序中實現輕量級消息傳遞,並支持經過聲明適配器與外部系統集成。 Spring Integration的主要目標是提供一個簡單的模型來構建企業集成解決方案,同時保持關注點的分離,這對於生成可維護,可測試的代碼相當重要。咱們熟知的 Spring Cloud Stream的底層就是Spring Integration。github
官方地址:github.com/spring-proj…redis
Spring Integration提供的全局鎖目前爲以下存儲提供了實現:算法
它們使用相同的API抽象,這意味着,不論使用哪一種存儲,你的編碼體驗是同樣的。試想一下你目前是基於zookeeper實現的分佈式鎖,哪天你想換成redis的實現,咱們只須要修改相關依賴和配置就能夠了,無需修改代碼。下面是你使用 Spring Integration
實現分佈式鎖時須要關注的方法:spring
方法名 | 描述 |
---|---|
lock() |
Acquires the lock. 加鎖,若是已經被其餘線程鎖住或者當前線程不能獲取鎖則阻塞 |
lockInterruptibly() |
Acquires the lock unless the current thread is interrupted. 加鎖,除非當前線程被打斷。 |
tryLock() |
Acquires the lock only if it is free at the time of invocation. 嘗試加鎖,若是已經有其餘鎖鎖住,獲取當前線程不能加鎖,則返回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. 嘗試在指定時間內加鎖,若是已經有其餘鎖鎖住,獲取當前線程不能加鎖,則返回false,加鎖失敗;加鎖成功則返回true |
unlock() |
Releases the lock. 解鎖 |
話很少說,咱們看看使用 Spring Integration
如何基於redis和zookeeper快速實現分佈式鎖,至於Gemfire 和 Jdbc的實現你們自行實踐。bash
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
複製代碼
spring:
redis:
host: 172.31.0.149
port: 7111
複製代碼
RedisLockRegistry
@Configuration
public class RedisLockConfiguration {
@Bean
public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory){
return new RedisLockRegistry(redisConnectionFactory, "redis-lock");
}
}
複製代碼
@RestController
@RequestMapping("lock")
@Log4j2
public class DistributedLockController {
@Autowired
private RedisLockRegistry redisLockRegistry;
@GetMapping("/redis")
public void test1() {
Lock lock = redisLockRegistry.obtain("redis");
try{
//嘗試在指定時間內加鎖,若是已經有其餘鎖鎖住,獲取當前線程不能加鎖,則返回false,加鎖失敗;加鎖成功則返回true
if(lock.tryLock(3, TimeUnit.SECONDS)){
log.info("lock is ready");
TimeUnit.SECONDS.sleep(5);
}
} catch (InterruptedException e) {
log.error("obtain lock error",e);
} finally {
lock.unlock();
}
}
}
複製代碼
/lock/redis
端點,一個正常秩序業務邏輯,另一個實例訪問出現以下錯誤
注意,若是使用新版Springboot進行集成時須要使用Redis4版本,不然會出現下面的異常告警,主要是
unlock()
釋放鎖時使用了UNLINK命令,這個須要Redis4版本才能支持。app
2020-05-14 11:30:24,781 WARN RedisLockRegistry:339 - The UNLINK command has failed (not supported on the Redis server?); falling back to the regular DELETE command
org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'UNLINK'
複製代碼
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-zookeeper</artifactId>
</dependency>
複製代碼
zookeeper:
host: 172.31.0.43:2181
複製代碼
ZookeeperLockRegistry
@Configuration
public class ZookeeperLockConfiguration {
@Value("${zookeeper.host}")
private String zkUrl;
@Bean
public CuratorFrameworkFactoryBean curatorFrameworkFactoryBean(){
return new CuratorFrameworkFactoryBean(zkUrl);
}
@Bean
public ZookeeperLockRegistry zookeeperLockRegistry(CuratorFramework curatorFramework){
return new ZookeeperLockRegistry(curatorFramework,"/zookeeper-lock");
}
}
複製代碼
@RestController
@RequestMapping("lock")
@Log4j2
public class DistributedLockController {
@Autowired
private ZookeeperLockRegistry zookeeperLockRegistry;
@GetMapping("/zookeeper")
public void test2() {
Lock lock = zookeeperLockRegistry.obtain("zookeeper");
try{
//嘗試在指定時間內加鎖,若是已經有其餘鎖鎖住,獲取當前線程不能加鎖,則返回false,加鎖失敗;加鎖成功則返回true
if(lock.tryLock(3, TimeUnit.SECONDS)){
log.info("lock is ready");
TimeUnit.SECONDS.sleep(5);
}
} catch (InterruptedException e) {
log.error("obtain lock error",e);
} finally {
lock.unlock();
}
}
}
複製代碼
/lock/zookeeper
端點,一個正常秩序業務邏輯,另一個實例訪問出現以下錯誤