亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

.net分布式架構之分布式鎖實現

2019-11-14 13:49:11
字體:
來源:轉載
供稿:網友

分布式鎖

經常用于在解決分布式環境下的業務一致性和協調分布式環境。

實際業務場景中,比如說解決并發一瞬間的重復下單,重復確認收貨,重復發現金券等。

使用分布式鎖的場景一般不能太多。

 

開源地址:http://git.oschina.net/chejiangyi/XXF.BaseService.DistributedLock

開源相關群: .net 開源基礎服務 238543768

這里整理了C#.net關于redis分布式鎖和zookeeper分布式鎖的實現,僅用于研究。(可能有bug)

采用ServiceStack.Redis實現Redis分布式鎖

 
/*      * Redis分布式鎖      * 采用ServiceStack.Redis實現的Redis分布式鎖      * 詳情可閱讀其開源代碼      * 備注:不同版本的 ServiceStack.Redis 實現reidslock機制不同 xxf里面默認使用2.2版本      */    public class RedisDistributedLock : BaseRedisDistributedLock    {        PRivate ServiceStack.Redis.RedisLock _lock;        private RedisClient _client;        public RedisDistributedLock(string redisserver, string key)            : base(redisserver, key)        {         }         public override LockResult TryGetDistributedLock(TimeSpan? getlockTimeOut, TimeSpan? taskrunTimeOut)        {            if (lockresult == LockResult.Success)                throw new DistributedLockException("檢測到當前鎖已獲取");            _client = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient();            /*             * 閱讀源碼發現當其獲取鎖后,redis連接資源會一直占用,知道獲取鎖的資源釋放后,連接才會跳出,可能會導致連接池資源的浪費。             */            

try { this._lock = new ServiceStack.Redis.RedisLock(_client, key, getlockTimeOut); lockresult = LockResult.Success; } catch (Exception exp) { XXF.Log.ErrorLog.Write(string.Format("redis分布式嘗試鎖系統級別嚴重異常,redisserver:{0}", redisserver.NullToEmpty()), exp); lockresult = LockResult.LockSystemExceptionFailure; } return lockresult; } public override void Dispose() { try { if (this._lock != null) this._lock.Dispose(); if (_client != null) this._client.Dispose(); } catch (Exception exp) { XXF.Log.ErrorLog.Write(string.Format("redis分布式嘗試鎖釋放嚴重異常,redisserver:{0}", redisserver.NullToEmpty()), exp); } } }

 

 

來自網絡的java實現Redis分布式鎖(C#版)

/*     * Redis分布式鎖     * 采用網絡上java實現的Redis分布式鎖     * 參考 http://www.blogjava.net/hello-yun/archive/2014/01/15/408988.html     * 詳情可閱讀其開源代碼     */    public class RedisDistributedLockFromJava : BaseRedisDistributedLock    {          public RedisDistributedLockFromJava(string redisserver, string key)            : base(redisserver, key)        {          }         public override LockResult TryGetDistributedLock(TimeSpan? getlockTimeOut, TimeSpan? taskrunTimeOut)        {            if (lockresult == LockResult.Success)                throw new DistributedLockException("檢測到當前鎖已獲取");            try            {                // 1. 通過SETNX試圖獲取一個lock               
         string @lock = key;
long taskexpiredMilliseconds = (taskrunTimeOut != null ? (long)taskrunTimeOut.Value.TotalMilliseconds : (long)DistributedLockConfig.MaxLockTaskRunTime); long getlockexpiredMilliseconds = (getlockTimeOut != null ? (long)getlockTimeOut.Value.TotalMilliseconds : 0); long hassleepMilliseconds = 0; while (true) { using (var redisclient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient()) { long value = CurrentUnixTimeMillis() + taskexpiredMilliseconds + 1; /*Java以前版本都是用SetNX,但是這種是無法設置超時時間的,不是很理解為什么,
              * 可能是因為原來的redis命令比較少導致的?現在用Add不知道效果如何.
                因對redis細節不了解,但個人懷疑若異常未釋放鎖經常發生,可能會導致內存逐步溢出
*/

              bool acquired = redisclient.Add<long>(@lock, value, TimeSpan.FromMilliseconds(taskexpiredMilliseconds + DistributedLockConfig.TaskLockDelayCleepUpTime)); //SETNX成功,則成功獲取一個鎖 if (acquired == true) { lockresult = LockResult.Success; } //SETNX失敗,說明鎖仍然被其他對象保持,檢查其是否已經超時
               else
               {
var oldValueBytes = redisclient.Get(@lock); //超時 if (oldValueBytes != null && BitConverter.ToInt64(oldValueBytes, 0) < CurrentUnixTimeMillis()) { /*此處雖然重設并獲取鎖,但是超時時間可能被覆蓋,故重設超時時間;若有進程一直在嘗試獲取鎖,那么鎖存活時間應該被延遲*/

                  var getValueBytes = redisclient.GetSet(@lock, BitConverter.GetBytes(value)); var o1 = redisclient.ExpireEntryIn(@lock, TimeSpan.FromMilliseconds(taskexpiredMilliseconds + DistributedLockConfig.TaskLockDelayCleepUpTime));//這里如果程序異常終止,依然會有部分鎖未釋放的情況。 // 獲取鎖成功 if (getValueBytes == oldValueBytes) { lockresult = LockResult.Success; } // 已被其他進程捷足先登了 else { lockresult = LockResult.GetLockTimeOutFailure; } } //未超時,則直接返回失敗 else { lockresult = LockResult.GetLockTimeOutFailure; } } } //成功拿到鎖 if (lockresult == LockResult.Success) break; //獲取鎖超時 if (hassleepMilliseconds >= getlockexpiredMilliseconds) { lockresult = LockResult.GetLockTimeOutFailure; break; } //繼續等待 System.Threading.Thread.Sleep(DistributedLockConfig.GetLockFailSleepTime); hassleepMilliseconds += DistributedLockConfig.GetLockFailSleepTime; } } catch (Exception exp) { XXF.Log.ErrorLog.Write(string.Format("redis分布式嘗試鎖系統級別嚴重異常,redisserver:{0}", redisserver.NullToEmpty()), exp); lockresult = LockResult.LockSystemExceptionFailure; } return lockresult; } private long CurrentUnixTimeMillis() { return (long)(System.DateTime.UtcNow - new System.DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc)).TotalMilliseconds; } public override void Dispose() { if (lockresult == LockResult.Success || lockresult == LockResult.LockSystemExceptionFailure) { try { long current = CurrentUnixTimeMillis(); using (var redisclient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient()) { var v = redisclient.Get(key); if (v != null) { // 避免刪除非自己獲取得到的鎖 if (current < BitConverter.ToInt64(v, 0)) { redisclient.Del(key); } } } } catch (Exception exp) { XXF.Log.ErrorLog.Write(string.Format("redis分布式嘗試鎖釋放嚴重異常,redisserver:{0}", redisserver.NullToEmpty()), exp); } } } }

 

ServiceStack.Redis內部實現版本(較舊)

 
/*     * Redis分布式鎖     * 采用ServiceStack.Redis實現的Redis分布式鎖     * 詳情可閱讀其開源代碼     * 備注:不同版本的 ServiceStack.Redis 實現reidslock機制不同       * 拷貝自網絡開源代碼 較舊的實現版本      */    public class RedisDistributedLockFromServiceStack : BaseRedisDistributedLock    {        public RedisDistributedLockFromServiceStack(string redisserver, string key)            : base(redisserver, key)        {          }        public override LockResult TryGetDistributedLock(TimeSpan? getlockTimeOut, TimeSpan? taskrunTimeOut)        {            if (lockresult == LockResult.Success)                throw new DistributedLockException("檢測到當前鎖已獲取");            try                        {                using (var redisClient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())                {                    ExecExtensions.RetryUntilTrue(                             () =>                             {                                 //This pattern is taken from the redis command for SETNX http://redis.io/commands/setnx                                  //Calculate a unix time for when the lock should expire                                 
                  TimeSpan realSpan = taskrunTimeOut ?? TimeSpan.FromMilliseconds(DistributedLockConfig.MaxLockTaskRunTime);
//new TimeSpan(365, 0, 0, 0); //if nothing is passed in the timeout hold for a year DateTime expireTime = DateTime.UtcNow.Add(realSpan); string lockString = (expireTime.ToUnixTimeMs() + 1).ToString(); //Try to set the lock, if it does not exist this will succeed and the lock is obtained
var nx = redisClient.SetEntryIfNotExists(key, lockString);
if (nx) { lockresult = LockResult.Success; return true; } //If we've gotten here then a key for the lock is present. This could be because the lock is
//correctly acquired or it could be because a client that had acquired the lock crashed (or didn't release it properly).
//Therefore we need to get the value of the lock to see when it should expire redisClient.Watch(key); string lockExpireString = redisClient.Get<string>(key); long lockExpireTime; if (!long.TryParse(lockExpireString, out lockExpireTime)) { redisClient.UnWatch(); // since the client is scoped externally
                      lockresult = LockResult.GetLockTimeOutFailure;
return false; } //If the expire time is greater than the current time then we can't let the lock go yet
                   if (lockExpireTime > DateTime.UtcNow.ToUnixTimeMs())
{ redisClient.UnWatch(); // since the client is scoped externally
                     lockresult = LockResult.GetLockTimeOutFailure;
return false; } //If the expire time is less than the current time then it wasn't released properly and we can attempt to //acquire the lock. The above call to Watch(_lockKey) enrolled the key in monitoring, so if it changes //before we call Commit() below, the Commit will fail and return false, which means that another thread //was able to acquire the lock before we finished processing. using (var trans = redisClient.CreateTransaction()) // we started the "Watch" above; this tx will succeed if the value has not moved { trans.QueueCommand(r => r.Set(key, lockString)); //return trans.Commit(); //returns false if Transaction failed var t = trans.Commit(); if (t == false) lockresult = LockResult.GetLockTimeOutFailure; else lockresult = LockResult.Success; return t; } }, getlockTimeOut ); } } catch (Exception exp) { XXF.Log.ErrorLog.Write(string.Format("redis分布式嘗試鎖系統級別嚴重異常,redisserver:{0}", redisserver.NullToEmpty()), exp); lockresult = LockResult.LockSystemExceptionFailure; } return lockresult; } public override void Dispose() { try { using (var redisClient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient()) { redisClient.Remove(key); } } catch (Exception exp) { XXF.Log.ErrorLog.Write(string.Format("redis分布式嘗試鎖釋放嚴重異常,redisserver:{0}", redisserver.NullToEmpty()), exp); } } }

 

 

Zookeeper 版本實現分布式鎖

/*  * 來源java網絡源碼的zookeeper分布式鎖實現(目前僅翻譯并簡單測試ok,未來集成入sdk)  * 備注:    共享鎖在同一個進程中很容易實現,但是在跨進程或者在不同 Server 之間就不好實現了。Zookeeper 卻很容易實現這個功能,實現方式也是需要獲得鎖的 Server 創建一個 EPHEMERAL_SEQUENTIAL 目錄節點,   
然后調用 getChildren方法獲取當前的目錄節點列表中最小的目錄節點是不是就是自己創建的目錄節點,如果正是自己創建的,那么它就獲得了這個鎖,
如果不是那么它就調用 exists(String path, boolean watch) 方法并監控 Zookeeper 上目錄節點列表的變化,一直到自己創建的節點是列表中最小編號的目錄節點,
從而獲得鎖,釋放鎖很簡單,只要刪除前面它自己所創建的目錄節點就行了。
*/ public class ZooKeeprDistributedLockFromJava : IWatcher { private ZooKeeper zk; private string root = "/locks"; // private string lockName; //競爭資源的標志 private string waitNode; //等待前一個鎖 private string myZnode; //當前鎖 //private CountDownLatch latch; //計數器 private AutoResetEvent autoevent; private TimeSpan sessionTimeout = TimeSpan.FromMilliseconds(30000); private IList<Exception> exception = new List<Exception>(); /// <summary> /// 創建分布式鎖,使用前請確認config配置的zookeeper服務可用 </summary> /// <param name="config"> 127.0.0.1:2181 </param> /// <param name="lockName"> 競爭資源標志,lockName中不能包含單詞lock </param> public ZooKeeprDistributedLockFromJava(string config, string lockName) { this.lockName = lockName; // 創建一個與服務器的連接 try
       { zk
= new ZooKeeper(config, sessionTimeout, this); var stat = zk.Exists(root, false); if (stat == null) { // 創建根節點 zk.Create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent); } } catch (KeeperException e) { throw e; } } /// <summary> /// zookeeper節點的監視器 /// </summary> public virtual void Process(WatchedEvent @event) { if (this.autoevent != null) { this.autoevent.Set(); } } public virtual bool tryLock() { try { string splitStr = "_lock_"; if (lockName.Contains(splitStr)) { //throw new LockException("lockName can not contains //u000B");
          }
//創建臨時子節點 myZnode = zk.Create(root + "/" + lockName + splitStr, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EphemeralSequential); Console.WriteLine(myZnode + " is created "); //取出所有子節點 IList<string> subNodes = zk.GetChildren(root, false); //取出所有lockName的鎖 IList<string> lockObjNodes = new List<string>(); foreach (string node in subNodes) { if (node.StartsWith(lockName)) { lockObjNodes.Add(node); } } Array alockObjNodes = lockObjNodes.ToArray(); Array.Sort(alockObjNodes); Console.WriteLine(myZnode + "==" + lockObjNodes[0]); if (myZnode.Equals(root + "/" + lockObjNodes[0])) { //如果是最小的節點,則表示取得鎖 return true; } //如果不是最小的節點,找到比自己小1的節點 string subMyZnode = myZnode.Substring(myZnode.LastIndexOf("/", StringComparison.Ordinal) + 1); waitNode = lockObjNodes[Array.BinarySearch(alockObjNodes, subMyZnode) - 1]; } catch (KeeperException e) { throw e; } return false; } public virtual bool tryLock(TimeSpan time) { try { if (this.tryLock()) { return true; } return waitForLock(waitNode, time); } catch (KeeperException e) { throw e; } return false; } private bool waitForLock(string lower, TimeSpan waitTime) { var stat = zk.Exists(root + "/" + lower, true); //判斷比自己小一個數的節點是否存在,如果不存在則無需等待鎖,同時注冊監聽 if (stat != null) { Console.WriteLine("Thread " + System.Threading.Thread.CurrentThread.Name + " waiting for " + root + "/" + lower); autoevent = new AutoResetEvent(false); bool r = autoevent.WaitOne(waitTime); autoevent.Dispose(); autoevent = null; return r; } else return true; } public virtual void unlock() { try { Console.WriteLine("unlock " + myZnode); zk.Delete(myZnode, -1); myZnode = null; zk.Dispose(); } catch (KeeperException e) { throw e; } } }

 

 

以上代碼僅做參考,未壓測。

代碼粘貼有些問題,詳細請下載開源包運行研究。

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
一区二区在线免费视频| 国产日韩精品在线| 国产欧美最新羞羞视频在线观看| 成人网欧美在线视频| 欧美性20hd另类| 国产欧美日韩高清| 日韩在线视频网| 日韩欧美成人区| 欧美一区二区三区艳史| 午夜精品福利电影| 欧美激情视频在线| 欧美在线日韩在线| 日韩av大片在线| 亚洲人成在线播放| 久久精品久久久久久国产 免费| 亚洲国产成人爱av在线播放| 国产一区二区av| 欧美久久精品午夜青青大伊人| 日韩欧美一区二区三区| 亚洲美女激情视频| 国产成人拍精品视频午夜网站| 亚洲新声在线观看| 永久免费毛片在线播放不卡| 中文字幕精品久久| 色婷婷综合久久久久中文字幕1| 国产日韩在线看片| 色诱女教师一区二区三区| 国产精品久久久久免费a∨| 久久久久久国产免费| 91性高湖久久久久久久久_久久99| 久久视频在线免费观看| 亚洲自拍另类欧美丝袜| 欧美激情视频一区二区三区不卡| 亚洲大胆美女视频| 51色欧美片视频在线观看| 国产精品免费电影| 久久影院模特热| 亚洲视频免费一区| 国内精品视频一区| 社区色欧美激情 | 欧美日韩一区二区三区| 国产精品久久久久福利| 久久天天躁夜夜躁狠狠躁2022| 欧美激情国产精品| 91色p视频在线| 琪琪第一精品导航| 日本最新高清不卡中文字幕| 2019亚洲男人天堂| 久久亚洲影音av资源网| 亚洲性视频网站| 亚洲国产成人爱av在线播放| 亚洲视频自拍偷拍| 26uuu亚洲伊人春色| 国产成人极品视频| 欧美成人精品h版在线观看| 精品久久久久久中文字幕| 国产精品自产拍在线观看| 亚洲一区二区三区四区视频| 性欧美xxxx视频在线观看| 欧美大片va欧美在线播放| 欧美午夜激情小视频| 插插插亚洲综合网| 亚洲精品福利资源站| 欧美华人在线视频| 亚洲a在线播放| 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美天天综合色影久久精品| 成人午夜激情免费视频| 欧美黑人狂野猛交老妇| 国色天香2019中文字幕在线观看| 久久久精品2019中文字幕神马| 亚洲在线免费观看| 国产精品影院在线观看| 亚洲天堂色网站| 国产精品色婷婷视频| 亚洲影院在线看| 91精品久久久久久久久久久久久| 欧美激情国产日韩精品一区18| 日韩精品极品视频免费观看| 中文字幕亚洲一区二区三区五十路| xxxxxxxxx欧美| 欧美性猛交xxxxx水多| 91av视频在线观看| 97在线视频一区| 午夜精品一区二区三区av| 欧美贵妇videos办公室| 精品成人在线视频| 欧美成人第一页| 日韩一区二区福利| 亚洲国产精品99久久| 欧美成人午夜免费视在线看片| 中文字幕久热精品视频在线| 日韩大片免费观看视频播放| 久久精品国产综合| 日韩av综合中文字幕| 久久人人爽人人爽人人片av高请| 色先锋资源久久综合5566| 国产精品免费久久久久久| 一区二区三区日韩在线| 国产精品视频午夜| 91国语精品自产拍在线观看性色| 国产午夜精品全部视频播放| 国产91色在线|免| 九九视频直播综合网| 国产精品久久久久久av福利软件| 日韩精品中文字幕久久臀| 亚洲一区美女视频在线观看免费| 97久久精品人人澡人人爽缅北| 久久精品99久久久久久久久| 亚洲国产日韩精品在线| 精品成人久久av| 亚洲无亚洲人成网站77777| 国产z一区二区三区| 91精品久久久久久久久久另类| 国产精品国产福利国产秒拍| 日韩免费观看网站| 亚洲午夜小视频| 成人a视频在线观看| 国产精品久久久精品| 国产成人极品视频| 国产一区二区成人| 亚洲人成在线播放| 久久99亚洲精品| 欧美日韩国产成人| 人人做人人澡人人爽欧美| 亚洲高清福利视频| 国产综合香蕉五月婷在线| 亚洲欧美一区二区三区久久| 国产一区二区三区丝袜| 中文字幕一精品亚洲无线一区| 91干在线观看| 久久久亚洲影院你懂的| 91久久国产综合久久91精品网站| 5252色成人免费视频| 久久精品国产91精品亚洲| 亚洲综合小说区| 日韩亚洲成人av在线| 欧美日韩综合视频| 亚洲国产精品中文| 久久手机免费视频| 欧美国产第一页| 亚洲全黄一级网站| 久久99青青精品免费观看| 久久久久久网址| 国色天香2019中文字幕在线观看| 日韩欧美在线国产| 国产精品入口免费视频一| 亚洲**2019国产| 久久久亚洲欧洲日产国码aⅴ| 亚洲va码欧洲m码| 亚洲视频日韩精品| 成人午夜激情免费视频| 美女精品久久久| 久久影院资源网| 精品人伦一区二区三区蜜桃免费| 26uuu另类亚洲欧美日本老年| 久久久久久久久久久免费| 欧美国产日韩一区二区在线观看| 国产精品久久久久久久久久久新郎| 国产激情999| 日韩午夜在线视频| 国产一区二区黄| 日韩有码在线播放| 亚洲人成电影在线观看天堂色|