欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解ASP.Net Core 中如何借助CSRedis實(shí)現(xiàn)一個(gè)安全高效的分布式鎖

 更新時(shí)間:2019年04月09日 15:29:27   作者:菠蘿吹雪—Code  
這篇文章主要介紹了ASP.Net Core 中如何借助CSRedis實(shí)現(xiàn)一個(gè)安全高效的分布式鎖,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

引言:最近回頭看了看開發(fā)的.Net Core 2.1項(xiàng)目的復(fù)盤總結(jié),其中在多處用到Redis實(shí)現(xiàn)的分布式鎖,雖然在OnResultExecuting方法中做了防止死鎖的處理,但在某些場景下還是會(huì)發(fā)生死鎖的問題,下面我只展示部分代碼:

問題:

(1)這里setnx設(shè)置的值“1”,我想問,你最后del的這個(gè)值一定是你自己創(chuàng)建的嗎?

(2)圖中標(biāo)注的步驟1和步驟2不是原子操作,會(huì)有死鎖的概率嗎?

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

一、使用Redis實(shí)現(xiàn)分布式鎖常見的幾個(gè)命令

Setnx

命令:SETNX key value
說明:將 key 的值設(shè)為 value ,當(dāng)且僅當(dāng) key 不存在。若給定的 key 已經(jīng)存在,則 SETNX 不做任何動(dòng)作。SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫。
時(shí)間復(fù)雜度:O(1)
返回值:設(shè)置成功,返回1 ; 設(shè)置失敗,返回 0

Getset

命令:GETSET key value
說明:將給定 key 的值設(shè)為 value ,并返回 key 的舊值(old value)。當(dāng) key 存在但不是字符串類型時(shí),返回一個(gè)錯(cuò)誤。
時(shí)間復(fù)雜度:O(1)
返回值:返回給定 key 的舊值; 當(dāng) key 沒有舊值時(shí),也即是, key 不存在時(shí),返回 nil 。

Expire

命令:EXPIRE key seconds
說明:為給定 key 設(shè)置生存時(shí)間,當(dāng) key 過期時(shí)(生存時(shí)間為 0 ),它會(huì)被自動(dòng)刪除。
時(shí)間復(fù)雜度:O(1)
返回值:設(shè)置成功返回 1 ;當(dāng) key 不存在或者不能為 key 設(shè)置生存時(shí)間時(shí)(比如在低于 2.1.3 版本的 Redis 中你嘗試更新 key 的生存時(shí)間),返回 0 。

Del

命令:DEL key [key ...]
說明:刪除給定的一個(gè)或多個(gè) key 。不存在的 key 會(huì)被忽略。
時(shí)間復(fù)雜度:O(N); N 為被刪除的 key 的數(shù)量。
刪除單個(gè)字符串類型的 key ,時(shí)間復(fù)雜度為O(1)。
刪除單個(gè)列表、集合、有序集合或哈希表類型的 key ,時(shí)間復(fù)雜度為O(M), M 為以上數(shù)據(jù)結(jié)構(gòu)內(nèi)的元素?cái)?shù)量。
返回值:被刪除 key 的數(shù)量。

好了,命令熟悉之后,下面我們就開始一步一步實(shí)現(xiàn)分布式鎖。

二、使用Redis實(shí)現(xiàn)分布式鎖版本一:與時(shí)間戳的結(jié)合

對于上面的setnx設(shè)置的默認(rèn)值1,我們采用時(shí)間戳來防止問題一,下面先讓我們來看下想當(dāng)然寫法流程圖。

流程圖:

C#代碼實(shí)現(xiàn):

static void Main(string[] args)
    {
      var lockTimeout = 5000;//單位是毫秒
      var currentTime = DateTime.Now.ToUnixTime(true);
      if (SetNx("lockkey", currentTime+ lockTimeout,lockTimeout))
      {
        //TODO:一些業(yè)務(wù)邏輯代碼
        //.....
        //.....
        //最后釋放鎖
        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的值我們使用時(shí)間戳,不是一個(gè)固定的值了,至少能保證你刪除的key確實(shí)是你自己的,所以,建議大家在設(shè)value的值時(shí),不要設(shè)置一個(gè)固定的值,最好是隨機(jī)的。但是這樣寫雖然解決了問題一,但是這種寫法還是存在一定的風(fēng)險(xiǎn),雖然Redis是單線程的并且setnx、expire是原子操作,但是先setnx再expire就不是原子操作了!?。∥覀円紤]多線程環(huán)境和容器部署時(shí)多實(shí)例環(huán)境等等,那這樣的寫法就會(huì)出現(xiàn)問題。

比如:現(xiàn)在有A、B兩臺(tái)服務(wù)器在跑這個(gè)應(yīng)用,當(dāng)A臺(tái)應(yīng)用跑到:setnx成功但是還沒有設(shè)置過期時(shí)間的時(shí)候,突然重啟服務(wù),這個(gè)時(shí)候在分布式環(huán)境中就會(huì)發(fā)生死鎖的問題,因?yàn)槟銢]有設(shè)置過期時(shí)間。

下面我們通過調(diào)試來展示死鎖的場景:

A應(yīng)用:在執(zhí)行到setnx成功但是在執(zhí)行expire之前宕機(jī)了,此時(shí)的Redis已經(jīng)有數(shù)據(jù)了,但是沒有過期時(shí)間

B應(yīng)用:運(yùn)行正常

但是B應(yīng)用就會(huì)一直獲取不到鎖,導(dǎo)致死鎖。

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

三、使用Redis實(shí)現(xiàn)分布式鎖版本二:雙重防死鎖

流程圖:

C#代碼實(shí)現(xiàn):

public static void RedisLockV2()
    {
      var lockTimeout = 5000;//單位是毫秒
      var currentTime = DateTime.Now.ToUnixTime(true);

      if (SetNxV2("lockkey",DateTime.Now.ToUnixTime(true)+lockTimeout))
      {
        //設(shè)置過期時(shí)間
        redisClient.Expire("lockkey", TimeSpan.FromMilliseconds(5000));
        //TODO:一些業(yè)務(wù)邏輯代碼
        
        Console.WriteLine("處理業(yè)務(wù)ing");
        Thread.Sleep(100000);

        Console.WriteLine("處理業(yè)務(wù)ed");
        //最后釋放鎖
        Remove("lockkey");
      }
      else
      {
        //未獲取到鎖,繼續(xù)判斷,判斷時(shí)間戳看看是否可以重置并獲取鎖
        var lockValue = redisClient.Get("lockkey");
        var time = DateTime.Now.ToUnixTime(true);

        if (!string.IsNullOrEmpty(lockValue) && time> lockValue.ToInt64())
        {
          //再次用當(dāng)前時(shí)間戳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:一些業(yè)務(wù)邏輯代碼
            //.....
            //.....
            Console.WriteLine("處理業(yè)務(wù)");
            //最后釋放鎖
            Remove("lockkey");
          }
          else
          {
            Console.WriteLine("沒有獲取到鎖");
          }

        }
        else
        {
          Console.WriteLine("沒有獲取到鎖");
        }
      }
      
    }

現(xiàn)在,Redis中的情況如下:

我們運(yùn)行上面的代碼,結(jié)果如下:

副本.exe中添加一行代碼。來模擬這種場景:有A、B兩臺(tái)服務(wù)器在跑這個(gè)應(yīng)用,當(dāng)A臺(tái)應(yīng)用跑到:setnx成功但是還沒有設(shè)置過期時(shí)間的時(shí)候,突然重啟服務(wù),這個(gè)時(shí)候在分布式環(huán)境中就會(huì)發(fā)生死鎖的問題,因?yàn)槟銢]有設(shè)置過期時(shí)間

我們先執(zhí)行Lottery.ThriftRpc - 副本.exe,等Redis里面有值了,并且這個(gè)key是沒有過期時(shí)間,再關(guān)閉掉該程序:

然后,再執(zhí)行Lottery.ThriftRpc.exe

看,我們是不是解決了該問題,至于過期時(shí)間設(shè)置為多少要結(jié)合你的具體業(yè)務(wù)處理時(shí)間來計(jì)算出一個(gè)合理的值,好了,聊到這里關(guān)于Redis的分布式鎖就講完了,希望對你有幫助,謝謝。

四、總結(jié):

上面的示例中Redis的組件用的是CSRedisCore,這里只是自己的一點(diǎn)體會(huì),如果你有更好的辦法,可以在評論區(qū)討論,關(guān)于Redis的理論講解有太多的文章了,大家可以參考,關(guān)于Redis的文章我只總結(jié)工作中遇到的一些問題,關(guān)于文章中的源碼,我就不提供了,太簡單了。后面我會(huì)不定期分享一些Redis的問題,希望大家多多支持。

以上所述是小編給大家介紹的ASP.Net Core 中如何借助CSRedis實(shí)現(xiàn)一個(gè)安全高效的分布式鎖詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • EF?Core通過顯式編譯提高查詢性能

    EF?Core通過顯式編譯提高查詢性能

    這篇文章介紹了EF?Core通過顯式編譯提高查詢性能的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • .NET命令行解析器示例程序(命令行選項(xiàng)功能)

    .NET命令行解析器示例程序(命令行選項(xiàng)功能)

    經(jīng)常需要開發(fā)一下小工具,之前都是自己解析命令行參數(shù),接觸過動(dòng)態(tài)語言社區(qū)以后,發(fā)現(xiàn)命令行解析有特定的模式和框架可以利用,本文介紹一個(gè).NET 平臺(tái)的類庫CommandLineParser
    2013-11-11
  • ASP.NET Core實(shí)現(xiàn)自動(dòng)依賴注入

    ASP.NET Core實(shí)現(xiàn)自動(dòng)依賴注入

    這篇文章主要介紹了ASP.NET Core實(shí)現(xiàn)自動(dòng)依賴注入的示例,幫助大家更好的理解和學(xué)習(xí)使用.net技術(shù),感興趣的朋友可以了解下
    2021-04-04
  • 比較完整的 asp.net 學(xué)習(xí)流程

    比較完整的 asp.net 學(xué)習(xí)流程

    好多朋友想學(xué)習(xí)后臺(tái)編程語言,但請注意的事,學(xué)習(xí)后臺(tái)是個(gè)循序漸進(jìn)的過程,不可能一下就到位,其實(shí)不只是asp.net其它的編程語言都需要下面的一些知識(shí)。
    2009-06-06
  • Blazor組件的生命周期解析

    Blazor組件的生命周期解析

    這篇文章介紹了Blazor組件的生命周期,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • 初識(shí)ASP.NET Mvc5+EF7的奇妙之旅

    初識(shí)ASP.NET Mvc5+EF7的奇妙之旅

    這篇文章主要和大家一起感受ASP.NET Mvc5+EF7的奇妙之旅,從旅程中認(rèn)識(shí)了解.NET5框架,感興趣的小伙伴們可以參考一下
    2015-09-09
  • 輕松解決asp.net用戶ASPNET登錄失敗問題的方法分享

    輕松解決asp.net用戶ASPNET登錄失敗問題的方法分享

    這篇文章介紹了asp.net用戶ASPNET登錄失敗問題的方法,有需要的朋友可以參考一下
    2013-11-11
  • .NET?8新預(yù)覽版使用?Blazor?組件進(jìn)行服務(wù)器端呈現(xiàn)(項(xiàng)目體驗(yàn))

    .NET?8新預(yù)覽版使用?Blazor?組件進(jìn)行服務(wù)器端呈現(xiàn)(項(xiàng)目體驗(yàn))

    這篇文章主要介紹了.NET?8新預(yù)覽版使用?Blazor?組件進(jìn)行服務(wù)器端呈現(xiàn)(項(xiàng)目體驗(yàn)),這是 Blazor 統(tǒng)一工作的開始,旨在使 Blazor 組件能夠滿足客戶端和服務(wù)器端的所有 Web UI 需求,需要的朋友可以參考下
    2023-04-04
  • Asp.net mvc 權(quán)限過濾和單點(diǎn)登錄(禁止重復(fù)登錄)

    Asp.net mvc 權(quán)限過濾和單點(diǎn)登錄(禁止重復(fù)登錄)

    這篇文章主要介紹了Asp.net mvc 權(quán)限過濾和單點(diǎn)登錄(禁止重復(fù)登錄)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-12-12
  • asp.net使用jQuery Uploadify上傳附件示例

    asp.net使用jQuery Uploadify上傳附件示例

    Uploadify是JQuery的一個(gè)上傳插件,實(shí)現(xiàn)的效果非常不錯(cuò),帶進(jìn)度顯示,本文是一個(gè)簡單的介紹Demo,主要是動(dòng)態(tài)傳遞參數(shù)方法,通過formdata 向處理程序傳遞額外的表單數(shù)據(jù)
    2014-01-01

最新評論