MVC使用Redis實現分布式鎖


使用場景

在做Web項目的時候,有很多特殊的場景要使用到鎖

比如說搶紅包,資源分配,訂單支付等場景

就拿搶紅包來說,如果一個紅包有5份,同時100個人搶如果沒有用到鎖的話

100個人同時並發都搶成功,那就出大事了

 

怎么實現鎖

class Test   
{   
    //定義一個私有成員變量,用於Lock   
    private static object lockobj = new object();   
    void DoSomething()   
    {   
        lock (lockobj)   
        {   
            //需要鎖定的代碼塊   
        }   
    }   
}  

這樣我們就可以很好的控制並發的情況,從而不出現問題

但是在項目還小的時候,可能只運行在一台服務器,一個進程的情況下

這種代碼不會出現問題

但是在部署在多台服務器,每個服務器開多個進程的情況下

.net自帶的lock鎖只能保證同一個進程在並發情況在不出現問題

而多服務器,多進程情況下。lock鎖就不能滿足我們的要求了

 

怎么實現分布式鎖

實現思路為
當我們在執行代碼前,先去設置一個分布式鎖

其實就是給Redis設置一個Key,但是要這個Key不存再的情況下才可以設置成功

如果設置成功,表示當前進程拿到鎖,可以執行后續代碼 

如果設置失敗,表示其它進程已經鎖定,那么我們就要讓當前進程休眠一下,然后再去重試設置鎖

直到設置鎖成功,才表示當前進程鎖定,才可以執行自定義代碼

在執行自寶義代碼后,釋放鎖,這樣其它進程就可以拿到鎖了

我們在設置鎖的時候,為了防此自定義代碼報錯,而出現死鎖的情況

所以我們在設置鎖的時候可以設置鎖的一個過期時間,這樣就算自定義代碼出錯,沒有釋放鎖的情況下

其它進程也可以在一定時間內拿到鎖,當然可以try,catch把自定義代碼包起來

 

代碼實現

        //TODO:拿鎖
        //定義一個鎖的Key
        var lockKey = CacheKeyFormat.AssignLockKeyFormat;
        var isLocked = false;//是否已經鎖定,默認否
        do
        {
            //使用do-while先去給Redis的lockKey隨便設置一個值
            //但是設置的條件是,如果當前lockKey存再(表示其它進程已經鎖定了)就返回false
            //如果lockKey不存再(表示當前沒有其它進程鎖定),就反回true,並且設置過期時間為600毫秒(如果進行沒有釋放,報錯死鎖的情況)
            isLocked = redisDb.StringSet(lockKey, id, TimeSpan.FromMilliseconds(600), StackExchange.Redis.When.NotExists);

            //如果isLocked反回false表示被其它進程鎖定,那么當前進程休眠200毫秒后,再去設置鎖
            //重復此動作直到當前進程拿到鎖為止
            if (!isLocked) System.Threading.Thread.Sleep(200);
        } while (!isLocked);

        //TODO:執行其它動作,to do something

        //在執行完自定義代碼后,釋放鎖
        redisDb.KeyDelete(CacheKeyFormat.AssignLockKeyFormat);

 

這樣的話,不管項目部署多少服務器,開多少個進程

我們都能保證在這個情況下,這個執行動作是一個一個執行,不會存再並發不可控的情況

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM