使用Redis和Zookeeper實(shí)現(xiàn)分布式鎖

介紹

分布式鎖
一般用在分布式系統(tǒng)或者多個(gè)應(yīng)用中,用來控制同一任務(wù)是否執(zhí)行或者任務(wù)的執(zhí)行順序。在項(xiàng)目中,部署了多個(gè)tomcat應(yīng)用,在執(zhí)行定時(shí)任務(wù)時(shí)就會(huì)遇到同一任務(wù)可能執(zhí)行多次的情況,我們可以借助分布式鎖,保證在同一時(shí)間只有一個(gè)tomcat應(yīng)用執(zhí)行了定時(shí)任務(wù)。

實(shí)現(xiàn)方式

1.使用redis的setnx()和expire()
2.使用redis的getset()
3.使用zookeeper的創(chuàng)建節(jié)點(diǎn)node
4.使用zookeeper的創(chuàng)建臨時(shí)序列節(jié)點(diǎn)

實(shí)際應(yīng)用

· 使用redis的setnx()和expire()來實(shí)現(xiàn)分布式鎖

setnx(key,value) 如果key不存在,設(shè)置為當(dāng)前key的值為value;如果key存在,直接返回。
expire()來設(shè)置超時(shí)時(shí)間

## 定義注解類
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Lockable{
    // redis緩存key
    String key();
    // redis緩存key中的數(shù)據(jù)
    String value() default "";
    // 過期時(shí)間(秒),默認(rèn)為一分鐘
    long expire() default 60;
}

## 定時(shí)任務(wù)增加注解@Lockable
@Lockable(key = "DistributedLock:dealExpireRecords")
 public void dealExpireRecords() {
 }

## 定義一個(gè)aop切面LockAspect
## 使用@Around處理所有注解為@Lockable的方法
## 通過連接點(diǎn)確認(rèn)此注解是用在方法上
## 通過方法獲取注解信息,使用setIfAbsent來判斷是否獲取分布式鎖
## 如果沒有獲取分布式鎖,直接返回;如果獲取到分布式鎖,通過expire設(shè)置過期時(shí)間,并調(diào)用指定方法

@Component
@Slf4j
@Aspect
public class LockAspect {

    @Autowired
    private RedisTemplate redisTemplate;

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

        //確認(rèn)此注解是用在方法上
        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,設(shè)置此值并返回true;如果有此key,則返回false
        boolean result = redisTemplate.boundValueOps(key).setIfAbsent(value);
        if (!result) {
            //其他程序已經(jīng)獲取分布式鎖
            return resultObject;
        }

        //設(shè)置過期時(shí)間,默認(rèn)一分鐘
        redisTemplate.boundValueOps(key).expire(expire, TimeUnit.SECONDS);

        try {
            resultObject = pjp.proceed(); //調(diào)用對(duì)應(yīng)方法執(zhí)行
        } 
        catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return resultObject;
    }
}

· 使用redis的getset()來實(shí)現(xiàn)分布式鎖

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

· 使用zookeeper的創(chuàng)建節(jié)點(diǎn)node

使用zookeeper創(chuàng)建節(jié)點(diǎn)node
如果創(chuàng)建節(jié)點(diǎn)成功,表示獲取了此分布式鎖
如果創(chuàng)建節(jié)點(diǎn)失敗,表示此分布式鎖已經(jīng)被其他程序占用
## 多個(gè)程序同時(shí)創(chuàng)建一個(gè)節(jié)點(diǎn)node,只有一個(gè)能夠創(chuàng)建成功

· 使用zookeeper的創(chuàng)建臨時(shí)序列節(jié)點(diǎn)

使用zookeeper創(chuàng)建臨時(shí)序列節(jié)點(diǎn)來實(shí)現(xiàn)分布式鎖
## 適用于順序執(zhí)行的程序
大體思路:
創(chuàng)建臨時(shí)序列節(jié)點(diǎn),找出最小的序列節(jié)點(diǎn),獲取分布式鎖
程序執(zhí)行完成之后此序列節(jié)點(diǎn)消失 ## 通過watch來監(jiān)控節(jié)點(diǎn)的變化
從剩下的節(jié)點(diǎn)的找到最小的序列節(jié)點(diǎn),獲取分布式鎖,執(zhí)行相應(yīng)處理,依次類推...
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容