分佈式鎖-Redisson

 前段時間在kubernetes中做多副本部署時,發現有些服務中存在定時任務,這樣一來不同副本都會執行這些定時任務,導致任務重複執行,然後找了一下決定使用 Redisson 來做這個,其實現在來看分佈式時鎖相對容易多了,就是在同一時刻去獲取一把鎖,拿到這把鎖就可以進行操作,拿不到就不能操作,這裏 Redisson 是利用在 Redis 存放一個 Key (其實是setNX)來做這個工作。當然定時任務僅僅分佈式鎖運用的一種場景。現在 Redisson 的使用也簡單了很多,官方直接提供了 redisson-spring-boot-starter,和SpringBoot整合也很方便,下面是集成示例:

1. 引入依賴

注:redisson-spring-data 需要根據項目中使用的 SpringBoot 的版本進行重選,3.11.5 版本默認引用的是 redisson-spring-data-21(即適用於SpringBoot版本爲2.1.x的使用),但項目中我的SpringBoot是2.0.7就不行,需要選用redisson-spring-data-20,不做這一步啓動會出錯的,具體的pom依賴爲:

  <dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson-spring-data-20</artifactId>
   <version>3.11.5</version>
  </dependency>

  <dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson-spring-boot-starter</artifactId>
   <version>3.11.5</version>
   <exclusions>
    <exclusion>
     <artifactId>redisson-spring-data-21</artifactId>
     <groupId>org.redisson</groupId>
    </exclusion>
   </exclusions>
  </dependency>

2. 配置

 配置方面如果只想試試,直接在application.properties文件中使用Spring-data-redis的基本配置即可:

spring.redis.database=
spring.redis.host=
spring.redis.port=
spring.redis.password=

當然更全面的配置可以通過spring.redis.redisson.config=classpath:redisson.yaml的方式以外部配置的方式植入,示例文件如下:

## 1. 雲託管方式(注意名字)
#replicatedServersConfig:
#  # 連接空閒超時,默認值10000,如果當前連接池裏的連接數量超過了最小空閒連接數,同時有連接空閒時間超過了該數值,那這些連接將會自動被關閉,並從連接池裏去掉
#  idleConnectionTimeout: 10000
#  pingTimeout: 1000
#  # 連接超時,默認10000,同任何節點建立連接時的等待超時
#  connectTimeout: 10000
#  # 命令等待超時
#  timeout: 3000
#  # 命令失敗重試次數,如果命令嘗試次數超過該值仍然不能發送至某個指定的節點時,將拋出錯誤。如果嘗試在此限制之內發送成功,則開始啓用 timeout(命令等待超時) 計時
#  retryAttempts: 3
#  # 命令重試發送時間間隔
#  retryInterval: 1500
#  # 當與某個節點的連接斷開時,等待與其重新建立連接的時間間隔
#  reconnectionTimeout: 3000
#  # 執行失敗最大次數,在某個節點執行相同或不同命令時,連續失敗次數大於該數值時,該節點將被從可用節點列表裏清除,直到 reconnectionTimeout 超時以後再次嘗試
#  failedAttempts: 3
#  password: 123456
#  # 單個連接最大訂閱數量
#  subscriptionsPerConnection: 5
#  # 客戶端名稱,在Redis節點裏顯示的客戶端名稱
#  clientName: null
#  # 負載均衡算法類的選擇,多Redis服務節點的環境裏有3種可選:
#  # org.redisson.connection.balancer.WeightedRoundRobinBalancer - 權重輪詢調度算法
#  # org.redisson.connection.balancer.RoundRobinLoadBalancer - 輪詢調度算法(默認)
#  # org.redisson.connection.balancer.RandomLoadBalancer - 隨機調度算法
#  loadBalancer: org.redisson.connection.balancer.RoundRobinLoadBalancer
#  # slave用於發佈和訂閱連接的最小保持連接數(長連接),默認值1。Redisson內部經常通過發佈和訂閱來實現許多功能。長期保持一定數量的發佈訂閱連接是必須的
#  slaveSubscriptionConnectionMinimumIdleSize: 1
#  # slave的發佈和訂閱連接池大小,默認50,連接池的連接數量自動彈性伸縮
#  slaveSubscriptionConnectionPoolSize: 50
#  # slave的最小空閒連接數
#  slaveConnectionMinimumIdleSize: 32
#  # slave的連接池大小,默認64,連接池的連接數量自動彈性伸縮
#  slaveConnectionPoolSize: 64
#  # mater的最小空閒連接數
#  masterConnectionMinimumIdleSize: 32
#  # mater的連接池大小
#  masterConnectionPoolSize: 64
#  # 讀取操作的負載均衡模式:SLAVE-只在從服務節點裏讀取,MASTER-只在主服務節點裏讀取,MASTER_SLAVE-在主、從服務節點裏都可以讀取
#  readMode: "SLAVE"
#  # 節點地址,通過host:port的格式來指定雲託管模式的多個Redis集羣節點的地址
#  nodeAddresses:
#    - "redis://127.0.0.1:6379"
#    - "redis://127.0.0.1:6380"
#  # 主節點變化掃描間隔時間,對主節點變化節點狀態掃描的時間間隔
#  scanInterval: 1000
## 線程池數量,默認=當前處理核數量*2,該線程池數量被所有RTopic對象監聽器,RRemoteService調用者和RExecutorService任務共同共享
#threads: 0
## Netty線程池數量,默認=當前處理核數量*2,該線程池數量是在一個Redisson實例內,被其創建的所有分佈式數據類型和服務,以及底層客戶端所一同共享的線程池裏保存的線程數量
#nettyThreads: 0
## 編碼,Redisson的對象編碼類是用於將對象進行序列化和反序列化,以實現對該對象在Redis裏的讀取和存儲,默認JsonJacksonCodec
#codec: org.redisson.codec.JsonJacksonCodec
## 傳輸模式,可選:NIO(默認)、EPOLL(需要依賴裏有netty-transport-native-epoll包 Linux)、KQUEUE(需要依賴裏有 netty-transport-native-kqueue包(macOS)
#transportMode:"NIO"

# 1. 單節點
#singleServerConfig:
#  idleConnectionTimeout: 10000
#  pingTimeout: 1000
#  connectTimeout: 10000
#  timeout: 3000
#  retryAttempts: 3
#  retryInterval: 1500
#  reconnectionTimeout: 3000
#  failedAttempts: 3
#  password: 123456
#  subscriptionsPerConnection: 5
#  clientName: null
#  address: "redis://127.0.0.1:6379"
#  subscriptionConnectionMinimumIdleSize: 1
#  subscriptionConnectionPoolSize: 50
#  connectionMinimumIdleSize: 32
#  connectionPoolSize: 64
#  database: 5
##  dnsMonitoring: false
##  dnsMonitoringInterval: 5000
#threads: 0
#nettyThreads: 0
#codec: !<org.redisson.codec.JsonJacksonCodec> {}
#transportMode: NIO

# 2. 主從節點
masterSlaveServersConfig:
  idleConnectionTimeout: 10000
  pingTimeout: 1000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  reconnectionTimeout: 3000
  failedAttempts: 3
  password: 123456
  subscriptionsPerConnection: 5
  clientName: null
  loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}
  slaveSubscriptionConnectionMinimumIdleSize: 1
  slaveSubscriptionConnectionPoolSize: 50
  slaveConnectionMinimumIdleSize: 32
  slaveConnectionPoolSize: 64
  masterConnectionMinimumIdleSize: 32
  masterConnectionPoolSize: 64
  readMode: "SLAVE"
  slaveAddresses:
    - "redis://127.0.0.1:6380"
  masterAddress: "redis://127.0.0.1:6379"
  database: 0
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
"transportMode": "NIO"

3. 使用

 在使用redisson客戶端時,就不需要再去配置jedis或者lettuce相關參數了,同樣具備RedisTemplate可以使用


    @Autowired
    RedisTemplate<String, Object> redisTemplate;
    @Autowired
    RedissonClient                redissonClient;

    public void testRedisson() {
        // 獲取可重入鎖
        RLock rLock = redissonClient.getLock("test-lock");
        try {
            // 如果該線程無法獲取鎖將會不斷嘗試,但至多2s,如果仍未獲取,返回false
            // 如果該線程獲取鎖了,這個鎖將會在redis中存活60s,返回true
            boolean lock = rLock.tryLock(2, 60, TimeUnit.SECONDS);
            if (lock) {
                System.out.println("get lock success!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 利用體用的模板對redis進行操作
        redisTemplate.opsForValue().set("test2", buildProject());
    }

    private Project buildProject() {
        Project project = new Project();
        project.setId(1);
        project.setName("測試");
        project.setUserId(111);
        project.setCreateTime(new Date());
        return project;
    }

Redisson 存入的鎖對應的value是一個UUID:線程ID的形式,具體可到redis中自行查看。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章