参考资料:
下载路径:https://mirrors.cnnic.cn/apache/zookeeper/
Zookeeper安装: http://blog.csdn.net/fenglibing/article/details/30840175
主体思路
1. 在locks节点下创建临时顺序节点node_n
2. 判断当前创建的节点是否为locks节点下所有子节点中最小的子节点
3. 是则获取锁,进行业务处理,否则将节点从小到大排序,监听当前节点上一个节点的删除事件
4. 事件触发后回到步骤2进行判断,直至拿到锁
代码块分析
构造函数中创建Zookeeper对象
1. 注意创建完对象之后不一定和服务器建立了连接,中间异步存在时间差,故增加了while循环等待
2. 建立连接之后创建锁的根节点,注意该节点为持久化节点
public ZooKeeperLock(string server, string lockName)
{
this.lockName = lockName;
this.zooKeeper = new ZooKeeper(server, new TimeSpan(0, 0, 0, SessionTimeout), this);
while (!this.connected)
{
//保证和zookeeper建立连接后再进行节点操作
}
var stat = this.zooKeeper.Exists(LockRootName, false);
if (stat == null)
{
this.zooKeeper.Create(LockRootName, null, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
}
}
事件监听
实现IWatch接口,主要负责对节点变化进行事件监听,在改例中监听了两个事件(有对ZooKeeper监听流程不熟悉的请自行百度学习)
1. 监听服务器建立连接事件,建立连接后需要告知ZooKeeper对象连接完毕,设置connected变量为true,该变量在上面构造函数中使用到了
2. 监听节点删除事件,上一个节点删除之后重新尝试获取锁操作
public void Process(WatchedEvent @event)
{
if (KeeperState.SyncConnected == @event.State)
{
this.connected = true;
}
if (@event.Type == EventType.NodeDeleted)
{
this.GetLock(false);
}
}
获取锁方法
1. 节点不存在的时候需要创建一个锁节点,该节点为临时顺序节点
2. 获取锁根节点下的所有子节点并进行由小到大排序,如果步骤1创建的节点是第一个节点则获得锁,返回true
3. 如果不是则找到上一个节点,监听其删除事件 (这样所有锁节点形成了一个链式的关系,避免了羊群效应)
public bool GetLock(bool create = true)
{
if (this.currentId == null && create)
{
this.currentId = this.zooKeeper.Create(LockRootName + "/" + this.lockName + "_", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EphemeralSequential);
}
var childrens = this.zooKeeper.GetChildren(LockRootName, false);
if (childrens == null || childrens.Count() == 1)
{
return true;
}
var orderChildrens = childrens.OrderBy(p => p).ToList();
var index = orderChildrens.FindIndex(p => p.Equals(this.currentId.Replace(LockRootName + "/", "")));
if (index == 0)
{
return true;
}
this.waitId = LockRootName + "/" + orderChildrens[index - 1];
var stat = this.zooKeeper.Exists(this.waitId, this);
if (stat == null)
{
this.GetLock(false);
}
return false;
}
释放锁
锁使用完成之后则将当前锁节点进行删除,释放当前锁
public void UnLock()
{
if (this.currentId == null)
{
return;
}
this.zooKeeper.Delete(this.currentId, -1);
this.currentId = null;
}
测试代码
for (int i = 0; i < 10; i++)
{
Task.Factory.StartNew(
() =>
{
var lockObj = new ZooKeeperLock("127.0.0.1:2181", "testlock");
bool outPut = false;
while (true)
{
if (lockObj.GetLock())
{
Console.WriteLine(lockObj.GetCurrentId() + "获得锁正在执行操作");
Thread.Sleep(5 * 1000);
Console.WriteLine(lockObj.GetCurrentId() + "执行操作完成,即将释放锁");
lockObj.UnLock();
lockObj.Dispose();
break;
}
else
{
if (!outPut)
{
Console.WriteLine(lockObj.GetCurrentId() + "在等待锁");
outPut = true;
}
}
}
});
}
执行结果
完整代码:https://github.com/One-One-Code/ZooKeeper-