Redlock實(shí)踐--非阻塞模式

本文主要介紹在琴房預(yù)約項(xiàng)目中所用到的資源訪問加鎖技術(shù)Redlock,以及如果實(shí)現(xiàn)將redlock轉(zhuǎn)為非阻塞鎖。

1. 背景簡介

1.1 項(xiàng)目簡介

本次實(shí)踐項(xiàng)目是THU琴房預(yù)約小程序與web管理端,后端使用koa框架。主要功能有預(yù)約琴房(用戶)、更改琴房可用時(shí)間(管理端)等。后端使用mysql存儲(chǔ)琴房信息(包含琴房可用時(shí)間串)。

1.2 問題介紹

  • 多個(gè)用戶可能在同一時(shí)間對(duì)同一琴房的同一時(shí)間段進(jìn)行預(yù)約
  • 用戶與管理員可能同時(shí)進(jìn)行預(yù)約、更改琴房可用時(shí)間

根據(jù)上篇文章所描述的,因?yàn)榇嬖诓煌僮髦g的資源競爭,所以我們這里使用增加信號(hào)量的方式——Redlock。

2. 加鎖實(shí)踐

redlock是一個(gè)基于redis數(shù)據(jù)庫的分布式鎖。通過在redis數(shù)據(jù)庫中設(shè)定鍵值來進(jìn)行信號(hào)量的定義。

  • 我們把“預(yù)約“和“更改琴房可用時(shí)間”定義為使用同一個(gè)鍵值。
  • 在操作前首先獲取此鍵值,如果獲取不到,說明被占用。

關(guān)于這個(gè)流程,包括set鍵值與”占用"和"未占用“狀態(tài)的更改是由Redlock實(shí)現(xiàn)的。

2.1 給資源加鎖

// 獲取權(quán)限
let redis = require("redis");
let client = redis.createClient("to change: your redis port","to change: your server ip");
let redlock = new Redlock([client]);
// 設(shè)置時(shí)間
let totalTime = 5000;
let key = "to change: as you like";
// 加鎖
redlock.lock(key, totalTime).then(async function(lock){
        // to do
        // your operation block
        // release lock
        lock.unlock().catch(function(err){})
    }
}).catch(()=>{}) // 如果不加catch會(huì)直接報(bào)錯(cuò)終止程序

主要流程為:

  • 獲取redis的操作權(quán)限
  • 設(shè)定鍵值,最長等待時(shí)間(防止一直被占用)
  • 執(zhí)行操作
  • 釋放鎖

按照上面流程,我們成功進(jìn)行了加鎖。

鍵值的設(shè)置非常自由,我們可以通過鍵值的設(shè)置,控制加鎖覆蓋的范圍以及力度。

2.2 阻塞鎖與非阻塞鎖

在完成加鎖之后,我們進(jìn)行壓力測試,發(fā)現(xiàn)了一個(gè)非??拥那闆r:Redlock是非阻塞鎖。

這就意味著,當(dāng)一個(gè)用戶進(jìn)行預(yù)約的時(shí)候,別的用戶如果有請(qǐng)求就會(huì)直接失敗,這樣是非常用戶不友好的。所以我們需要一些操作來將redlock轉(zhuǎn)為非阻塞鎖。有兩個(gè)方案:

  • 方案一:實(shí)現(xiàn)一個(gè)請(qǐng)求隊(duì)列以及回調(diào)函數(shù),在資源被釋放的時(shí)候,進(jìn)行回調(diào)。
  • 方案二:設(shè)置最長等待時(shí)間,在此時(shí)間段內(nèi)進(jìn)行輪詢,如果超過此時(shí)間,放棄請(qǐng)求。

對(duì)于方案一,請(qǐng)求隊(duì)列可能會(huì)很長,如果前面的資源不釋放,就會(huì)一直等待;對(duì)于方案二,輪詢需要消耗更多的資源。

我們這里說明方案二的實(shí)現(xiàn)(目前最常用的轉(zhuǎn)阻塞鎖的方案)

let redis = require("redis");
let client = redis.createClient(config.redisPort,config.serverIp);
let redlock = new Redlock([client]);
let totalTime = 5000;
let key = "to change: as you like";
let intervalTime = 50;  // 輪詢時(shí)間間隔

let sleep = function(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

let tag = 0;
for(let j = 0; j<200; j++){
    redlock.lock(key, totalTime).then(async function(lock){
        if(tag === 1){
            lock.unlock().catch(function(err){})
        }
        else{
            tag = 1
            // to do
            // your operation block
            // release lock
            lock.unlock().catch(function(err){})
        }
    }).catch(()=>{})
    if(tag === 1){
        break
    }
    await sleep(intervalTime) // 設(shè)置間隔時(shí)間
}
if(tag === 0){
    errorMsg = "請(qǐng)求超時(shí)"
    return ;
}

3. 效果檢驗(yàn)

加鎖效果

在預(yù)約與檢票單項(xiàng)測試的時(shí)候,1000個(gè)人中只有一個(gè)人成功,符合預(yù)期。

加鎖效果

在預(yù)約與更改琴房可用時(shí)間混合測試的時(shí)候,2000個(gè)人中只有一個(gè)人成功,符合預(yù)期。

以上,說明這樣加鎖,可以成功解決資源訪問競爭情況。

4. 參考資料

  1. redlock 效果解析

  2. redlock git地址

?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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