在node專案中,我們常會跑一些定時任務,比如定時傳送通知、定時傳送郵件等,專案部署的時候,我們往往是多機多例項部署,這就導致每個例項都會跑一次同樣的任務,所以我們需要一個分散式事務鎖,來保證任務只能跑一次。
分散式事務鎖
分散式事務鎖有多種實現方式,大致分為以下幾類
- 基於資料庫實現
- 基於快取(redis,memcached)
- 基於Zookeeper
針對目前的需求,使用資料庫鎖太過於麻煩,Zookeeper目前生產未使用,而redis專案中剛好有使用,所以我們採取第二種實現方式
redLock
redis官方推薦了對應的解決方案 Redlock,官方中列出了各個語言的實現,其中有 node 的實現,如下連線
庫中做了封裝,使用起來非常簡單
Configuration 配置
var client1 = require('redis').createClient(6379, 'redis1.example.com');
var client2 = require('redis').createClient(6379, 'redis2.example.com');
var client3 = require('redis').createClient(6379, 'redis3.example.com');
var Redlock = require('redlock');
var redlock = new Redlock(
// you should have one client for each independent redis node
// or cluster
[client1, client2, client3],
{
// the expected clock drift; for more details
// see http://redis.io/topics/distlock
driftFactor: 0.01, // time in ms
// the max number of times Redlock will attempt
// to lock a resource before erroring
retryCount: 10,
// the time in ms between attempts
retryDelay: 200, // time in ms
// the max time in ms randomly added to retries
// to improve performance under high contention
// see https://www.awsarchitectureblog.com/2015/03/backoff.html
retryJitter: 200 // time in ms
}
);
複製程式碼
Locking & Unlocking 鎖事務和釋放鎖
// the string identifier for the resource you want to lock
var resource = 'locks:account:322456';
// the maximum amount of time you want the resource locked,
// keeping in mind that you can extend the lock up until
// the point when it expires
var ttl = 1000; // 鎖的生存時間,在該時間內,若鎖未釋放,強行釋放
redlock.lock(resource, ttl).then(function(lock) {
// ...do something here...
// unlock your resource when you are done
return lock.unlock()
.catch(function(err) {
// we weren't able to reach redis; your lock will eventually
// expire, but you probably want to log this error
console.error(err);
});
})
複製程式碼
通過以上方式,我們就可以實現分散式事務鎖了
遇到的問題
在測試過程中,發現事務沒有被鎖住,一查,發現兩臺機子的系統時間不一致,有10秒左右的差別(測試夥伴因別的任務手動調整了時間),這就導致時間早的機子先跑了任務,時間慢的機子,在去獲取鎖的時候,鎖早已經釋放,所以RedLock 建立在了 Time 是可信的模型上
的。
這裡推薦一篇文章 Redis RedLock 完美的分散式鎖麼? 解釋的非常好
在別的部落格看過一句話
分散式的CAP理論告訴我們“任何一個分散式系統都無法同時滿足一致性(Consistency)、可用性(Availability)和分割槽容錯性(Partition tolerance),最多隻能同時滿足兩項。”
優秀但不完美的方案在加上優秀的運維,定能夠解決大部分的業務需求。
還有一個關於超時時間ttl
的設定問題,到底是設定多長時間比較好,若設定太短,在任務還沒執行完,鎖就釋放了,反之,如果設定的時間太長,其他獲取鎖的執行緒就可能要平白的多等一段時間,所以這個就要根據具體的業務場景來設定啦
小結
如果redis是使用單臺的話,就沒必要使用 redlock
這個方案,直接使用 setnx
設定一個標誌位,就ok了