node.js 中使用redis實現分散式事務鎖

小黎也發表於2018-06-09

在node專案中,我們常會跑一些定時任務,比如定時傳送通知、定時傳送郵件等,專案部署的時候,我們往往是多機多例項部署,這就導致每個例項都會跑一次同樣的任務,所以我們需要一個分散式事務鎖,來保證任務只能跑一次。

分散式事務鎖

分散式事務鎖有多種實現方式,大致分為以下幾類

  • 基於資料庫實現
  • 基於快取(redis,memcached)
  • 基於Zookeeper

針對目前的需求,使用資料庫鎖太過於麻煩,Zookeeper目前生產未使用,而redis專案中剛好有使用,所以我們採取第二種實現方式

redLock

redis官方推薦了對應的解決方案 Redlock,官方中列出了各個語言的實現,其中有 node 的實現,如下連線

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了

相關文章