ASP.NET Core中借助CSRedis实现安全高效的分布式锁

引言


最近回头看了看开发的.NET Core 2.1项目的复盘总结,其中在多处用到Redis实现的分布式锁,虽然在OnResultExecuting方法中做了防止死锁的处理,但在某些场景下还是会发生死锁的问题,下面我只展示部分代码:


640?wx_fmt=png


问题:


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,我们采用时间戳来防止问题一,下面先让我们来看下想当然写法流程图。


640?wx_fmt=png


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已经有数据了,但是没有过期时间


640?wx_fmt=png


B应用:运行正常


但是B应用就会一直获取不到锁,导致死锁。


640?wx_fmt=png


所以上面在获取锁的逻辑还是有问题的,为了解决这个问题,我们采用下面的方式来处理。


三、使用Redis实现分布式锁版本二:双重防死锁


流程图:


640?wx_fmt=png


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中的情况如下:


640?wx_fmt=png


我们运行上面的代码,结果如下:


640?wx_fmt=png


副本.exe中添加一行代码。来模拟这种场景:有A、B两台服务器在跑这个应用,当A台应用跑到:setnx成功但是还没有设置过期时间的时候,突然重启服务,这个时候在分布式环境中就会发生死锁的问题,因为你没有设置过期时间


640?wx_fmt=png


我们先执行Lottery.ThriftRpc - 副本.exe,等Redis里面有值了,并且这个key是没有过期时间,再关闭掉该程序:


640?wx_fmt=png


然后,再执行Lottery.ThriftRpc.exe


640?wx_fmt=png


640?wx_fmt=png


我们是不是解决了该问题,至于过期时间设置为多少要结合你的具体业务处理时间来计算出一个合理的值,好了,聊到这里关于Redis的分布式锁就讲完了。


四、总结


上面的示例中Redis的组件用的是CSRedisCore,这里只是自己的一点体会,如果你有更好的办法,可以在评论区讨论,关于Redis的理论讲解有太多的文章了,大家可以参考,关于Redis的文章我只总结工作中遇到的一些问题,关于文章中的源码,我就不提供了,太简单了。

原文地址:http://cnblogs.com/runningsmallguo/p/10322315.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com
640?wx_fmt=jpeg


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/317359.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

L Machining Disc Rotors

L Machining Disc Rotors 题意: 圆心为(0,0)半径为R的圆,现在被被n个互不相交的圆切割(圆心和半径会给出),保证这n个彼此之间不会交叉,保证n个圆中不会有某个包含整个大圆的情况。问切割后大圆剩余部分的直径(即两点…

线性代数二之矩阵加速DP——数学作业,Arc of Dream

矩阵加速数学作业descriptionsolutioncodeArc of Dreamdescriptionsolutioncode数学作业 description solution dpdpdp状态转移方程,dpidpi−1∗10lenii(modM)dp_{i}dp_{i-1}*10^{len_i}i\pmod Mdpi​dpi−1​∗10leni​i(modM) nnn巨大,分段矩阵加速 …

Shadow Properties之美(二)【Microsoft Entity Framework Core随笔】

接着上一篇Shadow Properties之美(一),我们来继续举一个有点啰嗦的栗子。先看简单需求:某HR系统,需要记录员工资料。需要记录的资料有:员工号(规则:分公司所在城市拼音首字母&#x…

线性代数三之状压DP的矩阵加速——Quad Tiling,Bus公交线路

状压与矩阵加速的藕断丝连Quad Tilingdescriptionsolutioncode[Hnoi2010]Bus 公交线路descriptionsolutioncodeQuad Tiling description solution 设dpi,S:dp_{i,S}:dpi,S​: iii列的状态为SSS的方案数&#xff0c;最后答案为dpn,(1<<4)−1dp_{n,(1<<4)-1}dpn,(…

如何在ASP.NET Core程序启动时运行异步任务(2)

原文&#xff1a;Running async tasks on app startup in ASP.NET Core (Part 2)作者&#xff1a;Andrew Lock译者&#xff1a;Lamond Lu在我的上一篇博客中&#xff0c;我介绍了如何在ASP.NET Core应用程序启动时运行一些一次性异步任务。本篇博客将继续讨论上一篇的内容&…

线性代数四之动态DP(广义矩阵加速)——Can you answer these queries III,保卫王国

动态DP——广义矩阵加速SP1716 GSS3 - Can you answer these queries IIIdescriptionsolutioncode[NOIP2018 提高组] 保卫王国descriptionsolutioncode动态DP能矩阵加速要满足外层操作符对内层操作符具有分配率加法对于乘法就具有分配率(ab)*ca*cb*c SP1716 GSS3 - Can you a…

.Net Core跨平台应用研究-HelloArm(串口篇)

引言为了验证采用dotnet core技术开发的物联网设备数据采集接入服务应用是否能在高性价比的linux嵌入式平台运行&#xff0c;针对dotnet core应用程序进行嵌入式linux环境的发布部署运行验证研究。硬件环境硬件系统经过对比筛选&#xff0c;选用了友善之臂出品的NanoPC-T3 Plus…

[数论系列一]C Looooops,跳跳棋,The Luckiest number,CF906D Power Tower,Minimal Power of Prime,仪仗队,LCMSUM

文章目录C Looooopsdescriptionsolutioncode跳跳棋descriptionsolutioncodeThe Luckiest numberdescriptionsolutioncodeCF906D Power TowerdescriptionsolutioncodeMinimal Power of Primedescriptionsolutioncode[SDOI2008]仪仗队descriptionsolutioncodeLCMSUMdescriptionso…

.NET Core使用微软AI认知服务识别文字语言

点击上方蓝字关注“汪宇杰博客”识别一段文字的语言有多种途径&#xff0c;在这个以AI为热点的时代&#xff0c;我们也可以给自己的应用强行加上AI&#xff0c;然后就能加上“智慧”的名字“自主研发成功”后去吹牛逼。今天我带大家来看看如何使用微软智慧云Azure提供的AI认知服…

【NET CORE微服务一条龙应用】第二章 配置中心使用

背景系列目录&#xff1a;【NET CORE微服务一条龙应用】开始篇与目录在分布式或者微服务系统里&#xff0c;通过配置文件来管理配置内容&#xff0c;是一件比较令人痛苦的事情&#xff0c;再谨慎也有湿鞋的时候&#xff0c;这就是在项目架构发展的过程中&#xff0c;配置中心存…

D - ABC Conjecture Gym - 102798D

D - ABC Conjecture Gym - 102798D 题意&#xff1a; 规定rad(n)n的所有质因子的乘积 给你一个c&#xff0c;问能否构造a和b使得abc且rad(abc)<c 题解&#xff1a; 先说结论&#xff0c;如果c可以拆分出两个一样的质因子&#xff0c;则能构造a和b 即 np1a1 * p2a2 . . .…

“幕后英雄”之Backing Fields【Microsoft Entity Framework Core随笔】

刘德华 有一首歌叫《马桶》&#xff0c;其中有一句歌词是&#xff1a;每一个马桶都是英雄。EFCore也有一个英雄&#xff0c;在幕后默默地任劳任怨。它就叫 "支持字段" (Backing Fields):中文版&#xff1a;https://docs.microsoft.com/zh-cn/ef/core/modeling/backin…

数论练习二之BSGS算法——随机数生成器,Matrix,Lunar New Year and a Recursive Sequence,Fermat‘s Last Theorem

[SDOI2013] 随机数生成器 description solution 肯定是非常想找一个通项公式来表示第nnn个数的 依据形式&#xff0c;考虑化成等比数列 xi1ka(xik)a⋅xibt⇒kba−1x_{i1}ka(x_ik)ax_ibt\Rightarrow k\frac{b}{a-1}xi1​ka(xi​k)a⋅xi​bt⇒ka−1b​ ⇒xiba−1ai−1(x1ba−…

NetCore下模拟和使用Modbus工业通信协议

Tips&#xff1a;1、目前NetCore下与Modbus通信的框架主要选择了 Modbus.Net https://github.com/parallelbgls/Modbus.Net2、modbus是常用的工业通信协议&#xff0c;在软件调试时可以通过modbus pollslave模拟通信通过达到调试目的&#xff0c;下图是我使用软件1&#xff09…

C - Rencontre Gym - 102798C

C - Rencontre Gym - 102798C 参考题解&#xff1a; 参考一 参考二 题意&#xff1a; 有一棵树&#xff0c;树上的点分为三种&#xff0c;&#xff08;一个点可以为多种&#xff09;&#xff0c;现在分别在三种点中随机选一点a&#xff0c;b&#xff0c;c&#xff0c;然后找到…

API标准化成为技术团队面临的最大挑战

调查表明&#xff0c;API 标准化成为了技术团队面临的最大挑战。SmartBear 发布了 2019 年 API 状态报告“The State of API 2019”&#xff0c;此报告旨在为 API 行业建立关于软件团队在 2019 年规划、设计、开发、测试、记录和监控 API 的方法、实践和工具的基准。此次调查有…

线性代数五之高斯消元——[SDOI2010]外星千足虫,[HNOI2013]游走,[HNOI2011]XOR和路径,[hdu 4035]Maze

多类型高斯消元杂题[SDOI2010]外星千足虫descriptionsolutioncode[HNOI2013]游走descriptionsolutioncode[HNOI2011]XOR和路径descriptionsolutioncodeMaze(树上高斯消元)problemsolutioncode[SDOI2010]外星千足虫 description solution 高斯消元的模板题 虽然感觉问了个最…

Ocelot 资源汇总

前言最近一两年.NET Core的关注度持续上升, 微服务及云原生应用开发上采用.NET Core也越来越多&#xff0c;Ocelot 作为.NET Core平台下一款开源的API 网关开发库越来越得到社区的认可&#xff0c;应用到生产中的案例也有好几百了。春节抽空整理了现有网上公开的Ocelot相关的资…

[TJOI2017]城市(未解决)

[TJOI2017]城市 题意&#xff1a; 一棵树&#xff0c;现在要求你将一条边改变他的位置&#xff0c;&#xff08;即改变左右所连接的端点&#xff0c;权值不变&#xff09;&#xff0c;修改后任意两点相互可达&#xff0c;且使得两个点之间的最大交通费用最小 题解: 有O(n^2…

数论三之组合数学Ⅰ-Max-Min Sums,Binomial Coefficient is Fun,Strivore,Bubble Sort,放棋子,LOJ6671,Iroha and a Grid

组合计数我最爱Max-Min SumsdescriptionsolutioncodeBinomial Coefficient is FundescriptionsolutioncodeStrivoredescriptionsolutioncodeBubble Sortdescriptionsolutioncode[HAOI2016]放棋子descriptionsolutioncodeEntropyIncreaser 与 MinecraftdescriptionsolutioncodeD…