1.采用redis分布式锁
using RedLockNet.SERedis; using ServiceStack.Redis; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace DotnetDocker.Models { public class RedisUtitily { public static string RedisServerIp = "192.168.0.107"; public static int RedisServerPort = 6379; public static object LockObject = new object(); public static string keylock = "redislock"; public static RedLockFactory redLockFactory = RedLockUtitily.GetRedlockFactory(); public static RedisClient GetClient() { return new RedisClient(RedisServerIp, RedisServerPort); } public static void Test() { var lockValue = Guid.NewGuid().ToString() + Thread.CurrentThread.ManagedThreadId; //var rLock = redLockFactory.CreateLock(keylock, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(20)); try { using (RedisClient client = GetClient()) { var flag = client.SetValueIfNotExists(keylock, lockValue, TimeSpan.FromSeconds(60)); if (flag) { //var a = redisClient.Get("test123"); var count = client.Get<int>("good:001"); if (count <= 0) { Console.WriteLine("good:001商品已经卖光"); } else { client.Decr("good:001"); Console.WriteLine($"第{count}件商品被购买"); } } } } catch (Exception ex) { Console.WriteLine($"Error:{ex.Message}"); } finally { using (RedisClient client = GetClient()) { try { var lua = $"if redis.call(\"get\", KEYS[1]) == ARGV[1] then" + " return redis.call(\"del\", KEYS[1])" + " else" + " return 0" + " end"; int res = (int)client.ExecLuaAsInt(lua, keys: new string[] { keylock }, args: new string[] { lockValue }); if (res == 1) { Console.WriteLine($"删除key成功{lockValue}"); } else { Console.WriteLine($"删除key失败{lockValue}"); } } catch (Exception ex) { Console.WriteLine($"error:{ex.Message}"); } } //rLock.Dispose(); //redLockFactory.Dispose(); } } public static void TestRedLock() { var lockValue = Guid.NewGuid().ToString() + Thread.CurrentThread.ManagedThreadId; //expire 锁过期时间 //wait 线程等待时间,如果更呆了wait时间还没有获得锁,放弃 //retry //每隔多长时间试着获取锁 var rLock = redLockFactory.CreateLock(keylock, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(20)); try { using (RedisClient client = GetClient()) { if (rLock.IsAcquired) { //var a = redisClient.Get("test123"); var count = client.Get<int>("good:001"); if (count <= 0) { Console.WriteLine("good:001商品已经卖光"); } else { client.Decr("good:001"); Console.WriteLine($"第{count}件商品被购买"); } } } } catch (Exception ex) { Console.WriteLine($"Error:{ex.Message}"); } finally { rLock.Dispose(); Console.WriteLine($"finally {lockValue}"); } } } }
采用redlock.net是,需要因拥抱并写一个创建锁的工厂类
using RedLockNet.SERedis; using RedLockNet.SERedis.Configuration; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; namespace DotnetDocker.Models { public class RedLockUtitily { public static RedLockFactory GetRedlockFactory() { var endPoints = new List<RedLockEndPoint>(); endPoints.Add(new DnsEndPoint("192.168.0.107",6379)); return RedLockFactory.Create(endPoints); } } }
当同时有100个线程时,要注意线程变量。线程变量作用域只在本线程,线程间不可共享
所以,下面的keyLock值不同线程是不同的。
秒杀可以用下面这种lua脚本,不过多线程会出现超卖
public static void SecKill(int j) { for (int i = 0; i < 10; i++) { using (RedisClient client = new RedisClient("192.168.145.128", 6379)) { //var num = client.Decr("number"); var num = client.Get<int>("number"); if (num > 0) { var lua = @"local count = redis.call('get',KEYS[1]) if(tonumber(count) > 0) then redis.call('INCR',ARGV[1]) return redis.call('DECR',KEYS[1]) else return -1 end "; var result = client.ExecLuaAsInt(lua, keys: new string[] { "number"}, args: new string[] { "sellnumber" }); var leftnum = client.Get<int>("number"); if (result == -1) { Console.WriteLine($"{j}抢购失败,10个买完了-{result}"); } else { Console.WriteLine($"{j}抢购成功,TaskId:{Task.CurrentId},ThreadId: {Thread.CurrentThread.ManagedThreadId}-{result}"); } //client.Set<int>("number", --num); } else { Console.WriteLine($"{j}抢购失败"); } }; } }