C# 基于ZooKeeper实现分布式锁

主体思路

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-

猜你喜欢

转载自blog.csdn.net/IIN22/article/details/84553018