Redis分布式锁的扩展方法

发布时间 2023-12-15 16:46:27作者: bxzjzg

 

分布式锁代码

#region 秒杀业务测试
        private static readonly string redisConnectionStr = "127.0.0.1:6379,connectTimeout=5000,allowAdmin=false,defaultDatabase=1";
        /// <summary>
        /// 秒杀业务
        /// </summary>
        private static void TestSeckillDemo()
        {
            //模拟线程数
            var thredNumber = 20;
            //秒杀库存数
            var stockNumber = 3;

            //秒杀成功队列key
            var key = "order_queue";
            //分布式锁key
            var nxKey = "orderNX";

            var csredis = new CSRedisClient(redisConnectionStr);
            csredis.Del(key);
            var isEnd = false;

            // 创建秒杀执行信号量集合
            List<Task> taskList = new List<Task>();
            // 添加计时器
            Stopwatch stopwatch = new Stopwatch();
            // 开启
            stopwatch.Start();
            for (int i = 0; i < thredNumber; i++)
            {
                int number = i;
                taskList.Add(Task.Run(() =>
                {
                    Thread.Sleep(50);

                    if (isEnd)
                    {
                        Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId} - 用户{number} 秒杀失败,抢完了。");
                    }

                    //设置客户端的标识,用于加锁解锁
                    var nxSelfMarkvalue = $"thred{Thread.CurrentThread.ManagedThreadId}_user{number}";
                    //当前线程用户加锁
                    var setnxResult = csredis.RedisLock(nxKey, nxSelfMarkvalue, 1000);
                    if (setnxResult)
                    {
                        var len = csredis.LLen(key);//获取列表长度
                        //成功的队列长度>=库存 (库存不足)
                        if (len >= stockNumber)
                        {
                            isEnd = true;
                            stopwatch.Stop();
                            Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId} - 用户{number} 秒杀失败,抢完了。");
                            //其实库存不足了,也不用解锁了。如果这里再做解锁操作,其他线程会出现再次加锁,但是返回的还是库存不足。加不加都行
                        }
                        else
                        {
                            var value = $"线程{Thread.CurrentThread.ManagedThreadId}-用户{number}";
                            csredis.LPush(key, value);//名单添加到成功队列
                            //当前线程用户解锁 (nxSelfMarkvalue,防止误解锁)
                            csredis.RedisUnLock(nxKey, nxSelfMarkvalue);
                            Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId} - 用户{number} 秒杀成功。");
                        }
                    }
                    else
                    {
                        Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId} - 用户{number} 系统繁忙,请稍后再试。 秒杀失败。");
                    }
                }));
            }
            // 等待所有秒杀列表中任务结束
            Task.WaitAll(taskList.ToArray());
            var lenALL = csredis.LLen(key);
            Console.WriteLine($"\r\n秒杀成功人数:{lenALL} 人,用时:{stopwatch.ElapsedMilliseconds} 毫秒.");
            Console.WriteLine($"\r\n是否超售:{(lenALL > stockNumber ? "" : "")}");
            Console.WriteLine("\r\n秒杀成功人员名单:");
            for (int i = 0; i < stockNumber; i++)
            {
                Console.WriteLine(csredis.RPop(key));
            }
        }
        #endregion
View Code

 

 

redis的扩展方法,一个加锁一个解锁的操作

/// <summary>
    /// Redis分布式锁
    /// </summary>
    public static class RedisDistributedLockExtension
    {

        #region 推荐使用
        /// <summary>
        /// 加锁毫秒级
        /// </summary>
        /// <param name="client">redis客户端连接</param>
        /// <param name="key">锁key</param>
        /// <param name="value">锁值</param>
        /// <param name="expireMilliSeconds">缓存时间 单位/毫秒 默认1000毫秒</param>
        /// <returns></returns>
        public static bool RedisLock(this CSRedisClient client, string key, object value, int expireMilliSeconds = 1000)
        {
            var script = @"local isNX = redis.call('SETNX', KEYS[1], ARGV[1])
                           if isNX == 1 then
                               redis.call('PEXPIRE', KEYS[1], ARGV[2])
                               return 1
                           end
                           return 0";

            return client.Eval(script, key, value, expireMilliSeconds)?.ToString() == "1";
        }

        /// <summary>
        /// 解锁
        /// </summary>
        /// <param name="client">redis客户端连接</param>
        /// <param name="key">锁key</param>
        /// <param name="selfMark">对应加锁客户端标识</param>
        /// <returns></returns>
        public static bool RedisUnLock(this CSRedisClient client, string key, string selfMark)
        {
            var script = @"local getLock = redis.call('GET', KEYS[1])
                            if getLock == ARGV[1] then
                              redis.call('DEL', KEYS[1])
                              return 1
                            end
                            return 0";

            return client.Eval(script, key, selfMark)?.ToString() == "1";
        }

        #endregion

    }
View Code


原文链接:https://blog.csdn.net/qq_39788123/article/details/124500222