如何使用 Redis 實(shí)現(xiàn)分布式鎖
在多線程的環(huán)境下,為了保證一個(gè)代碼塊在同一時(shí)間只能由一個(gè)線程訪問(wèn)?,F(xiàn)在公司都是流行分布式架構(gòu),在分布式環(huán)境下,如何保證不同節(jié)點(diǎn)的線程同步執(zhí)行呢?
實(shí)際上,對(duì)于分布式場(chǎng)景,我們可以使用分布式鎖,它是控制分布式系統(tǒng)之間互斥訪問(wèn)共享資源的一種方式。對(duì)于每個(gè)鎖,最好有一個(gè)唯一id,保證不會(huì)錯(cuò)誤解鎖。(例如 :A鎖與B鎖的key相同,在A鎖過(guò)期的一瞬間,B鎖進(jìn)行解鎖,若不校驗(yàn)鎖id,會(huì)導(dǎo)致A鎖被解鎖);Redis提供了SETNX(set if not exists),僅在key不存在時(shí)插入value;Redis在2.6.12版本提供了SET函數(shù)的重載,支持僅在key不存在時(shí)插入帶有過(guò)期時(shí)間的value;雖然Redis沒(méi)有提供僅在value相同時(shí)刪除的命令,但是在2.6.0版本提供了EXAL用于執(zhí)行腳本,通過(guò)該腳本可以;僅在value相同時(shí)刪除這一功能。

下面介紹實(shí)現(xiàn)分布式鎖。
引入依賴
using ServiceStack.Redis;鎖實(shí)現(xiàn)過(guò)程
   private static readonly string ScriptSetIfAbsent = "return redis.call('SET',KEYS[1],ARGV[1],'EX',ARGV[2],'NX')";
        private static readonly string ScriptDeleteIfEqualValue = @"if redis.call('GET',KEYS[1]) == ARGV[1] then
        return redis.call('DEL',KEYS[1])
        else
        return 'FALSE'
        end";  /// <summary>
        /// 加鎖
        /// </summary>
        /// <param name="key">鎖key</param>
        /// <param name="lockToken">鎖令牌,用于釋放鎖</param>
        /// <param name="lockExpirySeconds">鎖自動(dòng)超時(shí)時(shí)間(秒)</param>
        /// <param name="waitLockSeconds">等待鎖時(shí)間(秒)</param>
        /// <returns>加鎖成功</returns>
        public bool Lock(string key, out string lockToken, int lockExpirySeconds = 10, double waitLockSeconds = 0)
        {
            int waitIntervalMs = 1000;
            string lockKey = GetLockKey(key);
            DateTime begin = DateTime.Now;
            string uuid = Guid.NewGuid().ToString();
            //循環(huán)獲取取鎖
            while (true)
            {
                string result;
                using (var client = GetNativeClient())
                {
                    //返回SET操作結(jié)果,為OK時(shí)成功
                    result = client.EvalStr(ScriptSetIfAbsent, 1,
                        System.Text.Encoding.UTF8.GetBytes(lockKey),
                        System.Text.Encoding.UTF8.GetBytes(uuid),
                        System.Text.Encoding.UTF8.GetBytes(lockExpirySeconds.ToString()));
                }
                if (result == "OK")
                {
                    lockToken = uuid;
                    return true;
                }
                //超過(guò)等待時(shí)間,則不再等待
                if ((DateTime.Now - begin).TotalSeconds >= waitLockSeconds) break;
                Thread.Sleep(waitIntervalMs);
            }
            lockToken = null;
            return false;
        }   /// <summary>
        /// 釋放鎖,執(zhí)行完代碼以后調(diào)用
        /// </summary>
        /// <param name="key">鎖Key</param>
        /// <param name="lockToken">鎖令牌</param>
        /// <returns>釋放鎖成功</returns>
        public bool DelLock(string key, string lockToken)
        {
            if (string.IsNullOrWhiteSpace(lockToken))
            {
                throw new Exception("參數(shù)lockToken不能為空");
            }
            string lockKey = GetLockKey(key);
            using (var client = GetNativeClient())
            {
                //返回刪除的行數(shù),為1時(shí)成功
                string result = client.EvalStr(ScriptDeleteIfEqualValue, 1,
                    System.Text.Encoding.UTF8.GetBytes(lockKey),
                    System.Text.Encoding.UTF8.GetBytes(lockToken));
                return result == "1";
            }
        }鎖使用過(guò)程
   if (RedisManager.Lock(key, out tokenLock))
            {
                try
                {
                    IRedisClient rdsclient = null;
                    try
                    {
                     
                    }
                    finally
                    {
                        rdsclient?.Dispose();
                    }
                }
                finally
                {
                    RedisManager.DelLock(key, tokenLock);
                }
            }














 
 
 














 
 
 
 