ZookeeperNet客户端源码解析

ZookeeperNet客户端源码解析

说明:

源代码版本为GitHub 3.3.4.8,本文主要讲解ZookeeperNet内部运行机制,不过多介绍客户端调用方法。

框架图:


主要流程:客户端通过TCP建立与Zookeeper服务器集群的连接,同时会启动两个线程SendThread及EventThread,SendThread负责处理与Zookeeper Server之间的通信,EventThread处理由服务器触发的事件,OutgoingQueue为客户端发送消息队列,PendingQueue为客户端接收服务器消息队列。

源码解析:

建立与zookeeper服务器连接

var zk = new ZooKeeper(Config.IP, new TimeSpan(0, 0, 0, 50000), new Watcher());//建立连接

看下ZooKeeper构造器,设置默认watcher,实例化ClientConnection对象

public ZooKeeper(string connectstring, TimeSpan sessionTimeout, IWatcher watcher)
{
    LOG.InfoFormat("Initiating client connection, connectstring={0} sessionTimeout={1} watcher={2}", connectstring, sessionTimeout, watcher);

    watchManager.DefaultWatcher = watcher;
    cnxn = new ClientConnection(connectstring, sessionTimeout, this, watchManager);
    cnxn.Start();
}

public ClientConnection(string connectionString, TimeSpan sessionTimeout, ZooKeeper zooKeeper, ZKWatchManager watcher):
    this(connectionString, sessionTimeout, zooKeeper, watcher, 0, new byte[16], DefaultConnectTimeout)
{
}

public ClientConnection(string hosts, TimeSpan sessionTimeout, ZooKeeper zooKeeper, ZKWatchManager watcher, long sessionId, byte[] sessionPasswd, TimeSpan connectTimeout)
{
    this.hosts = hosts;
    this.zooKeeper = zooKeeper;
    this.watcher = watcher;
    SessionTimeout = sessionTimeout;
    SessionId = sessionId;
    SessionPassword = sessionPasswd;
    ConnectionTimeout = connectTimeout;

    // parse out chroot, if any
    hosts = SetChrootPath();
    GetHosts(hosts);
    SetTimeouts(sessionTimeout);
    CreateConsumer();
    CreateProducer();
}

private void CreateConsumer()
{
    consumer = new ClientConnectionEventConsumer(this);
}

public ClientConnectionEventConsumer(ClientConnection conn)
{
    this.conn = conn;
    eventThread = new Thread(new SafeThreadStart(PollEvents).Run) { Name = new StringBuilder("ZK-EventThread ").Append(conn.zooKeeper.Id).ToString(), IsBackground = true };
}

private void CreateProducer()
{
    producer = new ClientConnectionRequestProducer(this);
}

public ClientConnectionRequestProducer(ClientConnection conn)
{
    this.conn = conn;
    zooKeeper = conn.zooKeeper;
    zkEndpoints = new ZooKeeperEndpoints(conn.serverAddrs);
    requestThread = new Thread(new SafeThreadStart(SendRequests).Run) { Name = new StringBuilder("ZK-SendThread ").Append(conn.zooKeeper.Id).ToString(), IsBackground = true };
}

实例化ClientConnection对象,设置SessionTimeout 为客户端传入的值,ConnectionTimeout 为默认值,SetTimeouts(sessionTimeout)设置readTimeout=sessionTimeout*2/3。CreateConsumer()、CreateProducer()会启动两个后台线程,对应的方法为PollEvents、SendRequests,PollEvents的工作相对简单,负责处理服务器触发的事件,SendRequests负责处理与服务器的通信。


PollEvents代码:

public void PollEvents()
{
    try
    {
        while(!waitingEvents.IsCompleted)
        {
            try
            {
                ClientConnection.WatcherSetEventPair pair = null;
                if (waitingEvents.TryTake(out pair, -1))
                {
                    ProcessWatcher(pair.Watchers, pair.WatchedEvent);
                }
            }
            catch (ObjectDisposedException)
            {
            }
            catch (InvalidOperationException)
            {
            }
            catch (OperationCanceledException)
            {
                //ignored
            }
            catch (Exception t)
            {
                LOG.Error("Caught unexpected throwable", t);
            }
        }
    }
    catch (ThreadInterruptedException e)
    {
        LOG.Error("Event thread exiting due to interruption", e);
    }

    LOG.Info("EventThread shut down");
}

private static void ProcessWatcher(IEnumerable<IWatcher> watchers,WatchedEvent watchedEvent)
{
    foreach (IWatcher watcher in watchers)
    {
        try
        {
            if (null != watcher)
            {
                watcher.Process(watchedEvent);
            }
        }
        catch (Exception t)
        {
            LOG.Error("Error while calling watcher ", t);
        }
    }
}

waitingEvents.TryTake(out pair, -1),如果waitingEvents没值会阻塞当前线程阻塞,一旦有服务器返回的事件需要处理便会执行事件的Process方法。


SendRequests代码:

private void SendRequests()
{
    DateTime now = DateTime.UtcNow;
    DateTime lastSend = now;
    Packet packet = null;

    while (zooKeeper.State.IsAlive())
    {
        try
        {
            now = DateTime.UtcNow;
            if ((client == null || client.Client == null) || (!client.Connected || zooKeeper.State == ZooKeeper.States.NOT_CONNECTED))
            {
                // don't re-establish connection if we are closing
                if(conn.IsClosed || closing)
                    break;

                StartConnect();
                lastSend = now;
            }
            TimeSpan idleSend = now - lastSend;
            if (zooKeeper.State == ZooKeeper.States.CONNECTED)
            {
                TimeSpan timeToNextPing = new TimeSpan(0, 0, 0, 0, Convert.ToInt32(conn.readTimeout.TotalMilliseconds / 2 - idleSend.TotalMilliseconds));
                if (timeToNextPing <= TimeSpan.Zero)
                    SendPing();
            }
            // Everything below and until we get back to the select is
            // non blocking, so time is effectively a constant. That is
            // Why we just have to do this once, here                    

            packet = null;
            lock (outgoingQueue)
            {
                if(!outgoingQueue.IsEmpty())
                {
                    packet = outgoingQueue.First();
                    outgoingQueue.RemoveFirst();
                }
            }
            if (packet != null)
            {
                // We have something to send so it's the same
                // as if we do the send now.                        
                DoSend(packet);
                lastSend = DateTime.UtcNow;
                packet = null;
            }
            else
                packetAre.WaitOne(TimeSpan.FromMilliseconds(1));
        }
        catch (Exception e)
        {
            if (conn.IsClosed || closing)
            {
                if (LOG.IsDebugEnabled)
                {
                    // closing so this is expected
                    LOG.DebugFormat(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
什么是ZooKeeperZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。 ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 Rabbit ZooKeeper Extensions 该项目使用了 Apache ZooKeeper .NET async Client 组件,除提供了基本的zk操作,还额外封装了常用的功能以便让.net开发者更好的使用zookeeper。 提供的功能 session过期重连 永久watcher 递归删除节点 递归创建节点 跨平台(支持.net core) 使用说明 创建连接 IZookeeperClient client = new ZookeeperClient(new ZookeeperClientOptions         {             ConnectionString = "172.18.20.132:2181",             BasePath = "/", //default value             ConnectionTimeout = TimeSpan.FromSeconds(10), //default value             SessionTimeout = TimeSpan.FromSeconds(20), //default value             OperatingTimeout = TimeSpan.FromSeconds(60), //default value             ReadOnly = false, //default value             SessionId = 0, //default value             SessionPasswd = null //default value         }); 创建节点 var data = Encoding.UTF8.GetBytes("2016"); //快速创建临时节点 await client.CreateEphemeralAsync("/year", data); await client.CreateEphemeralAsync("/year", data, ZooDefs.Ids.OPEN_ACL_UNSAFE); //快速创建永久节点 await client.CreatePersistentAsync("/year", data); await client.CreatePersistentAsync("/year", data, ZooDefs.Ids.OPEN_ACL_UNSAFE); //完整调用 await client.CreateAsync("/year", data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); //递归创建 await client.CreateRecursiveAsync("/microsoft/netcore/aspnet", data, CreateMode.PERSISTENT); 获取节点数据 IEnumerable data = await client.GetDataAsync("/year"); Encoding.UTF8.GetString(data.ToArray()); 获取子节点 IEnumerable children= await client.GetChildrenAsync("/microsoft"); 判断节点是否存在 bool exists = await client.ExistsAsync("/year"); 删除节点 await client.DeleteAsync("/year"); //递归删除 bool success = await client.DeleteRecursiveAsync("/microsoft"); 更新数据 Stat stat = await client.SetDataAsync("/year", Encoding.UTF8.GetBytes("2017")); 订阅数据变化 await client.Subscrib

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值