工作中,經常會遇到分布式環境中資源訪問沖突問題,比如商城的庫存數量處理,或者某個事件的原子性操作,都需要確保某個時間段內只有一個線程在訪問或處理資源。
因此現在網上也有很多的分布式鎖的解決方案,有數據庫、MemCache、ZoopKeeper等等的方式。
這次,我們要學習的是一個基於Redis分布式鎖的插件,RedLock.Net。
首先必須要有一個Redis服務來支持此分布式鎖,其次就當然是要獲取此插件了。
可以從Nuget中獲取,也可以直接去Github下載 https://github.com/samcook/RedLock.net。

獲取到插件,話不多說上代碼。這個是分布式鎖的封裝類,在需要使用鎖的地方直接調用即可。
using RedLock; using System; using System.Collections.Generic; using System.Linq; using System.Net; namespace KingsBlog.Core { public class DistributedLockManager { private List<RedisLockEndPoint> azureEndPoint; public DistributedLockManager() { azureEndPoint = new List<RedisLockEndPoint>(); azureEndPoint.AddRange(GetEndPoint().Select(o => new RedisLockEndPoint { EndPoint = o.Item1, Password = o.Item2 })); } /// <summary> /// 從配置文件獲取Redis連接 /// </summary> /// <returns></returns> private List<Tuple<EndPoint, string>> GetEndPoint() { List<Tuple<EndPoint, string>> result = new List<Tuple<EndPoint, string>>(); var redisParms = RedisCacheBase.ConnectionString.Split(';'); // "127.0.0.1:6379,password=ucs123;127.0.0.1:6378,password=ucs123;" foreach (var re in redisParms) { var re1 = re.Split(','); var re2 = re1[0].Split(':'); var re3 = re1[0].Split('='); result.Add(new Tuple<EndPoint, string>(new DnsEndPoint(re2[0], Convert.ToInt16(re2.Length > 1 ? re2[1] : "6379")), re3[1])); } return result; } /// <summary> /// 阻塞式調用,事情最終會被調用(等待時間內) /// </summary> /// <param name="resource">鎖定資源的標識</param> /// <param name="expiryTime">鎖過期時間</param> /// <param name="waitTime">等待時間</param> /// <param name="work"></param> public bool BlockingWork(string resource, TimeSpan expiryTime, TimeSpan waitTime, Action work) { resource = CreateKey(resource); using (var redisLockFactory = new RedisLockFactory(azureEndPoint)) { // blocks until acquired or 'wait' timeout using (var redisLock = redisLockFactory.Create(resource, expiryTime, waitTime, TimeSpan.FromSeconds(1))) { if (redisLock.IsAcquired) { work(); return true; } } return false; } } /// <summary> /// 跳過式調用,如果事情正在被調用,直接跳過 /// </summary> /// <param name="resource">鎖定資源的標識</param> /// <param name="expiryTime">鎖過期時間</param> /// <param name="work"></param> public bool OverlappingWork(string resource, TimeSpan expiryTime, Action work) { resource = CreateKey(resource); using (var redisLockFactory = new RedisLockFactory(azureEndPoint)) { using (var redisLock = redisLockFactory.Create(resource, expiryTime)) { if (redisLock.IsAcquired) { work(); return true; } } return false; } } /// <summary> /// 重新設置鍵 /// </summary> /// <param name="key"></param> /// <returns></returns> private string CreateKey(string key) { return string.Join("_", RedisCacheBase.SystemCode, "LOCK", key); } } }
調用示例,簡單粗暴
DistributedLockManager lockManager=new DistributedLockManager(); TimeSpan expiryTime = new TimeSpan(0,3,0); bool isWork=lockManager.OverlappingWork("LockName",expiryTime,()=>{ work(); //Do your job }); if(isWork) { //成功執行 } else { //未執行 }
這樣就十分簡單地實現了基於Redis的分布式鎖。
代碼很簡單,有興趣的朋友也可以利用反編譯軟件ILSpy去了解RedLock的實現原理,以下兩個截圖其實就是RedLock的部分源碼:


其它就不多說了,如有疑問,歡迎提出。
