Redis分布式锁的扩展方法
分布式锁代码
#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
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 }
原文链接:https://blog.csdn.net/qq_39788123/article/details/124500222