Redisson實現分布式鎖


Redisson文檔參考:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95

   redis是實現分布式鎖的一種方式,其他還可以基於數據庫,zookeeper等方式實現;這里拿出redis單說一下,redis從原理上大概有兩種實現方式,要么是調用redis原生的原子性命令,要么是通過eval執行封裝好的lua腳本;而從使用上來講也有兩種方式,要么自己動手實現(參考:https://www.cnblogs.com/linjiqin/p/8003838.html),要么使用別人已經封裝好的,例如Redis官方推薦的Redisson客戶端。考慮到Redisson的官方推薦,加上大牛效應,當然是“拿來主義”好了。

開始引入

pom文件中引入依賴:

<!-- redisson 分布式鎖實現 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.8.2</version>
</dependency>

spring中配置:

  引入約束:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
   xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:redisson="http://redisson.org/schema/redisson"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-2.5.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    http://redisson.org/schema/redisson http://redisson.org/schema/redisson/redisson.xsd"
   default-lazy-init="true">

 

         測試staging環境:

<!-- redisson 客戶端 配置實現 -->
<redisson:client id="redissonClient">
    <redisson:cluster-servers password="${password}">
        <redisson:node-address value="redis://127.0.0.1:6600"/>
        <redisson:node-address value="redis://127.0.0.1:6601"/>
        <redisson:node-address value="redis://127.0.0.1:6602"/>
    </redisson:cluster-servers>
</redisson:client>

         生產production環境:

<!-- redisson 客戶端 配置實現 -->
<redisson:client id="redissonClient">
    <redisson:cluster-servers password="${password}">
        <redisson:node-address value="redis://127.0.0.1:6602"/>
        <redisson:node-address value="redis://127.0.0.1:6603"/>
        <redisson:node-address value="redis://127.0.0.2:6602"/>
        <redisson:node-address value="redis://127.0.0.2:6603"/>
        <redisson:node-address value="redis://127.0.0.3:6602"/>
        <redisson:node-address value="redis://127.0.0.3:6603"/>
    </redisson:cluster-servers>
</redisson:client>

業務中使用:

public class LockBiz {

    @Autowired
    RedissonClient redissonClient;

    public boolean doWithLock(long groupId, OperateType operateType, String operator, List<Config> configList) {
        boolean isLocked = false;
        String lockName = "fi_config_groupid_" + groupId;
        RLock lock = redissonClient.getLock(lockName);
        if (lock == null) {
            log.error("lock is null");
            return false;
        }

        try {
            try {
                boolean lockedState = lock.isLocked();
                if (lockedState) {
                    log.error("lock_redisson state seems already locked: {}, name of lock is: {}", lockedState, lockName);
                }
                for (int i = 0; i < Constants.TRY_TIMES; ++i) {
                    isLocked = lock.tryLock(Constants.TRY_LOCK_TIME, Constants.AUTO_UNLOCK_TIMES, Constants.LOCK_TIME_UNIT);
                    log.info("lock_redisson result: {}, try times: {}, time consuming: {}", isLocked, (i+1), (System.currentTimeMillis() - startLock));
                    if (isLocked) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                log.error("failed to get lock_redisson: ", e);
            }
            if (!isLocked) {
                log.error("try lock_redisson failed");
            }
            /**
             加鎖成功,處理業務邏輯
             */
        } finally {
            if (isLocked) {
                try {
                    lock.unlock();
                } catch (Throwable t) {
                    log.error("failed to unlock_redisson, {}", ExceptionUtils.getStackTrace(t));
                }
            }
        }
    }

}

 

注意事項:

  1. redisson的2版本和3版本在配置redis地址的時候貌似不一致,2版本無需前綴“redis://”,而3版本需要;

  2. getLock時,RLock lock = redissonClient.getLock(lockName);  這個lockName一定要唯一,redisson應該是將這個lockName同時作為lock的name和key的名稱,如果和別人重復了,就需要和別人競爭同一把鎖了,而不是自己的業務和自己的業務競爭鎖了。(今天就出現了個問題,我把lockName設置為2,經常會出現加鎖失敗,並且是在循環加鎖之前 這把鎖就已經鎖上了,現在想想應該是別人在其他地方給redis中添加了個key=2的,導致我無法加鎖,排查了挺長時間)

 

Redisson的大概原理

首先是獲取到了一個可重入鎖:

然關鍵后是tryLock方法:

 

從上面可以看出,redisson底層也是基於封裝Lua腳本實現分布式鎖的,但是應該解決了一些其他可能存在的問題,例如官方說的:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM