.net 分布式架构之分布式锁实现
分布式锁
经常用于在解决分布式环境下的业务一致性和协调分布式环境。
实际业务场景中,比如说解决并发一瞬间的重复下单,重复确认收货,重复发现金券等。
使用分布式锁的场景一般不能太多。
开源地址:http://git.oschina.net/chejiangyi/XXF.BaseService.DistributedLock
开源相关群: .net 开源基础服务 238543768
这里整理了C#.net关于redis分布式锁和zookeeper分布式锁的实现,仅用于研究。(可能有bug)
采用ServiceStack.Redis实现Redis分布式锁
/*
* Redis分布式锁
* 采用ServiceStack.Redis实现的Redis分布式锁
* 详情可阅读其开源代码
* 备注:不同版本的 ServiceStack.Redis 实现reidslock机制不同 xxf里面默认使用2.2版本
*/ public class RedisDistributedLock : BaseRedisDistributedLock
{
private ServiceStack.Redis.RedisLock _lock;
private RedisClient _client;
public RedisDistributedLock(string redisserver, string key)
: base(redisserver, key)
{ } public override LockResult TryGetDistributedLock(TimeSpan? getlockTimeOut, TimeSpan? taskrunTimeOut)
{
if (lockresult == LockResult.Success)
throw new DistributedLockException("检测到当前锁已获取");
_client = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient();
/* * 阅读源码发现当其获取锁后,redis连接资源会一直占用,知道获取锁的资源释放后,连接才会跳出,可能会导致连接池资源的浪费。 */ try {
this._lock = new ServiceStack.Redis.RedisLock(_client, key, getlockTimeOut);
lockresult = LockResult.Success;
}
catch (Exception exp)
{
XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁系统级别严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
lockresult = LockResult.LockSystemExceptionFailure;
}
return lockresult;
} public override void Dispose()
{
try {
if (this._lock != null)
this._lock.Dispose();
if (_client != null)
this._client.Dispose();
}
catch (Exception exp)
{
XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁释放严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
}
}
}
来自网络的java实现Redis分布式锁(C#版)
/*
* Redis分布式锁
* 采用网络上java实现的Redis分布式锁
* 参考 http://www.blogjava.net/hello-yun/archive/2014/01/15/408988.html
* 详情可阅读其开源代码
*/ public class RedisDistributedLockFromJava : BaseRedisDistributedLock
{ public RedisDistributedLockFromJava(string redisserver, string key)
: base(redisserver, key)
{ } public override LockResult TryGetDistributedLock(TimeSpan? getlockTimeOut, TimeSpan? taskrunTimeOut)
{
if (lockresult == LockResult.Success)
throw new DistributedLockException("检测到当前锁已获取");
try {
// 1. 通过SETNX试图获取一个lock
string @lock = key;
long taskexpiredMilliseconds = (taskrunTimeOut != null ? (long)taskrunTimeOut.Value.TotalMilliseconds : (long)DistributedLockConfig.MaxLockTaskRunTime);
long getlockexpiredMilliseconds = (getlockTimeOut != null ? (long)getlockTimeOut.Value.TotalMilliseconds : );
long hassleepMilliseconds = ;
while (true)
{
using (var redisclient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
{
long value = CurrentUnixTimeMillis() + taskexpiredMilliseconds + ;
/*Java以前版本都是用SetNX,但是这种是无法设置超时时间的,不是很理解为什么,
* 可能是因为原来的redis命令比较少导致的?现在用Add不知道效果如何.
因对redis细节不了解,但个人怀疑若异常未释放锁经常发生,可能会导致内存逐步溢出*/ bool acquired = redisclient.Add<long>(@lock, value, TimeSpan.FromMilliseconds(taskexpiredMilliseconds + DistributedLockConfig.TaskLockDelayCleepUpTime));
//SETNX成功,则成功获取一个锁
if (acquired == true)
{
lockresult = LockResult.Success;
}
//SETNX失败,说明锁仍然被其他对象保持,检查其是否已经超时
else
{
var oldValueBytes = redisclient.Get(@lock);
//超时
if (oldValueBytes != null && BitConverter.ToInt64(oldValueBytes, ) < CurrentUnixTimeMillis())
{
/*此处虽然重设并获取锁,但是超时时间可能被覆盖,故重设超时时间;若有进程一直在尝试获取锁,那么锁存活时间应该被延迟*/ var getValueBytes = redisclient.GetSet(@lock, BitConverter.GetBytes(value));
var o1 = redisclient.ExpireEntryIn(@lock, TimeSpan.FromMilliseconds(taskexpiredMilliseconds + DistributedLockConfig.TaskLockDelayCleepUpTime));//这里如果程序异常终止,依然会有部分锁未释放的情况。 // 获取锁成功 if (getValueBytes == oldValueBytes)
{
lockresult = LockResult.Success;
}
// 已被其他进程捷足先登了
else
{
lockresult = LockResult.GetLockTimeOutFailure;
}
}
//未超时,则直接返回失败
else
{
lockresult = LockResult.GetLockTimeOutFailure;
}
}
} //成功拿到锁
if (lockresult == LockResult.Success)
break; //获取锁超时
if (hassleepMilliseconds >= getlockexpiredMilliseconds)
{
lockresult = LockResult.GetLockTimeOutFailure;
break;
} //继续等待
System.Threading.Thread.Sleep(DistributedLockConfig.GetLockFailSleepTime);
hassleepMilliseconds += DistributedLockConfig.GetLockFailSleepTime;
}
}
catch (Exception exp)
{
XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁系统级别严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
lockresult = LockResult.LockSystemExceptionFailure;
}
return lockresult;
} private long CurrentUnixTimeMillis()
{
return (long)(System.DateTime.UtcNow - new System.DateTime(, , , , , , System.DateTimeKind.Utc)).TotalMilliseconds;
} public override void Dispose()
{
if (lockresult == LockResult.Success || lockresult == LockResult.LockSystemExceptionFailure)
{
try {
long current = CurrentUnixTimeMillis();
using (var redisclient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
{
var v = redisclient.Get(key);
if (v != null) {
// 避免删除非自己获取得到的锁
if (current < BitConverter.ToInt64(v, ))
{
redisclient.Del(key);
}
}
}
}
catch (Exception exp)
{
XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁释放严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
}
}
}
}
ServiceStack.Redis内部实现版本(较旧)
/*
* Redis分布式锁
* 采用ServiceStack.Redis实现的Redis分布式锁
* 详情可阅读其开源代码
* 备注:不同版本的 ServiceStack.Redis 实现reidslock机制不同
* 拷贝自网络开源代码 较旧的实现版本
*/ public class RedisDistributedLockFromServiceStack : BaseRedisDistributedLock
{
public RedisDistributedLockFromServiceStack(string redisserver, string key)
: base(redisserver, key)
{ }
public override LockResult TryGetDistributedLock(TimeSpan? getlockTimeOut, TimeSpan? taskrunTimeOut)
{
if (lockresult == LockResult.Success)
throw new DistributedLockException("检测到当前锁已获取");
try
{
using (var redisClient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
{
ExecExtensions.RetryUntilTrue(
() =>
{
//This pattern is taken from the redis command for SETNX http://redis.io/commands/setnx
//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);
string lockString = (expireTime.ToUnixTimeMs() + ).ToString(); //Try to set the lock, if it does not exist this will succeed and the lock is obtained
var nx = redisClient.SetEntryIfNotExists(key, lockString);
if (nx)
{
lockresult = LockResult.Success;
return true;
} //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
redisClient.Watch(key);
string lockExpireString = redisClient.Get<string>(key);
long lockExpireTime;
if (!long.TryParse(lockExpireString, out lockExpireTime))
{
redisClient.UnWatch(); // since the client is scoped externally
lockresult = LockResult.GetLockTimeOutFailure;
return false;
} //If the expire time is greater than the current time then we can't let the lock go yet
if (lockExpireTime > DateTime.UtcNow.ToUnixTimeMs())
{
redisClient.UnWatch(); // since the client is scoped externally
lockresult = LockResult.GetLockTimeOutFailure;
return false;
} //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 {
trans.QueueCommand(r => r.Set(key, lockString));
//return trans.Commit(); //returns false if Transaction failed var t = trans.Commit();
if (t == false)
lockresult = LockResult.GetLockTimeOutFailure;
else
lockresult = LockResult.Success;
return t;
}
},
getlockTimeOut
); }
}
catch (Exception exp)
{
XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁系统级别严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
lockresult = LockResult.LockSystemExceptionFailure;
}
return lockresult;
} public override void Dispose()
{
try {
using (var redisClient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
{
redisClient.Remove(key);
}
}
catch (Exception exp)
{
XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁释放严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
}
}
}
Zookeeper 版本实现分布式锁
/* * 来源java网络源码的zookeeper分布式锁实现(目前仅翻译并简单测试ok,未来集成入sdk)
* 备注: 共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就不好实现了。Zookeeper 却很容易实现这个功能,实现方式也是需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点,
然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,
如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,
从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。
*/ public class ZooKeeprDistributedLockFromJava : IWatcher
{
private ZooKeeper zk;
private string root = "/locks"; //根
private string lockName; //竞争资源的标志
private string waitNode; //等待前一个锁
private string myZnode; //当前锁
//private CountDownLatch latch; //计数器
private AutoResetEvent autoevent;
private TimeSpan sessionTimeout = TimeSpan.FromMilliseconds();
private IList<Exception> exception = new List<Exception>(); /// <summary>
/// 创建分布式锁,使用前请确认config配置的zookeeper服务可用 </summary>
/// <param name="config"> 127.0.0.1:2181 </param>
/// <param name="lockName"> 竞争资源标志,lockName中不能包含单词lock </param>
public ZooKeeprDistributedLockFromJava(string config, string lockName)
{
this.lockName = lockName;
// 创建一个与服务器的连接
try
{
zk = new ZooKeeper(config, sessionTimeout, this);
var stat = zk.Exists(root, false);
if (stat == null)
{
// 创建根节点
zk.Create(root, new byte[], Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
}
}
catch (KeeperException e)
{
throw e;
}
} /// <summary>
/// zookeeper节点的监视器
/// </summary>
public virtual void Process(WatchedEvent @event)
{
if (this.autoevent != null)
{
this.autoevent.Set();
}
} public virtual bool tryLock()
{
try {
string splitStr = "_lock_";
if (lockName.Contains(splitStr))
{
//throw new LockException("lockName can not contains \\u000B");
}
//创建临时子节点
myZnode = zk.Create(root + "/" + lockName + splitStr, new byte[], Ids.OPEN_ACL_UNSAFE, CreateMode.EphemeralSequential);
Console.WriteLine(myZnode + " is created ");
//取出所有子节点
IList<string> subNodes = zk.GetChildren(root, false);
//取出所有lockName的锁
IList<string> lockObjNodes = new List<string>();
foreach (string node in subNodes)
{
if (node.StartsWith(lockName))
{
lockObjNodes.Add(node);
}
}
Array alockObjNodes = lockObjNodes.ToArray();
Array.Sort(alockObjNodes);
Console.WriteLine(myZnode + "==" + lockObjNodes[]);
if (myZnode.Equals(root + "/" + lockObjNodes[]))
{
//如果是最小的节点,则表示取得锁
return true;
}
//如果不是最小的节点,找到比自己小1的节点
string subMyZnode = myZnode.Substring(myZnode.LastIndexOf("/", StringComparison.Ordinal) + );
waitNode = lockObjNodes[Array.BinarySearch(alockObjNodes, subMyZnode) - ];
}
catch (KeeperException e)
{
throw e;
}
return false;
} public virtual bool tryLock(TimeSpan time)
{
try {
if (this.tryLock())
{
return true;
}
return waitForLock(waitNode, time);
}
catch (KeeperException e)
{
throw e;
}
return false;
} private bool waitForLock(string lower, TimeSpan waitTime)
{
var stat = zk.Exists(root + "/" + lower, true);
//判断比自己小一个数的节点是否存在,如果不存在则无需等待锁,同时注册监听
if (stat != null)
{
Console.WriteLine("Thread " + System.Threading.Thread.CurrentThread.Name + " waiting for " + root + "/" + lower);
autoevent = new AutoResetEvent(false);
bool r = autoevent.WaitOne(waitTime);
autoevent.Dispose();
autoevent = null;
return r;
}
else return true;
} public virtual void unlock()
{
try {
Console.WriteLine("unlock " + myZnode);
zk.Delete(myZnode, -);
myZnode = null;
zk.Dispose();
}
catch (KeeperException e)
{
throw e;
}
} }
以上代码仅做参考,未压测。
代码粘贴有些问题,详细请下载开源包运行研究。
.net 分布式架构之分布式锁实现的更多相关文章
- .net 分布式架构之分布式缓存中间件
开源git地址: http://git.oschina.net/chejiangyi/XXF.BaseService.DistributedCache 分布式缓存中间件 方便实现缓存的分布式,集群, ...
- 大型Java web项目分布式架构演进-分布式部署
http://blog.csdn.net/binyao02123202/article/details/32340283/ 知乎相关文章https://www.zhihu.com/question/2 ...
- .net 分布式架构之分布式锁实现(转)
分布式锁是控制分布式系统之间同步访问共享资源的一种方式.在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防 ...
- 基于Storm 分布式BP神经网络,将神经网络做成实时分布式架构
将神经网络做成实时分布式架构: Storm 分布式BP神经网络: http://bbs.csdn.net/topics/390717623 流式大数据处理的三种框架:Storm,Spark和Sa ...
- Spark实战--搭建我们的Spark分布式架构
Spark的分布式架构 如我们所知,spark之所以强大,除了强大的数据处理功能,另一个优势就在于良好的分布式架构.举一个例子在Spark实战--寻找5亿次访问中,访问次数最多的人中,我用四个spar ...
- 转载:把你的精力专注在java,jvm原理,spring原理,mysql锁,事务,多线程,大并发,分布式架构,微服务,以及相关的项目管理等等,这样你的核心竞争力才会越来越高
https://developer.51cto.com/art/202001/608984.htm 把你的精力专注在java,jvm原理,spring原理,mysql锁,事务,多线程,大并发,分布式架 ...
- 从Paxos到ZooKeeper-一、分布式架构
本系列为本人读<从Paxos到ZooKeeper>一书的一些读书笔记,仅供学习使用,谢谢. 一.从集中式到分布式 1.1 分布式的定义: 分布式系统是一个硬件或软件组件分布在不同的网络计算 ...
- 不懂这些分布式架构、分布式系统的数据一致性解决方案,你如何能找到高新互联网工作呢?强势解析eBay BASE模式、去哪儿及蘑菇街分布式架构
互联网行业是大势所趋,从招聘工资水平即可看出,那么如何提升自我技能,满足互联网行业技能要求?需要以目标为导向,进行技能提升,本文主要针对招聘中高频提及的分布式系统设计.架构(数据一致性)做了分析,祝各 ...
- 不懂这些高并发分布式架构、分布式系统的数据一致性解决方案,你如何能找到高新互联网工作呢?强势解析eBay BASE模式、去哪儿及蘑菇街分布式架构
互联网行业是大势所趋,从招聘工资水平即可看出,那么如何提升自我技能,满足互联网行业技能要求?需要以目标为导向,进行技能提升,本文主要针对高并发分布式系统设计.架构(数据一致性)做了分析,祝各位早日走上 ...
随机推荐
- 使用struct处理二进制
有的时候需要用python处理二进制数据,比如,存取文件.socket操作时.这时候,可以使用python的struct模块来完成. struct模块中最重要的三个函数是pack(), unpack( ...
- DBImport V3.7版本发布及软件稳定性(自动退出问题)解决过程分享
DBImport V3.7介绍: 1:先上图,再介绍亮点功能: 主要的升级功能为: 1:增加(Truncate Table)清表再插入功能: 清掉再插,可以保证两个库的数据一致,自己很喜欢这个功能. ...
- CoreCRM 开发实录——想用国货不容易
昨天(2016年12月29日)发了开始开发的文章.本来晚上准备在 Coding.NET 上添加几个任务开始搞起了.可是真的开始用的时候才发现:Coding.NET 的任务功能只针对私有的任务开放.我想 ...
- WebForm获取GET或者POST参数到实体的转换,ADO.NET数据集自动转换实体
最近在修改维护以前的webform项目(维护别人开发的.....)整个aspx没有用到任何的控件,这个我也比较喜欢不用控件所以在提交信息的时候需要自己手动的去Request.QueryString[] ...
- Tesseract-OCR字符识别简介
OCR(Optical Character Recognition):光学字符识别,是指对图片文件中的文字进行分析识别,获取的过程.Tesseract:开源的OCR识别引擎,初期Tesseract引擎 ...
- angluarjs2项目生成内容合并到asp.net mvc4项目中一起发布
应用场景 angular2(下文中标注位NG2)项目和.net mvc项目分别开发,前期采用跨域访问进行并行开发,后期只需要将NG2项目的生产版本合并到.net项目. NG2项目概述 ng2项目采用的 ...
- 【Star CCM+实例】开发一个简单的计算流程.md
流程开发在CAE过程中处于非常重要的地位. 主要的作用可能包括: 将一些经过验证的模型隐藏在流程中,提高仿真的可靠性 将流程封装成更友好的界面,降低软件的学习周期 流程开发实际上需要做非常多的工作,尤 ...
- 定时Job在IIS中潜在危险-IIS 定期回收
引言 有时我们会在IIS中启用一些定时服务,但是你必须清楚IIS会定期回收Asp.net的应用程序的.首先来看IIS啥时候回收APPDomain. APPDomain 回收时机 There are ...
- 分享在Linux下使用OSGi.NET插件框架快速实现一个分布式服务集群的方法
在这篇文章我分享了如何使用分层与模块化的方法来设计一个分布式服务集群.这个分布式服务集群是基于DynamicProxy.WCF和OSGi.NET插件框架实现的.我将从设计思路.目标和实现三方面来描述. ...
- 玩转 Linux 系统的方法论
Linus 说“Just for fun”,而我要说“Just for 折腾”.想知道我是怎样折腾 Linux 的,请看下面这个截图: 从这个截图可以看出,我为了“折腾” Linux 系统,在我的电脑 ...