1. 程式人生 > >Spring Boot Redis 實現分散式鎖,真香!!

Spring Boot Redis 實現分散式鎖,真香!!

之前看很多人手寫分散式鎖,其實 Spring Boot 現在已經做的足夠好了,開箱即用,支援主流的 Redis、Zookeeper 中介軟體,另外還支援 JDBC。 本篇棧長以 Redis 為例(這也是用得最多的方案),教大家如何利用 Spring Boot 整合 Redis 實現快取,如何簡單、快速實現 Redis 分散式鎖。 ## 分散式鎖介紹 Spring Boot 實現 Redis 分散式鎖在 `spring-integration` 這個專案中,參考: > https://docs.spring.io/spring-integration/docs/5.3.1.RELEASE/reference/html/redis.html#redis-lock-registry 首先來看下 `LockRegistry` 鎖註冊介面的所有實現類結構圖: ![](http://img.javastack.cn/20200624140016.png) `DefaultLockRegistry` 就是純單機的可重入鎖,`PassThruLockRegistry` 是一個空實現類,也都沒有什麼利用價值。 Spring Integration 4.0 引入了基於 Redis 的分散式鎖:`RedisLockRegistry`,並且從 5.0 開始實現了 `ExpirableLockRegistry` 介面,用來移除超時且沒有用的鎖。 ## 分散式鎖實戰 #### 新增依賴 上面提到 Spring Boot 實現 Redis 分散式鎖在 `spring-integration` 這個專案中,所以需要這三個依賴: - spring-boot-starter-data-redis - spring-boot-starter-integration - spring-integration-redis ``` ``` Spring Boot 基礎知識就不介紹了,不熟悉的可以關注公眾號Java技術棧,在後臺回覆:boot,可以閱讀我寫的歷史實戰教程。 #### 配置分散式鎖 ``` @Bean(destroyMethod = "destroy") public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) { return new RedisLockRegistry(redisConnectionFactory, "lock"); } ``` #### 使用示例 ``` @GetMapping("/redis/lock") public String lock(@RequestParam("key") String key) { for (int i = 0; i < 10; i++) { new Thread(() -> { redisLockService.lock(key); try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")); redisLockService.unlock(key); } ).start(); } return "OK"; } ``` RedisLockService 是我封裝了的一個 Redis 鎖服務,程式碼有點多,這裡就不貼了,完整的程式碼示例在 Github 上,大家可以 Star 一下: > https://github.com/javastacks/spring-boot-best-practice 測試: > http://localhost:8080/redis/lock?key=yeah 輸出: ``` 2020-06-23 11:15:34 2020-06-23 11:15:37 2020-06-23 11:15:40 2020-06-23 11:15:43 2020-06-23 11:15:46 2020-06-23 11:15:49 2020-06-23 11:15:52 2020-06-23 11:15:55 2020-06-23 11:15:58 2020-06-23 11:16:01 ``` 可以看到每個執行緒需要等上一個執行緒休眠 3 秒後才能獲取到鎖。 ## 原始碼分析 整合完了,會使用了,還得研究下 `RedisLockRegistry` 的原始碼,不然遇到什麼坑還得再踩一篇。 `RedisLockRegistry` 有兩個類構造器: ![](http://img.javastack.cn/20200624145354.png) - **connectionFactory**:Redis 連線工廠 - **registryKey**:鎖字首 - **expireAfter**:失效時間(非必須項,預設60秒) 所以,我們要註冊 `expireAfter` 這個選項,預設 60 秒是否滿足業務需要,如果超過預設的 60 少時間,否則將導致鎖失效。 還有兩個和 `RedisLockRegistry` 相關且很重要的成員變數: ``` private final String clientId = UUID.randomUUID().toString(); private f