首頁 > 軟體

Redis對批次資料實現分散式鎖的實現程式碼

2022-03-24 13:01:51

需求背景

在開發的收入結轉平臺介面上有一個歸集按鈕,可以實現抓取結轉表裡面的多條資料進行歸集操作。為了防止多人多電腦同時操作一條資料,我們自己開發了一個簡單的基於Redis實現的分散式鎖。

程式碼實現

邏輯程式碼中的使用案例

引數說明:

scIds:結轉資料的ID主鍵集合。

timeOutToDeleteRedisKey:最大鎖超時時間(用於自動解鎖)

organizationId:租戶ID(這個引數根據情況選擇是否需要)

ReturnLock returnLock = RedisLock.applyByIds(scIds, redisLockTime, organizationId, () -> {
    // dothing 具體的程式碼業務邏輯
    return 123;
});
 
if (!returnLock.getFlag()) {
    if(returnLock.getLock()){
        throw new CommonException("歸集資料有程式正在執行,請稍候重新整理頁面重試");
    }else {
        throw new CommonException(returnLock.getErrorMsg());
    }
}
// 返回物件
System.out.println(returnLock.getResObj()) // 123

Redis加鎖方法封裝

public static ReturnLock applyByIds(List<Long> scIds, Long timeOutToDeleteRedisKey, Long tenantId,
                                    Supplier<Object> supplier) {
    // 獲得鎖
    Map<String, String> keyMap = getLockByIds(scIds, timeOutToDeleteRedisKey, tenantId);
    ReturnLock returnLock = new ReturnLock();
    returnLock.setFlag(true);
    returnLock.setLock(false);
    try {
        // 判斷主鍵ID數量和加鎖的數量是否一致,不一致說明有加鎖失敗的資料,返回失敗鎖資訊
        if(scIds.size() > keyMap.size()){
            returnLock.setFlag(false);
            returnLock.setLock(true);
            returnLock.setKeyMap(keyMap);
            return returnLock;
        }
        // 應用程式碼執行後的返回結果 supplier:java8四大內建函數的供給型介面
        // Supplier<T>(供給型介面)無引數,返回型別為T的物件:T get()
        returnLock.setResObj(supplier.get());
    }catch (Exception e) {
        returnLock.setFlag(false);
        returnLock.setErrorMsg(e.getMessage());
        e.printStackTrace();
    }finally {
        // 應用程式碼執行報錯,解鎖
        unLockByIds(keyMap);
    }
    return returnLock;
}

getLockByIds

批次獲取每一條資料的Redis鎖

private static Map<String, String> getLockByIds(List<Long> scIds, Long timeOutToDeleteRedisKey, Long tenantId) {
    Map<String, String> keyMap = new HashMap<>();
    // 從spring上下文中獲取Redis的操作物件,因為這個程式碼是寫在util中,所以通過上下文方式獲取bean物件
    // RedisHelper:Redis的操作物件,我們自己公司基於redisTemplate封裝的
    RedisHelper redisHelper = SpringUtil.getBean(RedisHelper.class);
    try {
        if (CollectionUtil.isNotEmpty(scIds)) {
            for (int i = 0; i < scIds.size(); i++) {
                String item = "L_" + scIds.get(i);
                String UUID = UUIDUtils.generateTenantUUID(tenantId);
                // 判斷key值是否被鎖,如果沒有鎖則加鎖並設定過期時間, 如果有鎖, 則報錯並立即釋放已加的鎖
                // strSetIfAbsent會返回true/false,底層是封裝了java的setIfAbsent方法和Redis的setnx方法
                // 如果設定成功返回true否則false
                if (redisHelper.strSetIfAbsent(item, UUID)) {
                    // 設定過期時間
                    redisHelper.setExpire(item, timeOutToDeleteRedisKey, TimeUnit.SECONDS);
                    // 儲存設定的鎖資訊
                    keyMap.put(item, UUID);
                } else {
                    // 鎖設定異常,表示有資料被鎖住了,解鎖之間加鎖的資料
                    if (MapUtil.isNotEmpty(keyMap)) {
                        if (unLockByIds(keyMap)) {
                            // 解鎖失敗再次解鎖
                            unLockByIds(keyMap);
                        }
                    }
                    // 返回鎖資訊
                    return keyMap;
                }
            }
        }
        return keyMap;
    }catch (Exception e) {
        e.printStackTrace();
    }
}

解鎖

private static Boolean unLockByIds(Map<String, String> keyMap) {
    RedisHelper redisHelper = SpringUtil.getBean(RedisHelper.class);
    try {
        if (MapUtil.isEmpty(keyMap)) {
            return false;
        }
        // 判斷是否是自己的鎖
        for (String key : keyMap.keySet()) {
            String uuid = keyMap.get(key);
            if (StringUtils.equals(uuid, redisHelper.strGet(key))) {
                // 封裝了redisTemplate.delete(key);
                redisHelper.delKey(key);
            }
        return true;
    }catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

實現效果

當對資料進行批次加鎖的時候,若在加鎖的過程中出現加鎖失敗,則回滾直接加的鎖,並提示"歸集資料有程式正在執行,請稍候重新整理頁面重試"。

若業務程式碼邏輯執行成功或者執行報錯都會自動的解鎖當前加鎖的資料。

到此這篇關於利用Redis對批次資料實現分散式鎖的文章就介紹到這了,更多相關Redis分散式鎖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com