转载请注明出处:jiq•钦's technical Blog
ZooKeeperNet是ZooKeeper的.NET客户端,下载地址:https://github.com/ewhauser/zookeeper
测试程序
ZooKeeper zkClient = new ZooKeeper(URL,new TimeSpan(0, 0, 0, 10000), watcher);
if(zkClient.Exists("/config/configJ",null) ==null)
{
zkClient.Create("/config/configJ", json.GetBytes(),Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
}
初始化和启动
首先看ZooKeeper构造函数:
public ZooKeeper(string connectstring,TimeSpan sessionTimeout, IWatcherwatcher, long sessionId, byte[] sessionPasswd)
{
LOG.Info(string.Format("Initiating client connection, connectstring={0}sessionTimeout={1} watcher={2} sessionId={3} sessionPasswd={4}",connectstring, sessionTimeout, watcher, sessionId, (sessionPasswd == null ? "<null>": "<hidden>")));
//之后连接状态发生变化后会放入ClientConnectionEventConsumer回调
watchManager.defaultWatcher =watcher;
cnxn = newClientConnection(connectstring,sessionTimeout,this, watchManager, sessionId,sessionPasswd);
cnxn.Start();
}
创建了一个ClientConnection对象,继续看其构造函数:
public ClientConnection(string hosts,TimeSpan sessionTimeout, ZooKeeperzooKeeper, ZKWatchManager watcher,long sessionId, byte[]sessionPasswd)
{
this.hosts= hosts;
this.zooKeeper= zooKeeper;
this.watcher= watcher;
SessionTimeout = sessionTimeout;
SessionId = sessionId;
SessionPassword = sessionPasswd;
// parseout chroot, if any
hosts = SetChrootPath();
GetHosts(hosts);
SetTimeouts(sessionTimeout);
CreateConsumer();
CreateProducer();
}
关键是看最后两句代码,实现代码为:
private void CreateConsumer()
{
consumer = newClientConnectionEventConsumer(this);
}
private voidCreateProducer()
{
producer = newClientConnectionRequestProducer(this);
}
这两个是什么东西?
(1)先看ClientConnectionEventConsumer:
public ClientConnectionEventConsumer(ClientConnectionconn)
{
this.conn= conn;
eventThread = new Thread(new SafeThreadStart(PollEvents).Run){ Name ="ZK-EventThread " +conn.zooKeeper.Id, IsBackground =true };
}
创建了一个线程,线程的主体是:
public void PollEvents()
{
try
{
while(!waitingEvents.IsCompleted)
{
object@event = waitingEvents.Take();
try
{
if (@eventis ClientConnection.WatcherSetEventPair)
{
// each watcher will process the event
ClientConnection.WatcherSetEventPairpair = (ClientConnection.WatcherSetEventPair) @event;
foreach (IWatcherwatcherin pair.watchers)
{
try
{
watcher.Process(pair.@event);
}
catch (Exceptiont)
{
LOG.Error("Error while calling watcher ", t);
}
}
}
}
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("EventThreadshut down");
}
核心代码作用是不断从waitingEvents中取出@event,将其中的IWatcher的process函数通通调用一遍,看来ClientConnectionEventConsumer的作用是维护IWatcher集合,并调用它们对应的事件响应函数,至于事件是如何add到waitingEvents中的,稍后再看。
(2)在看看ClientConnectionRequestProducer:
public ClientConnectionRequestProducer(ClientConnectionconn)
{
this.conn= conn;
zooKeeper = conn.zooKeeper;
requestThread = new Thread(new SafeThreadStart(SendRequests).Run){ Name ="ZK-SendThread" +conn.zooKeeper.Id, IsBackground =true };
}
同样创建了一个线程,线程的主体是:
public void SendRequests()
{
DateTimenow = DateTime.Now;
DateTimelastHeard = now;
DateTimelastSend = now;
//当state != CLOSED && state != AUTH_FAILED循环
while(zooKeeper.State.IsAlive())
{
try
{
//TcpClient是否为空
if(client == null)
{
// don't re-establish connection if we are closing
if (conn.closing)
{
break;
}
StartConnect(); //建立到Zookeeper的TCP连接
lastSend = now;
lastHeard = now;
}
//若还未变为CONNECTED状态,判断是否超时
TimeSpanidleRecv = now - lastHeard;
TimeSpanidleSend = now - lastSend;
TimeSpanto = conn.readTimeout - idleRecv;
if(zooKeeper.State !=ZooKeeper.States.CONNECTED)
{
to =conn.connectTimeout - idleRecv;
}
if(to <= TimeSpan.Zero)
{
throw new SessionTimeoutExceptio