使用redis和zookeeper實現分散式鎖

沉澱發表於2017-07-09

1.分散式鎖

分散式鎖一般用在分散式系統或者多個應用中,用來控制同一任務是否執行或者任務的執行順序。在專案中,部署了多個tomcat應用,在執行定時任務時就會遇到同一任務可能執行多次的情況,我們可以藉助分散式鎖,保證在同一時間只有一個tomcat應用執行了定時任務。

2.分散式鎖的實現方式

  1. 使用redis的setnx()和expire()

  2. 使用redis的getset()

  3. 使用zookeeper的建立節點node

  4. 使用zookeeper的建立臨時序列節點

3.使用redis的setnx()和expire()來實現分散式鎖

setnx(key,value) 如果key不存在,設定為當前key的值為value;如果key存在,直接返回。
expire()來設定超時時間

定義註解類:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Lockable{
    // redis快取key
    String key();
    // redis快取key中的資料
    String value() default "";
    // 過期時間(秒),預設為一分鐘
    long expire() default 60;
}

定時任務增加註解@Lockable:

 @Lockable(key = "DistributedLock:dealExpireRecords")
 public void dealExpireRecords() {
 }

定義一個aop切面LockAspect,使用@Around處理所有註解為@Lockable的方法,通過連線點確認此註解是用在方法上,通過方法獲取註解資訊,使用setIfAbsent來判斷是否獲取分散式鎖,如果沒有獲取分散式鎖,直接返回;如果獲取到分散式鎖,通過expire設定過期時間,並呼叫指定方法。

@Component
@Slf4j
@Aspect
public class LockAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    @Around("@annotation(com.records.aop.Lockable)")
    public Object distributeLock(ProceedingJoinPoint pjp) {
        Object resultObject = null;

        //確認此註解是用在方法上
        Signature signature = pjp.getSignature();
        if (!(signature instanceof MethodSignature)) {
            log.error("Lockable is method annotation!");
            return resultObject;
        }

        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();

        //獲取註解資訊
        Lockable lockable = targetMethod.getAnnotation(Lockable.class);
        String key = lockable.key();
        String value = lockable.value();
        long expire = lockable.expire();

        // 分散式鎖,如果沒有此key,設定此值並返回true;如果有此key,則返回false
        boolean result = redisTemplate.boundValueOps(key).setIfAbsent(value);
        if (!result) {
            //其他程式已經獲取分散式鎖
            return resultObject;
        }

        //設定過期時間,預設一分鐘
        redisTemplate.boundValueOps(key).expire(expire, TimeUnit.SECONDS);

        try {
            resultObject = pjp.proceed(); //呼叫對應方法執行
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return resultObject;
    }
}

4.使用redis的getset()來實現分散式鎖

此方法使redisTemplate.boundValueOps(key).getAndSet(value)的方法,如果返回空,表示獲取了分散式鎖;如果返回不為空,表示分散式鎖已經被其他程式佔用

5.使用zookeeper的建立節點node

使用zookeeper建立節點node,如果建立節點成功,表示獲取了此分散式鎖;如果建立節點失敗,表示此分散式鎖已經被其他程式佔用(多個程式同時建立一個節點node,只有一個能夠建立成功)

6.使用zookeeper的建立臨時序列節點

使用zookeeper建立臨時序列節點來實現分散式鎖,適用於順序執行的程式,大體思路就是建立臨時序列節點,找出最小的序列節點,獲取分散式鎖,程式執行完成之後此序列節點消失,通過watch來監控節點的變化,從剩下的節點的找到最小的序列節點,獲取分散式鎖,執行相應處理,依次類推……

本文主要介紹了使用redis和zookeeper實現分散式鎖的處理,也可以關注我的公眾號:不知風在何處,相互溝通,共同進步。

相關文章