地下城钓鱼网站如何做,如何自己注册网站,怎么查一个网站的外链,深圳市8号公告最新消息引言最近回头看了看开发的.NET Core 2.1项目的复盘总结#xff0c;其中在多处用到Redis实现的分布式锁#xff0c;虽然在OnResultExecuting方法中做了防止死锁的处理#xff0c;但在某些场景下还是会发生死锁的问题#xff0c;下面我只展示部分代码#xff1a;问题#x… 引言最近回头看了看开发的.NET Core 2.1项目的复盘总结其中在多处用到Redis实现的分布式锁虽然在OnResultExecuting方法中做了防止死锁的处理但在某些场景下还是会发生死锁的问题下面我只展示部分代码问题1、这里setnx设置的值“1”我想问你最后del的这个值一定是你自己创建的吗2、图中标注的步骤1和步骤2不是原子操作会有死锁的概率吗大家可以思考一下先下面让我们带着这两个问题往下看下面介绍一下使用Redis实现分布式锁常用的几个命令。一、使用Redis实现分布式锁常见的几个命令► Setnx命令SETNX key value说明将 key 的值设为 value 当且仅当 key 不存在。若给定的 key 已经存在则 SETNX 不做任何动作。SETNX 是『SET if Not eXists』(如果不存在则 SET)的简写。时间复杂度O(1)返回值设置成功返回1 设置失败返回 0► Getset命令GETSET key value说明将给定 key 的值设为 value 并返回 key 的旧值(old value)。当 key 存在但不是字符串类型时返回一个错误。时间复杂度O(1)返回值返回给定 key 的旧值; 当 key 没有旧值时也即是 key 不存在时返回 nil 。► Expire命令EXPIRE key seconds说明为给定 key 设置生存时间当 key 过期时(生存时间为 0 )它会被自动删除。时间复杂度O(1)返回值设置成功返回 1 当 key 不存在或者不能为 key 设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的生存时间)返回 0 。► Del命令DEL key [key ...]说明删除给定的一个或多个 key 。不存在的 key 会被忽略。时间复杂度O(N); N 为被删除的 key 的数量。删除单个字符串类型的 key 时间复杂度为O(1)。删除单个列表、集合、有序集合或哈希表类型的 key 时间复杂度为O(M) M 为以上数据结构内的元素数量。返回值被删除 key 的数量。好了命令熟悉之后下面我们就开始一步一步实现分布式锁。二、使用Redis实现分布式锁版本一与时间戳的结合对于上面的setnx设置的默认值1我们采用时间戳来防止问题一下面先让我们来看下想当然写法流程图。C#代码实现static void Main(string[] args){ var lockTimeout 5000;//单位是毫秒 var currentTime DateTime.Now.ToUnixTime(true); if (SetNx(lockkey, currentTime lockTimeout,lockTimeout)) { //TODO:一些业务逻辑代码 //..... //..... //最后释放锁 Remove(lockkey); } else { Console.WriteLine(没有获得分布式锁); } Console.ReadKey();}public static bool SetNx(string key,long time ,double expireMS){ if (redisClient.SetNx(key, time)) { if (expireMS 0) redisClient.Expire(key, TimeSpan.FromMilliseconds(expireMS)); return true; } return false;}public static bool Remove(string key){ return redisClient.Del(key) 0;}上面的代码中value的值我们使用时间戳不是一个固定的值了至少能保证你删除的key确实是你自己的所以建议大家在设value的值时不要设置一个固定的值最好是随机的。但是这样写虽然解决了问题一但是这种写法还是存在一定的风险虽然Redis是单线程的并且setnx、expire是原子操作但是先setnx再expire就不是原子操作了我们要考虑多线程环境和容器部署时多实例环境等等那这样的写法就会出现问题。比如现在有A、B两台服务器在跑这个应用当A台应用跑到setnx成功但是还没有设置过期时间的时候突然重启服务这个时候在分布式环境中就会发生死锁的问题因为你没有设置过期时间。下面我们通过调试来展示死锁的场景A应用在执行到setnx成功但是在执行expire之前宕机了此时的Redis已经有数据了但是没有过期时间B应用运行正常但是B应用就会一直获取不到锁导致死锁。所以上面在获取锁的逻辑还是有问题的为了解决这个问题我们采用下面的方式来处理。三、使用Redis实现分布式锁版本二双重防死锁流程图C#代码实现public static void RedisLockV2(){ var lockTimeout 5000;//单位是毫秒 var currentTime DateTime.Now.ToUnixTime(true); if (SetNxV2(lockkey,DateTime.Now.ToUnixTime(true)lockTimeout)) { //设置过期时间 redisClient.Expire(lockkey, TimeSpan.FromMilliseconds(5000)); //TODO:一些业务逻辑代码 Console.WriteLine(处理业务ing); Thread.Sleep(100000); Console.WriteLine(处理业务ed); //最后释放锁 Remove(lockkey); } else { //未获取到锁继续判断判断时间戳看看是否可以重置并获取锁 var lockValue redisClient.Get(lockkey); var time DateTime.Now.ToUnixTime(true); if (!string.IsNullOrEmpty(lockValue) time lockValue.ToInt64()) { //再次用当前时间戳getset //返回固定key的旧值旧值判断是否可以获取锁 var getsetResult redisClient.GetSet(lockkey, time); if (getsetResult null || (getsetResult ! null getsetResult lockValue)) { Console.WriteLine(获取到Redis锁了); //真正获取到锁 redisClient.Expire(lockkey, TimeSpan.FromMilliseconds(5000)); //TODO:一些业务逻辑代码 //..... //..... Console.WriteLine(处理业务); //最后释放锁 Remove(lockkey); } else { Console.WriteLine(没有获取到锁); } } else { Console.WriteLine(没有获取到锁); } }}现在Redis中的情况如下我们运行上面的代码结果如下副本.exe中添加一行代码。来模拟这种场景有A、B两台服务器在跑这个应用当A台应用跑到setnx成功但是还没有设置过期时间的时候突然重启服务这个时候在分布式环境中就会发生死锁的问题因为你没有设置过期时间我们先执行Lottery.ThriftRpc - 副本.exe等Redis里面有值了并且这个key是没有过期时间再关闭掉该程序然后再执行Lottery.ThriftRpc.exe我们是不是解决了该问题至于过期时间设置为多少要结合你的具体业务处理时间来计算出一个合理的值好了聊到这里关于Redis的分布式锁就讲完了。四、总结上面的示例中Redis的组件用的是CSRedisCore这里只是自己的一点体会如果你有更好的办法可以在评论区讨论关于Redis的理论讲解有太多的文章了大家可以参考关于Redis的文章我只总结工作中遇到的一些问题关于文章中的源码我就不提供了太简单了。原文地址http://cnblogs.com/runningsmallguo/p/10322315.html.NET社区新闻深度好文欢迎访问公众号文章汇总 http://www.csharpkit.com