Redisson 分布式鎖的最佳實踐
- 第一、添加依賴
- 第二、添加redisson配置類
- 第三、添加測試類
- 測試結果
- 擴展知識
- redisson鎖中lock方法和tryLock方法有什么區別
- 鎖續約
- 注意事項
引言
在現代分布式系統中,處理并發問題是至關重要的。分布式鎖是解決這類問題的關鍵工具之一。本文將介紹如何使用 Redisson,一個強大的 Redis 客戶端庫,來實現高性能、高可用性的分布式鎖。
基本概念
分布式鎖是多個進程或線程之間協調操作的一種機制。它通常包括三個基本操作:加鎖、持有鎖、釋放鎖。Redisson 提供了簡單而強大的接口來管理這些操作。
Redisson 分布式鎖的使用方法
第一、添加依賴
<!-- redisson分布式鎖 -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.15.5</version>
</dependency>
第二、添加redisson配置類
package com.kingmouse.lock;import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** Created with IntelliJ IDEA** @author kingMouse* @date 2023/11/7 09:50* Description: redisson配置類 單機和集群自選*/@Configuration
public class RedissonConfig {/*** 單機模式* @return RedissonClient*/@Bean(destroyMethod = "shutdown")public RedissonClient redissonSingle() {// 1.創建配置Config config = new Config();// 單機模式config.useSingleServer()// 設置地址.setAddress("redis://127.0.0.1:6379")// 設置連接數.setConnectionPoolSize(10);return Redisson.create(config);}/*** 單機模式* @return RedissonClient*/// @Bean(destroyMethod = "shutdown")
// public RedissonClient redissonCluster() {
// // 1.創建配置
// Config config = new Config();
// // 集群模式-帶密碼
// config.useClusterServers()
// .addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001")
// .setPassword("12356")
// // 設置master節點最小連接數
// .setMasterConnectionMinimumIdleSize(10)
// // 設置slave節點最小連接數
// .setSlaveConnectionMinimumIdleSize(10);
// return Redisson.create(config);
// }
第三、添加測試類
package com.kingmouse.lock;import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.util.concurrent.TimeUnit;/*** Created with IntelliJ IDEA** @author kingMouse* @date 2023/11/5 09:54* Description:*/@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedissonLockTest {@Autowiredprivate RedissonClient redissonClient;/*** 阻塞式*/@Testpublic void testRedissonBlock() {// 設置鎖的名字,同一個名字為同一把鎖,不同名字之間互不干擾String lockKey = "kingmouse-001";// 獲取鎖對象RLock lock = redissonClient.getLock(lockKey);// 加鎖,嘗試獲取鎖,5秒后獲取不到則直接放棄lock.lock(5, TimeUnit.SECONDS);// 實際業務處理try {System.out.println("阻塞式--加鎖成功,線程 ID:" + Thread.currentThread().getId());} catch (Exception e) {log.error("業務處理異常:{}", e.getMessage());} finally {// 解鎖lock.unlock();System.out.println("阻塞式--解鎖成功,線程 ID:" + Thread.currentThread().getId());}}@Testpublic void testRedissonNonBlock() throws Exception {// 設置鎖的名字,同一個名字為同一把鎖,不同名字之間互不干擾String lockKey = "kingmouse-002";// 獲取鎖對象RLock lock = redissonClient.getLock(lockKey);// 嘗試獲取鎖,最長等待5秒,持有鎖最長30秒boolean isLock = lock.tryLock(5, 30, TimeUnit.SECONDS);if (isLock) {// 實際業務處理try {System.out.println("非阻塞式--獲取鎖成功,線程 ID:" + Thread.currentThread().getId());} catch (Exception e) {log.error("業務處理異常:{}", e.getMessage());} finally {// 解鎖lock.unlock();System.out.println("非阻塞式--解鎖成功,線程 ID:" + Thread.currentThread().getId());}}}}
測試結果
阻塞式--加鎖成功,線程 ID:1
阻塞式--解鎖成功,線程 ID:1
非阻塞式--獲取鎖成功,線程 ID:1
非阻塞式--解鎖成功,線程 ID:1
擴展知識
redisson鎖中lock方法和tryLock方法有什么區別
在 Redisson 中,lock 方法和 tryLock 方法都是用來獲取分布式鎖的,但它們在獲取鎖的方式和行為上有一些區別:
在 Redisson 中,lock
方法和 tryLock
方法都是用來獲取分布式鎖的,但它們在獲取鎖的方式和行為上有一些區別:
-
lock
方法:lock
方法是阻塞的,即如果獲取鎖失敗,調用該方法的線程會被阻塞,直到獲取到鎖為止。- 如果在獲取鎖時發生網絡分區或其他異常,會一直重試獲取鎖,直到獲取成功或者達到設置的超時時間為止。
- 使用
lock
方法時,如果獲取鎖成功后,需要手動調用unlock
方法來釋放鎖。
例如:
RLock lock = redisson.getLock("myLock");lock.lock();try {// 執行業務邏輯} finally {lock.unlock();}
-
tryLock
方法:tryLock
方法是非阻塞的,即如果獲取鎖失敗,方法會立即返回,不會阻塞當前線程。tryLock
方法可以設置超時時間,在指定的超時時間內嘗試獲取鎖,如果超時仍未獲取到鎖,則返回false
。- 使用
tryLock
方法時,獲取鎖成功后不需要手動釋放鎖,因為鎖會在超時時間后自動釋放。(建議使用完自動釋放鎖)
例如:
RLock lock = redisson.getLock("myLock");boolean isLocked = lock.tryLock(10, TimeUnit.SECONDS);if (isLocked) {try {// 執行業務邏輯} finally {lock.unlock();}} else {// 獲取鎖失敗的處理邏輯}
總結來說,lock
方法是阻塞的,會一直嘗試獲取鎖直到成功,而 tryLock
方法是非阻塞的,在指定的超時時間內嘗試獲取鎖,如果超時則返回失敗。選擇使用哪種方法取決于你的業務需求,以及是否能夠容忍等待獲取鎖的阻塞時間。
鎖續約
使用看門狗(Watchdog)可以實現鎖的自動續約:
String permitId = lock.acquire(10, TimeUnit.SECONDS);
RScheduledExecutorService executorService = redisson.getExecutorService("myExecutorService");
executorService.schedulePermitExpiration(permitId, 5, TimeUnit.SECONDS);
在這個例子中,我們使用 schedulePermitExpiration
方法每隔5秒續約一次鎖的持有時間。
注意事項
在使用分布式鎖時,需要注意以下幾點:
-
合理設置鎖的持有時間:不要將鎖的持有時間設置得過長,以免影響系統的并發性能。根據實際業務需求,選擇一個合適的持有時間。
-
使用看門狗進行續約:在需要鎖的持有時間較長的情況下,使用看門狗可以確保鎖不會在持有時間內過期,避免了手動續約的繁瑣操作。
-
異常處理:在加鎖、續約、釋放鎖的過程中,需要考慮可能出現的異常情況,并進行適當的處理,以確保鎖能夠被正確釋放,避免死鎖等問題。
實際應用場景
在電商系統中,分布式鎖可以用來避免商品超賣問題。在任務調度系統中,可以確保同一個任務在同一時刻只被一個節點執行,避免任務重復執行等問題。
總結
Redisson 提供了簡單而強大的分布式鎖解決方案,通過合理設置鎖的持有時間和使用看門狗進行續約,可以確保系統在高并發環境下的穩定性和可靠性。在實際應用中,開發人員可以根據業務需求選擇合適的鎖策略,并進行必要的異常處理,以實現分布式系統的高效運行。