分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,这个时候,便需要使用到分布式锁。

出处:http://www.cnblogs.com/chejiangyi/p/4938400.html

分布式锁

经常用于在解决分布式环境下的业务一致性和协调分布式环境。

实际业务场景中,比如说解决并发一瞬间的重复下单,重复确认收货,重复发现金券等。

使用分布式锁的场景一般不能太多。

开源地址:http://git.oschina.net/chejiangyi/XXF.BaseService.DistributedLock

开源相关群: .net 开源基础服务 238543768

这里整理了C#.net关于redis分布式锁和zookeeper分布式锁的实现,仅用于研究。(可能有bug)

采用ServiceStack.Redis实现Redis分布式锁

  1.  
1
  
  1. /*
  2. * Redis分布式锁
  3. * 采用ServiceStack.Redis实现的Redis分布式锁
  4. * 详情可阅读其开源代码
  5. * 备注:不同版本的 ServiceStack.Redis 实现reidslock机制不同 xxf里面默认使用2.2版本
  6. */ public class RedisDistributedLock : BaseRedisDistributedLock
  7. {
  8. private ServiceStack.Redis.RedisLock _lock;
  9. private RedisClient _client;
  10. public RedisDistributedLock(string redisserver, string key)
  11. : base(redisserver, key)
  12. {
  13.  
  14. }
  15.  
  16. public override LockResult TryGetDistributedLock(TimeSpan? getlockTimeOut, TimeSpan? taskrunTimeOut)
  17. {
  18. if (lockresult == LockResult.Success)
  19. throw new DistributedLockException("检测到当前锁已获取");
  20. _client = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient();
  21. /* * 阅读源码发现当其获取锁后,redis连接资源会一直占用,知道获取锁的资源释放后,连接才会跳出,可能会导致连接池资源的浪费。 */
  22.  
  23. try {
  24. this._lock = new ServiceStack.Redis.RedisLock(_client, key, getlockTimeOut);
  25. lockresult = LockResult.Success;
  26. }
  27. catch (Exception exp)
  28. {
  29. XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁系统级别严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
  30. lockresult = LockResult.LockSystemExceptionFailure;
  31. }
  32. return lockresult;
  33. }
  34.  
  35. public override void Dispose()
  36. {
  37. try {
  38. if (this._lock != null)
  39. this._lock.Dispose();
  40. if (_client != null)
  41. this._client.Dispose();
  42. }
  43. catch (Exception exp)
  44. {
  45. XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁释放严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
  46. }
  47. }
  48. }

来自网络的java实现Redis分布式锁(C#版)

  1. /*
  2. * Redis分布式锁
  3. * 采用网络上java实现的Redis分布式锁
  4. * 参考 http://www.blogjava.net/hello-yun/archive/2014/01/15/408988.html
  5. * 详情可阅读其开源代码
  6. */ public class RedisDistributedLockFromJava : BaseRedisDistributedLock
  7. {
  8.  
  9. public RedisDistributedLockFromJava(string redisserver, string key)
  10. : base(redisserver, key)
  11. {
  12.  
  13. }
  14.  
  15. public override LockResult TryGetDistributedLock(TimeSpan? getlockTimeOut, TimeSpan? taskrunTimeOut)
  16. {
  17. if (lockresult == LockResult.Success)
  18. throw new DistributedLockException("检测到当前锁已获取");
  19. try {
  20. // 1. 通过SETNX试图获取一个lock
             string @lock = key;
  21. long taskexpiredMilliseconds = (taskrunTimeOut != null ? (long)taskrunTimeOut.Value.TotalMilliseconds : (long)DistributedLockConfig.MaxLockTaskRunTime);
  22. long getlockexpiredMilliseconds = (getlockTimeOut != null ? (long)getlockTimeOut.Value.TotalMilliseconds : 0);
  23. long hassleepMilliseconds = 0;
  24. while (true)
  25. {
  26. using (var redisclient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
  27. {
  28. long value = CurrentUnixTimeMillis() + taskexpiredMilliseconds + 1;
  29. /*Java以前版本都是用SetNX,但是这种是无法设置超时时间的,不是很理解为什么,
                  * 可能是因为原来的redis命令比较少导致的?现在用Add不知道效果如何.
                    因对redis细节不了解,但个人怀疑若异常未释放锁经常发生,可能会导致内存逐步溢出*/
  30.  
  31.               bool acquired = redisclient.Add<long>(@lock, value, TimeSpan.FromMilliseconds(taskexpiredMilliseconds + DistributedLockConfig.TaskLockDelayCleepUpTime));
  32. //SETNX成功,则成功获取一个锁
  33. if (acquired == true)
  34. {
  35. lockresult = LockResult.Success;
  36. }
  37. //SETNX失败,说明锁仍然被其他对象保持,检查其是否已经超时
                   else
                   {
  38. var oldValueBytes = redisclient.Get(@lock);
  39. //超时
  40. if (oldValueBytes != null && BitConverter.ToInt64(oldValueBytes, 0) < CurrentUnixTimeMillis())
  41. {
  42. /*此处虽然重设并获取锁,但是超时时间可能被覆盖,故重设超时时间;若有进程一直在尝试获取锁,那么锁存活时间应该被延迟*/
  43.  
  44.                   var getValueBytes = redisclient.GetSet(@lock, BitConverter.GetBytes(value));
  45. var o1 = redisclient.ExpireEntryIn(@lock, TimeSpan.FromMilliseconds(taskexpiredMilliseconds + DistributedLockConfig.TaskLockDelayCleepUpTime));//这里如果程序异常终止,依然会有部分锁未释放的情况。 // 获取锁成功 if (getValueBytes == oldValueBytes)
  46. {
  47. lockresult = LockResult.Success;
  48. }
  49. // 已被其他进程捷足先登了
  50. else
  51. {
  52. lockresult = LockResult.GetLockTimeOutFailure;
  53. }
  54. }
  55. //未超时,则直接返回失败
  56. else
  57. {
  58. lockresult = LockResult.GetLockTimeOutFailure;
  59. }
  60. }
  61. }
  62.  
  63. //成功拿到锁
  64. if (lockresult == LockResult.Success)
  65. break;
  66.  
  67. //获取锁超时
  68. if (hassleepMilliseconds >= getlockexpiredMilliseconds)
  69. {
  70. lockresult = LockResult.GetLockTimeOutFailure;
  71. break;
  72. }
  73.  
  74. //继续等待
  75. System.Threading.Thread.Sleep(DistributedLockConfig.GetLockFailSleepTime);
  76. hassleepMilliseconds += DistributedLockConfig.GetLockFailSleepTime;
  77. }
  78. }
  79. catch (Exception exp)
  80. {
  81. XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁系统级别严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
  82. lockresult = LockResult.LockSystemExceptionFailure;
  83. }
  84. return lockresult;
  85. }
  86.  
  87. private long CurrentUnixTimeMillis()
  88. {
  89. return (long)(System.DateTime.UtcNow - new System.DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc)).TotalMilliseconds;
  90. }
  91.  
  92. public override void Dispose()
  93. {
  94. if (lockresult == LockResult.Success || lockresult == LockResult.LockSystemExceptionFailure)
  95. {
  96. try {
  97. long current = CurrentUnixTimeMillis();
  98. using (var redisclient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
  99. {
  100. var v = redisclient.Get(key);
  101. if (v != null)
  102.  
  103. {
  104. // 避免删除非自己获取得到的锁
  105. if (current < BitConverter.ToInt64(v, 0))
  106. {
  107. redisclient.Del(key);
  108. }
  109. }
  110. }
  111. }
  112. catch (Exception exp)
  113. {
  114. XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁释放严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
  115. }
  116. }
  117. }
  118. }

ServiceStack.Redis内部实现版本(较旧)

1
  
  1. /*
  2. * Redis分布式锁
  3. * 采用ServiceStack.Redis实现的Redis分布式锁
  4. * 详情可阅读其开源代码
  5. * 备注:不同版本的 ServiceStack.Redis 实现reidslock机制不同
  6. * 拷贝自网络开源代码 较旧的实现版本
  7. */ public class RedisDistributedLockFromServiceStack : BaseRedisDistributedLock
  8. {
  9. public RedisDistributedLockFromServiceStack(string redisserver, string key)
  10. : base(redisserver, key)
  11. {
  12.  
  13. }
  14. public override LockResult TryGetDistributedLock(TimeSpan? getlockTimeOut, TimeSpan? taskrunTimeOut)
  15. {
  16. if (lockresult == LockResult.Success)
  17. throw new DistributedLockException("检测到当前锁已获取");
  18. try
  19. {
  20. using (var redisClient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
  21. {
  22. ExecExtensions.RetryUntilTrue(
  23. () =>
  24. {
  25. //This pattern is taken from the redis command for SETNX http://redis.io/commands/setnx
  26. //Calculate a unix time for when the lock should expire
                      TimeSpan realSpan = taskrunTimeOut ?? TimeSpan.FromMilliseconds(DistributedLockConfig.MaxLockTaskRunTime); //new TimeSpan(365, 0, 0, 0); //if nothing is passed in the timeout hold for a year DateTime expireTime = DateTime.UtcNow.Add(realSpan);
  27. string lockString = (expireTime.ToUnixTimeMs() + 1).ToString();
  28.  
  29. //Try to set the lock, if it does not exist this will succeed and the lock is obtained
    var nx = redisClient.SetEntryIfNotExists(key, lockString);
  30. if (nx)
  31. {
  32. lockresult = LockResult.Success;
  33. return true;
  34. }
  35.  
  36. //If we've gotten here then a key for the lock is present. This could be because the lock is
    //correctly acquired or it could be because a client that had acquired the lock crashed (or didn't release it properly).
    //Therefore we need to get the value of the lock to see when it should expire
  37. redisClient.Watch(key);
  38. string lockExpireString = redisClient.Get<string>(key);
  39. long lockExpireTime;
  40. if (!long.TryParse(lockExpireString, out lockExpireTime))
  41. {
  42. redisClient.UnWatch(); // since the client is scoped externally
                          lockresult = LockResult.GetLockTimeOutFailure;
  43. return false;
  44. }
  45.  
  46. //If the expire time is greater than the current time then we can't let the lock go yet
                       if (lockExpireTime > DateTime.UtcNow.ToUnixTimeMs())
  47. {
  48. redisClient.UnWatch(); // since the client is scoped externally
                         lockresult = LockResult.GetLockTimeOutFailure;
  49. return false;
  50. }
  51.  
  52. //If the expire time is less than the current time then it wasn't released properly and we can attempt to //acquire the lock. The above call to Watch(_lockKey) enrolled the key in monitoring, so if it changes //before we call Commit() below, the Commit will fail and return false, which means that another thread //was able to acquire the lock before we finished processing. using (var trans = redisClient.CreateTransaction()) // we started the "Watch" above; this tx will succeed if the value has not moved {
  53. trans.QueueCommand(r => r.Set(key, lockString));
  54. //return trans.Commit(); //returns false if Transaction failed var t = trans.Commit();
  55. if (t == false)
  56. lockresult = LockResult.GetLockTimeOutFailure;
  57. else
  58. lockresult = LockResult.Success;
  59. return t;
  60. }
  61. },
  62. getlockTimeOut
  63. );
  64.  
  65. }
  66. }
  67. catch (Exception exp)
  68. {
  69. XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁系统级别严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
  70. lockresult = LockResult.LockSystemExceptionFailure;
  71. }
  72. return lockresult;
  73. }
  74.  
  75. public override void Dispose()
  76. {
  77. try {
  78. using (var redisClient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
  79. {
  80. redisClient.Remove(key);
  81. }
  82. }
  83. catch (Exception exp)
  84. {
  85. XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁释放严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
  86. }
  87. }
  88. }

Zookeeper 版本实现分布式锁

  1. /* * 来源java网络源码的zookeeper分布式锁实现(目前仅翻译并简单测试ok,未来集成入sdk)
  2. * 备注: 共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就不好实现了。Zookeeper 却很容易实现这个功能,实现方式也是需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点,
    然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,
    如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,
    从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。
  3. */ public class ZooKeeprDistributedLockFromJava : IWatcher
  4. {
  5. private ZooKeeper zk;
  6. private string root = "/locks"; //根
  7. private string lockName; //竞争资源的标志
  8. private string waitNode; //等待前一个锁
  9. private string myZnode; //当前锁
  10. //private CountDownLatch latch; //计数器
  11. private AutoResetEvent autoevent;
  12. private TimeSpan sessionTimeout = TimeSpan.FromMilliseconds(30000);
  13. private IList<Exception> exception = new List<Exception>();
  14.  
  15. /// <summary>
  16. /// 创建分布式锁,使用前请确认config配置的zookeeper服务可用 </summary>
  17. /// <param name="config"> 127.0.0.1:2181 </param>
  18. /// <param name="lockName"> 竞争资源标志,lockName中不能包含单词lock </param>
  19. public ZooKeeprDistributedLockFromJava(string config, string lockName)
  20. {
  21. this.lockName = lockName;
  22. // 创建一个与服务器的连接
  23. try
           {
  24. zk = new ZooKeeper(config, sessionTimeout, this);
  25. var stat = zk.Exists(root, false);
  26. if (stat == null)
  27. {
  28. // 创建根节点
  29. zk.Create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
  30. }
  31. }
  32. catch (KeeperException e)
  33. {
  34. throw e;
  35. }
  36. }
  37.  
  38. /// <summary>
  39. /// zookeeper节点的监视器
  40. /// </summary>
  41. public virtual void Process(WatchedEvent @event)
  42. {
  43. if (this.autoevent != null)
  44. {
  45. this.autoevent.Set();
  46. }
  47. }
  48.  
  49. public virtual bool tryLock()
  50. {
  51. try {
  52. string splitStr = "_lock_";
  53. if (lockName.Contains(splitStr))
  54. {
  55. //throw new LockException("lockName can not contains \\u000B");
              }
  56. //创建临时子节点
  57. myZnode = zk.Create(root + "/" + lockName + splitStr, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EphemeralSequential);
  58. Console.WriteLine(myZnode + " is created ");
  59. //取出所有子节点
  60. IList<string> subNodes = zk.GetChildren(root, false);
  61. //取出所有lockName的锁
  62. IList<string> lockObjNodes = new List<string>();
  63. foreach (string node in subNodes)
  64. {
  65. if (node.StartsWith(lockName))
  66. {
  67. lockObjNodes.Add(node);
  68. }
  69. }
  70. Array alockObjNodes = lockObjNodes.ToArray();
  71. Array.Sort(alockObjNodes);
  72. Console.WriteLine(myZnode + "==" + lockObjNodes[0]);
  73. if (myZnode.Equals(root + "/" + lockObjNodes[0]))
  74. {
  75. //如果是最小的节点,则表示取得锁
  76. return true;
  77. }
  78. //如果不是最小的节点,找到比自己小1的节点
  79. string subMyZnode = myZnode.Substring(myZnode.LastIndexOf("/", StringComparison.Ordinal) + 1);
  80. waitNode = lockObjNodes[Array.BinarySearch(alockObjNodes, subMyZnode) - 1];
  81. }
  82. catch (KeeperException e)
  83. {
  84. throw e;
  85. }
  86. return false;
  87. }
  88.  
  89. public virtual bool tryLock(TimeSpan time)
  90. {
  91. try {
  92. if (this.tryLock())
  93. {
  94. return true;
  95. }
  96. return waitForLock(waitNode, time);
  97. }
  98. catch (KeeperException e)
  99. {
  100. throw e;
  101. }
  102. return false;
  103. }
  104.  
  105. private bool waitForLock(string lower, TimeSpan waitTime)
  106. {
  107. var stat = zk.Exists(root + "/" + lower, true);
  108. //判断比自己小一个数的节点是否存在,如果不存在则无需等待锁,同时注册监听
  109. if (stat != null)
  110. {
  111. Console.WriteLine("Thread " + System.Threading.Thread.CurrentThread.Name + " waiting for " + root + "/" + lower);
  112. autoevent = new AutoResetEvent(false);
  113. bool r = autoevent.WaitOne(waitTime);
  114. autoevent.Dispose();
  115. autoevent = null;
  116. return r;
  117. }
  118. else return true;
  119. }
  120.  
  121. public virtual void unlock()
  122. {
  123. try {
  124. Console.WriteLine("unlock " + myZnode);
  125. zk.Delete(myZnode, -1);
  126. myZnode = null;
  127. zk.Dispose();
  128. }
  129. catch (KeeperException e)
  130. {
  131. throw e;
  132. }
  133. }
  134.  
  135. }

以上代码仅做参考,未压测。

代码粘贴有些问题,详细请下载开源包运行研究。

.net 分布式架构之分布式锁实现(转)的更多相关文章

  1. .net 分布式架构之分布式缓存中间件

    开源git地址: http://git.oschina.net/chejiangyi/XXF.BaseService.DistributedCache 分布式缓存中间件  方便实现缓存的分布式,集群, ...

  2. 大型Java web项目分布式架构演进-分布式部署

    http://blog.csdn.net/binyao02123202/article/details/32340283/ 知乎相关文章https://www.zhihu.com/question/2 ...

  3. .net 分布式架构之分布式锁实现

    分布式锁 经常用于在解决分布式环境下的业务一致性和协调分布式环境. 实际业务场景中,比如说解决并发一瞬间的重复下单,重复确认收货,重复发现金券等. 使用分布式锁的场景一般不能太多. 开源地址:http ...

  4. 基于Storm 分布式BP神经网络,将神经网络做成实时分布式架构

    将神经网络做成实时分布式架构: Storm 分布式BP神经网络:    http://bbs.csdn.net/topics/390717623 流式大数据处理的三种框架:Storm,Spark和Sa ...

  5. Spark实战--搭建我们的Spark分布式架构

    Spark的分布式架构 如我们所知,spark之所以强大,除了强大的数据处理功能,另一个优势就在于良好的分布式架构.举一个例子在Spark实战--寻找5亿次访问中,访问次数最多的人中,我用四个spar ...

  6. 转载:把你的精力专注在java,jvm原理,spring原理,mysql锁,事务,多线程,大并发,分布式架构,微服务,以及相关的项目管理等等,这样你的核心竞争力才会越来越高

    https://developer.51cto.com/art/202001/608984.htm 把你的精力专注在java,jvm原理,spring原理,mysql锁,事务,多线程,大并发,分布式架 ...

  7. 从Paxos到ZooKeeper-一、分布式架构

    本系列为本人读<从Paxos到ZooKeeper>一书的一些读书笔记,仅供学习使用,谢谢. 一.从集中式到分布式 1.1 分布式的定义: 分布式系统是一个硬件或软件组件分布在不同的网络计算 ...

  8. 不懂这些分布式架构、分布式系统的数据一致性解决方案,你如何能找到高新互联网工作呢?强势解析eBay BASE模式、去哪儿及蘑菇街分布式架构

    互联网行业是大势所趋,从招聘工资水平即可看出,那么如何提升自我技能,满足互联网行业技能要求?需要以目标为导向,进行技能提升,本文主要针对招聘中高频提及的分布式系统设计.架构(数据一致性)做了分析,祝各 ...

  9. 不懂这些高并发分布式架构、分布式系统的数据一致性解决方案,你如何能找到高新互联网工作呢?强势解析eBay BASE模式、去哪儿及蘑菇街分布式架构

    互联网行业是大势所趋,从招聘工资水平即可看出,那么如何提升自我技能,满足互联网行业技能要求?需要以目标为导向,进行技能提升,本文主要针对高并发分布式系统设计.架构(数据一致性)做了分析,祝各位早日走上 ...

随机推荐

  1. socket编程---UDP

    头文件 #include <sys/types.h> #include <sys/socket.h> 函数原型 int sendto (int s, const void *b ...

  2. 一:线性dp

    概念: 动态规划是运筹学的一个分支,是求解决策过程最优化的数学方法. 动态规划是通过拆分问题,定义问题状态和状态之间的关系使得问题能够以递推(或者说分治)的方法去解决. 解决策略: 1)最优化原理:如 ...

  3. linux之 multipath 多路径

    一.什么是多路径 普通的电脑主机都是一个硬盘挂接到一个总线上,这里是一对一的关系.而到了有光纤组成的SAN环境,或者由iSCSI组成的IPSAN环境,由于主机和存储通过了光纤交换机或者多块网卡及IP来 ...

  4. staltStack安装配置

    http://www.cnblogs.com/kevingrace/p/5570290.html

  5. sdk manager 代理,解决下载速度慢的问题

    原文:http://blog.csdn.net/android_panda/article/details/18598883 地址:mirrors.neusoft.edu.cn 端口:80 要勾选:F ...

  6. PHP常用获取文件路径的函数集合整理

    转自: http://blog.sina.com.cn/s/blog_71ed1b870102vslg.html 我们在开发PHP项目过程中,经常会用到包含文件,所以有时候需要获取文件的相对路径或者绝 ...

  7. jetty之嵌入式开发

    一.Jetty 是一个开源的servlet容器,它为基于Java的web容器,例如JSP和servlet提供运行环境.Jetty是使用Java语言编写的,它的API以一组JAR包的形式发布.开发人员可 ...

  8. Linux gdb调试器用法全面解析

    GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师完成下面4个方面的功能: 启动程序,可以按照工程师自定义的要求随心所欲的运行程序. 让被调试的程序在工程师指定的断 ...

  9. php利用curl实现多进程下载文件类

    批量下载文件一般使用循环的方式,逐一执行下载.但在带宽与服务器性能允许的情况下,使用多进程进行下载可以大大提高下载的效率.本文介绍PHP利用curl的多进程请求方法,实现多进程同时下载文件. 原理: ...

  10. centos7.3下apache搭建django[未成功]

    1 apache肯定已经按照完毕了, 如果没有  yum install httpd yum install mod_wsgi 安装完成之后,mod_wsgi.so会在Apache的modules目录 ...