本地锁、redis分布式锁、zk分布式锁

https://www.cnblogs.com/yjq-code/p/dotnetlock.html

为什么要用锁?

  大型站点在高并发的情况下,为了保持数据最终一致性就需要用到技术方案来支持。比如:分布式锁、分布式事务。有时候我们在为了保证某一个方法每次只能被一个调用者使用的时候,这时候我们也可以锁来实现。

基于本地缓存实现锁

  为什么还要写基于本地缓存实现的锁呢,因为有些项目项目可能还是单机部署的,当随着业务量增长的时候就会变成多机部署,从单机到多机的切换过程中,我们也需要把原先业务相关的锁改成分布式锁,来保持数据的最终一致性。当然项目是使用ioc的那就更好了,切换注册时的实现类就完成了切换,非常方便。

实现思路:

  用户需要用一个key和一个唯一的值(知道当前这个key的使用者是谁)来获取一个锁,获取到锁之后,执行完对应的操作然后释放掉。在释放锁的时候 4我们需要判断下当前这个锁的使用者对应的值与想要释放传递过来的值是不是相等,如果相等则可以释放,不相等则不释放。

实现代码:

public sealed class LocalLock : ILock

{

private static ConcurrentDictionary<string, object> _LockCache = new ConcurrentDictionary<string, object>();

private static ConcurrentDictionary<string, string> _LockUserCache = new ConcurrentDictionary<string, string>();

  1. /// <summary>
  2. /// 获取一个锁(需要自己释放)
  3. /// </summary>
  4. /// <param name="key">锁的键</param>
  5. /// <param name="value">当前占用值</param>
  6. /// <param name="span">耗时时间</param>
  7. /// <returns>成功返回true</returns>
  8. public bool LockTake(string key, string value, TimeSpan span)
  9. {
  10. EnsureUtil.NotNullAndNotEmpty(key, "Lockkey");
  11. EnsureUtil.NotNullAndNotEmpty(value, "Lockvalue");
  12. var obj = _LockCache.GetValue(key, () => { return new object(); });
  13. if (Monitor.TryEnter(obj, span))
  14. {
  15. _LockUserCache[key] = value;
  16. return true;
  17. }
  18. return false;
  19. }
  20. /// <summary>
  21. /// 异步获取一个锁(需要自己释放)
  22. /// </summary>
  23. /// <param name="key">锁的键</param>
  24. /// <param name="value">当前占用值</param>
  25. /// <param name="span">耗时时间</param>
  26. /// <returns>成功返回true</returns>
  27. public Task<bool> LockTakeAsync(string key, string value, TimeSpan span)
  28. {
  29. return Task.FromResult(LockTake(key, value, span));
  30. }
  31. /// <summary>
  32. /// 释放一个锁
  33. /// </summary>
  34. /// <param name="key">锁的键</param>
  35. /// <param name="value">当前占用值</param>
  36. /// <returns>成功返回true</returns>
  37. public bool LockRelease(string key, string value)
  38. {
  39. EnsureUtil.NotNullAndNotEmpty(key, "Lockkey");
  40. EnsureUtil.NotNullAndNotEmpty(value, "Lockvalue");
  41. _LockCache.TryGetValue(key, out object obj);
  42. if (obj != null)
  43. {
  44. if (_LockUserCache[key] == value)
  45. {
  46. Monitor.Exit(obj);
  47. return true;
  48. }
  49. return false;
  50. }
  51. return true;
  52. }
  53. /// <summary>
  54. /// 异步释放一个锁
  55. /// </summary>
  56. /// <param name="key">锁的键</param>
  57. /// <param name="value">当前占用值</param>
  58. /// <returns>成功返回true</returns>
  59. public Task<bool> LockReleaseAsync(string key, string value)
  60. {
  61. return Task.FromResult(LockRelease(key, value));
  62. }
  63. /// <summary>
  64. /// 使用锁执行一个方法
  65. /// </summary>
  66. /// <param name="key">锁的键</param>
  67. /// <param name="value">当前占用值</param>
  68. /// <param name="span">耗时时间</param>
  69. /// <param name="executeAction">要执行的方法</param>
  70. public void ExecuteWithLock(string key, string value, TimeSpan span, Action executeAction)
  71. {
  72. if (executeAction == null) return;
  73. if (LockTake(key, value, span))
  74. {
  75. try
  76. {
  77. executeAction();
  78. }
  79. finally
  80. {
  81. LockRelease(key, value);
  82. }
  83. }
  84. }
  85. /// <summary>
  86. /// 使用锁执行一个方法
  87. /// </summary>
  88. /// <typeparam name="T">返回值类型</typeparam>
  89. /// <param name="key">锁的键</param>
  90. /// <param name="value">当前占用值</param>
  91. /// <param name="span">耗时时间</param>
  92. /// <param name="executeAction">要执行的方法</param>
  93. /// <param name="defaultValue">默认返回</param>
  94. /// <returns></returns>
  95. public T ExecuteWithLock<T>(string key, string value, TimeSpan span, Func<T> executeAction, T defaultValue = default(T))
  96. {
  97. if (executeAction == null) return defaultValue;
  98. if (LockTake(key, value, span))
  99. {
  100. try
  101. {
  102. return executeAction();
  103. }
  104. finally
  105. {
  106. LockRelease(key, value);
  107. }
  108. }
  109. return defaultValue;
  110. }
  111. /// <summary>
  112. /// 使用锁执行一个异步方法
  113. /// </summary>
  114. /// <param name="key">锁的键</param>
  115. /// <param name="value">当前占用值</param>
  116. /// <param name="span">耗时时间</param>
  117. /// <param name="executeAction">要执行的方法</param>
  118. public async Task ExecuteWithLockAsync(string key, string value, TimeSpan span, Func<Task> executeAction)
  119. {
  120. if (executeAction == null) return;
  121. if (await LockTakeAsync(key, value, span))
  122. {
  123. try
  124. {
  125. await executeAction();
  126. }
  127. catch
  128. {
  129. throw;
  130. }
  131. finally
  132. {
  133. LockRelease(key, value);
  134. }
  135. }
  136. }
  137. /// <summary>
  138. /// 使用锁执行一个异步方法
  139. /// </summary>
  140. /// <typeparam name="T">返回值类型</typeparam>
  141. /// <param name="key">锁的键</param>
  142. /// <param name="value">当前占用值</param>
  143. /// <param name="span">耗时时间</param>
  144. /// <param name="executeAction">要执行的方法</param>
  145. /// <param name="defaultValue">默认返回</param>
  146. /// <returns></returns>
  147. public async Task<T> ExecuteWithLockAsync<T>(string key, string value, TimeSpan span, Func<Task<T>> executeAction, T defaultValue = default(T))
  148. {
  149. if (executeAction == null) return defaultValue;
  150. if (await LockTakeAsync(key, value, span))
  151. {
  152. try
  153. {
  154. return await executeAction();
  155. }
  156. catch
  157. {
  158. throw;
  159. }
  160. finally
  161. {
  162. LockRelease(key, value);
  163. }
  164. }
  165. return defaultValue;
  166. }
  167. }

class Program

{

static void Main(string[] args)

{

ILock localLock = new LocalLock();

int excuteCount = 0;

Parallel.For(0, 10000, i =>

{

localLock.ExecuteWithLock("test", Guid.NewGuid().ToString(), TimeSpan.FromSeconds(5), () =>

{

Console.WriteLine("获取锁成功");

Interlocked.Increment(ref excuteCount);

});

});

Console.WriteLine("成功次数:" + excuteCount.ToString());

Console.WriteLine("执行完成");

Console.ReadLine();

}

}

基于zk实现的分布式锁

实现思路:

在获取锁的时候在固定节点下创建一个自增的临时节点,然后获取节点列表,按照增量排序,假如当前创建的节点是排在第一个的,那就表明这个节点是得到了执行的权限,假如在它前面还有其它节点,那么就对它的上一个节点进行监听,等到上一个节点被删除了,那么该节点就得到了执行的权限了。

由于代码片段太多,待会再github自行查看实现过程。zk获取锁的速度比较慢,导致有几个可能是失败的。

基于redis的实现

实现思路:

利用redis的setnx(key, value):“set if not exits”,若该key-value不存在,则成功加入缓存并且返回1,否则返回0。在有效时间内如果设置成功则获取执行限权,没有那就获取权限失败。

对比下会发现redis的执行效率会比zk的快一点。

项目下载地址:https://github.com/ProjectSharing/Lock

本地锁、redis分布式锁、zk分布式锁的更多相关文章

  1. .net下 本地锁、redis分布式锁、zk分布式锁的实现

    为什么要用锁? 大型站点在高并发的情况下,为了保持数据最终一致性就需要用到技术方案来支持.比如:分布式锁.分布式事务.有时候我们在为了保证某一个方法每次只能被一个调用者使用的时候,这时候我们也可以锁来 ...

  2. Redis与Zookeeper实现分布式锁的区别

    Redis实现分布式锁 1.根据lockKey区进行setnx(set not exist,如果key值为空,则正常设置,返回1,否则不会进行设置并返回0)操作,如果设置成功,表示已经获得锁,否则并没 ...

  3. 分布式交易系统的并发处理, 以及用Redis和Zookeeper实现分布式锁

    交易系统 交易系统的数据结构 支付系统API通常需要一个“订单号”作为入参, 而实际调用API接口时使用到的往往不是真正意义的业务订单号, 而是交易订单号.  支付系统的API会使用“商户号+订单号” ...

  4. 【zookeeper】Apache curator的使用及zk分布式锁实现

    上篇,本篇主要讲Apache开源的curator的使用,有了curator,利用Java对zookeeper的操作变得极度便捷. 其实在学之前我也有个疑虑,我为啥要学curator,撇开涨薪这些外在的 ...

  5. 【连载】redis库存操作,分布式锁的四种实现方式[四]--基于Redis lua脚本机制实现分布式锁

    一.redis lua介绍 Redis 提供了非常丰富的指令集,但是用户依然不满足,希望可以自定义扩充若干指令来完成一些特定领域的问题.Redis 为这样的用户场景提供了 lua 脚本支持,用户可以向 ...

  6. 【连载】redis库存操作,分布式锁的四种实现方式[二]--基于Redisson实现分布式锁

    一.redisson介绍 redisson实现了分布式和可扩展的java数据结构,支持的数据结构有:List, Set, Map, Queue, SortedSet, ConcureentMap, L ...

  7. 【连载】redis库存操作,分布式锁的四种实现方式[一]--基于zookeeper实现分布式锁

    一.背景 在电商系统中,库存的概念一定是有的,例如配一些商品的库存,做商品秒杀活动等,而由于库存操作频繁且要求原子性操作,所以绝大多数电商系统都用Redis来实现库存的加减,最近公司项目做架构升级,以 ...

  8. 【分布式锁的演化】终章!手撸ZK分布式锁!

    前言 这应该是分布式锁演化的最后一个章节了,相信很多小伙伴们看完这个章节之后在应对高并发的情况下,如何保证线程安全心里肯定也会有谱了.在实际的项目中也可以参考一下老猫的github上的例子,当然代码没 ...

  9. Redis、Zookeeper实现分布式锁——原理与实践

    Redis与分布式锁的问题已经是老生常谈了,本文尝试总结一些Redis.Zookeeper实现分布式锁的常用方案,并提供一些比较好的实践思路(基于Java).不足之处,欢迎探讨. Redis分布式锁 ...

随机推荐

  1. POJ 1006-Biorhythms,中国剩余定理,学信安的路过!

                                                       Biorhythms 我竟然1A了, 终于从一天的浑噩中找回点自信了.人生第一次做中国剩余定理的题 ...

  2. 七牛云赵之健:多维度融合赋能视频 AI 的实践

    6 月 30 日下午,赵之健在七牛架构师实践日第二十九期进行了<多维度融合赋能视频 AI 的实践>为题的实战分享. 
 作者简介: 
  赵之健,七牛人工智能实验室资深算法工程师, 七 ...

  3. poj 1780 , poj 1392 欧拉回路求前后相互衔接的数字串

    两道题目意思差不多 第一题是10进制 , 第二题是2进制的 都是利用欧拉回路的fleury算法来解决 因为我总是希望小的排在前面,所以我总是先将较小数加入栈,再利用另一个数组接收答案,但是这里再从栈中 ...

  4. linux 常见名词及命令(一)

    linux  PK  wondows 稳定且有效率.免费或少许费用.漏洞少且修补快.多任务多用户. 安全的用户及文件权限策略.适合小内核程序的嵌入系统.相对不耗资源. 热门的开源系统 红帽企业系统(R ...

  5. CDN是什么与CDN加速的原理

    CDN是什么 CDN全称:Content Delivery Network或Content Ddistribute Network,即内容分发网络 CDN设计思路 避让:尽可能避开互联网上有可能影响数 ...

  6. Go和HTTPS(TLS)

    原文链接: http://studygolang.com/wr?u=http%3a%2f%2ftonybai.com%2f2015%2f04%2f30%2fgo-and-https%2f 近期在构思一 ...

  7. burpsuite破解版

    来源:http://www.vuln.cn/8847

  8. topcoder srm 610

    div1 250pt: 题意:100*100的01矩阵,找出来面积最大的“类似国际象棋棋盘”的子矩阵. 解法:枚举矩阵宽(水平方向)的起点和终点,然后利用尺取法来找到每个固定宽度下的最大矩阵,不断更新 ...

  9. dva/dynamic

    1.安装: yarn add dva 2.引入: import dynamic from 'dva/dynamic'; * dva路由跳转 * dynamic(app, model, componen ...

  10. AES算法加密java实现

    package cn.itcast.coderUtils; import java.security.Key; import javax.crypto.Cipher; import javax.cry ...