日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

ASP.NET Core中借助CSRedis实现安全高效的分布式锁

發布時間:2023/12/4 asp.net 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET Core中借助CSRedis实现安全高效的分布式锁 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引言


最近回頭看了看開發的.NET Core 2.1項目的復盤總結,其中在多處用到Redis實現的分布式鎖,雖然在OnResultExecuting方法中做了防止死鎖的處理,但在某些場景下還是會發生死鎖的問題,下面我只展示部分代碼:



問題:


1、這里setnx設置的值“1”,我想問,你最后del的這個值一定是你自己創建的嗎?


2、圖中標注的步驟1和步驟2不是原子操作,會有死鎖的概率嗎?


大家可以思考一下先,下面讓我們帶著這兩個問題往下看,下面介紹一下使用Redis實現分布式鎖常用的幾個命令。


一、使用Redis實現分布式鎖常見的幾個命令


? Setnx


命令:SETNX key value


說明:將 key 的值設為 value ,當且僅當 key 不存在。若給定的 key 已經存在,則 SETNX 不做任何動作。SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫。


時間復雜度:O(1)


返回值:設置成功,返回1 ; 設置失敗,返回 0


? Getset


命令:GETSET key value


說明:將給定 key 的值設為 value ,并返回 key 的舊值(old value)。當 key 存在但不是字符串類型時,返回一個錯誤。


時間復雜度:O(1)


返回值:返回給定 key 的舊值; 當 key 沒有舊值時,也即是, key 不存在時,返回 nil 。


? Expire


命令:EXPIRE key seconds


說明:為給定 key 設置生存時間,當 key 過期時(生存時間為 0 ),它會被自動刪除。


時間復雜度:O(1)


返回值:設置成功返回 1 ;當 key 不存在或者不能為 key 設置生存時間時(比如在低于 2.1.3 版本的 Redis 中你嘗試更新 key 的生存時間),返回 0 。


? Del


命令:DEL key [key ...]


說明:刪除給定的一個或多個 key 。不存在的 key 會被忽略。


時間復雜度:O(N); N 為被刪除的 key 的數量。


刪除單個字符串類型的 key ,時間復雜度為O(1)。


刪除單個列表、集合、有序集合或哈希表類型的 key ,時間復雜度為O(M), M 為以上數據結構內的元素數量。


返回值:被刪除 key 的數量。


好了,命令熟悉之后,下面我們就開始一步一步實現分布式鎖。


二、使用Redis實現分布式鎖版本一:與時間戳的結合


對于上面的setnx設置的默認值1,我們采用時間戳來防止問題一,下面先讓我們來看下想當然寫法流程圖。



C#代碼實現:


static void Main(string[] args)
{
? ?var lockTimeout = 5000;//單位是毫秒
? ?var currentTime = DateTime.Now.ToUnixTime(true);
? ?if (SetNx("lockkey", currentTime+ lockTimeout,lockTimeout))
? ?{
? ? ? ?//TODO:一些業務邏輯代碼
? ? ? ?//.....
? ? ? ?//.....
? ? ? ?//最后釋放鎖
? ? ? ?Remove("lockkey");
? ?}
? ?else
? ?{
? ? ? ?Console.WriteLine("沒有獲得分布式鎖");
? ?}
? ?Console.ReadKey();
}

public static bool SetNx(string key,long time ,double expireMS)
{
? ?if (redisClient.SetNx(key, time))
? ?{
? ? ? ?if (expireMS > 0)
? ? ? ? ? ?redisClient.Expire(key, TimeSpan.FromMilliseconds(expireMS));
? ? ? ?return true;
? ?}
? ?return false;
}

public static bool Remove(string key)
{
? ?return redisClient.Del(key) > 0;
}


上面的代碼中value的值我們使用時間戳,不是一個固定的值了,至少能保證你刪除的key確實是你自己的,所以,建議大家在設value的值時,不要設置一個固定的值,最好是隨機的。


但是這樣寫雖然解決了問題一,但是這種寫法還是存在一定的風險,雖然Redis是單線程的并且setnx、expire是原子操作,但是先setnx再expire就不是原子操作了!!!我們要考慮多線程環境和容器部署時多實例環境等等,那這樣的寫法就會出現問題。


比如:現在有A、B兩臺服務器在跑這個應用,當A臺應用跑到:setnx成功但是還沒有設置過期時間的時候,突然重啟服務,這個時候在分布式環境中就會發生死鎖的問題,因為你沒有設置過期時間。


下面我們通過調試來展示死鎖的場景:


A應用:在執行到setnx成功但是在執行expire之前宕機了,此時的Redis已經有數據了,但是沒有過期時間



B應用:運行正常


但是B應用就會一直獲取不到鎖,導致死鎖。



所以上面在獲取鎖的邏輯還是有問題的,為了解決這個問題,我們采用下面的方式來處理。


三、使用Redis實現分布式鎖版本二:雙重防死鎖


流程圖:



C#代碼實現:


public static void RedisLockV2()
{
? ?var lockTimeout = 5000;//單位是毫秒
? ?var currentTime = DateTime.Now.ToUnixTime(true);
? ?if (SetNxV2("lockkey",DateTime.Now.ToUnixTime(true)+lockTimeout))
? ?{
? ? ? ?//設置過期時間
? ? ? ?redisClient.Expire("lockkey", TimeSpan.FromMilliseconds(5000));
? ? ? ?//TODO:一些業務邏輯代碼
? ? ? ?Console.WriteLine("處理業務ing");
? ? ? ?Thread.Sleep(100000);
? ? ? ?Console.WriteLine("處理業務ed");
? ? ? ?//最后釋放鎖
? ? ? ?Remove("lockkey");
? ?}
? ?else
? ?{
? ? ? ?//未獲取到鎖,繼續判斷,判斷時間戳看看是否可以重置并獲取鎖
? ? ? ?var lockValue = redisClient.Get("lockkey");
? ? ? ?var time = DateTime.Now.ToUnixTime(true);
? ? ? ?if (!string.IsNullOrEmpty(lockValue) && ?time> lockValue.ToInt64())
? ? ? ?{
? ? ? ? ? ?//再次用當前時間戳getset
? ? ? ? ? ?//返回固定key的舊值,舊值判斷是否可以獲取鎖
? ? ? ? ? ?var getsetResult = redisClient.GetSet("lockkey", time);
? ? ? ? ? ?if (getsetResult == null || (getsetResult != null && getsetResult == lockValue))
? ? ? ? ? ?{
? ? ? ? ? ? ? ?Console.WriteLine("獲取到Redis鎖了");
? ? ? ? ? ? ? ?//真正獲取到鎖
? ? ? ? ? ? ? ?redisClient.Expire("lockkey", TimeSpan.FromMilliseconds(5000));
? ? ? ? ? ? ? ?//TODO:一些業務邏輯代碼
? ? ? ? ? ? ? ?//.....
? ? ? ? ? ? ? ?//.....
? ? ? ? ? ? ? ?Console.WriteLine("處理業務");
? ? ? ? ? ? ? ?//最后釋放鎖
? ? ? ? ? ? ? ?Remove("lockkey");
? ? ? ? ? ?}
? ? ? ? ? ?else
? ? ? ? ? ?{
? ? ? ? ? ? ? ?Console.WriteLine("沒有獲取到鎖");
? ? ? ? ? ?}
? ? ? ?}
? ? ? ?else
? ? ? ?{
? ? ? ? ? ?Console.WriteLine("沒有獲取到鎖");
? ? ? ?}
? ?}
}


現在,Redis中的情況如下:



我們運行上面的代碼,結果如下:



副本.exe中添加一行代碼。來模擬這種場景:有A、B兩臺服務器在跑這個應用,當A臺應用跑到:setnx成功但是還沒有設置過期時間的時候,突然重啟服務,這個時候在分布式環境中就會發生死鎖的問題,因為你沒有設置過期時間



我們先執行Lottery.ThriftRpc - 副本.exe,等Redis里面有值了,并且這個key是沒有過期時間,再關閉掉該程序:



然后,再執行Lottery.ThriftRpc.exe




我們是不是解決了該問題,至于過期時間設置為多少要結合你的具體業務處理時間來計算出一個合理的值,好了,聊到這里關于Redis的分布式鎖就講完了。


四、總結


上面的示例中Redis的組件用的是CSRedisCore,這里只是自己的一點體會,如果你有更好的辦法,可以在評論區討論,關于Redis的理論講解有太多的文章了,大家可以參考,關于Redis的文章我只總結工作中遇到的一些問題,關于文章中的源碼,我就不提供了,太簡單了。

原文地址:http://cnblogs.com/runningsmallguo/p/10322315.html

.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com


總結

以上是生活随笔為你收集整理的ASP.NET Core中借助CSRedis实现安全高效的分布式锁的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。