摘要
本来和sendThread一起讲,但是sendThread太长了,下一节再讲,本节讲解ClientCnxn.EventThread,主要内容如下
EventThread属性
主要waitingEvents 临时存放需要被触发的Obj,包含Watcher和AsyncCallBack
EventThread方法
queueEvent方法把WatchedEvent加入到waitingEvents队列
queuePacket方法加入Packet,把AsyncCallBack事件加入到waitingEvents队列
queueEventOfDeath标记线程即将go die
run方法不断地从从waitingEvents队列取出WatchedEvent和Packet
processEvent方法对WatchedEvent和Packet两种事件进行处理
概述
EventThread是客户端ClientCnxn内部的一个事件处理线程,负责客户端的事件处理,并触发客户端注册的Watcher监听。EventThread中的watingEvents队列用于临时存放那些需要被触发的Object,包括客户端注册的Watcher和异步接口中注册的回调器AsyncCallback。同时,EventThread会不断地从watingEvents中取出Object,识别具体类型(Watcher或AsyncCallback),并分别调用process和processResult接口方法来实现对事件的触发和回调。
源码解析
类图如下
属性
private final LinkedBlockingQueue
属性需要结合方法理解
方法
异步加入WatchedEvent 至临时队列waitingEvents
public void queueEvent(WatchedEvent event) {//将WatchedEvent加入队列
if (event.getType() == EventType.None
&& sessionState == event.getState()) {
return;
}//如果没有发生什么状态变化
sessionState = event.getState();
// materialize the watchers based on the event
WatcherSetEventPair pair = new WatcherSetEventPair(
watcher.materialize(event.getState(), event.getType(),
event.getPath()),
event);//用WatcherSetEventPair封装watchers和watchedEvent
// queue the pair (watch set & event) for later processing
waitingEvents.add(pair);//加入队列
}
处理异步回调的Packet,根据标示位判断是异步还是同步处理
public void queuePacket(Packet packet) {//当Packet有callback的时候调用
if (wasKilled) {//如果kill掉了
synchronized (waitingEvents) {
if (isRunning) waitingEvents.add(packet);//线程在运行,则加入临时队列
else processEvent(packet);//同步处理Event
}
} else {
waitingEvents.add(packet);//加入临时队列
}
}
将eventOfDeath加入waitingEvents代表线程将要kill(进行take时才标示wasKilled)
public void queueEventOfDeath() {
waitingEvents.add(eventOfDeath);
}
线程run方法,根据eventOfDeath以及waitingEvents.isEmpty()改变两个标示位wasKilled,isRunning
@Override
public void run() {
try {
isRunning = true;
while (true) {
Object event = waitingEvents.take();//消费waitingEvents
if (event == eventOfDeath) {
wasKilled = true;//遇到了eventOfDeath就设置wasKilled表示需要被停掉(但是没有真正的停)
} else {
processEvent(event);//处理event
}
if (wasKilled)
synchronized (waitingEvents) {
if (waitingEvents.isEmpty()) {//当需要被停掉,且临时队列处理完了
isRunning = false;
break;//跳出while,结束线程
}
}
}
} catch (InterruptedException e) {
LOG.error("Event thread exiting due to interruption", e);
}
LOG.info("EventThread shut down for session: 0x{}",
Long.toHexString(getSessionId()));
}
processEvent函数太长了,看看就理解了,主要分为watch以及AsyncCallBack进行不同的回调
AsyncCallback用processResult
watch用process即可
private void processEvent(Object event) {//处理Event,主要分为watch以及AsyncCallBack
try {
if (event instanceof WatcherSetEventPair) {//如果是watch类型
// each watcher will process the event
WatcherSetEventPair pair = (WatcherSetEventPair) event;
for (Watcher watcher : pair.watchers) {//从WatcherSetEventPair这个wraper中取出watchers和event
try {
watcher.process(pair.event);//watcher处理WatchedEvent
} catch (Throwable t) {
LOG.error("Error while calling watcher ", t);
}
}
} else {//否则就是AsyncCallBack类型事件
Packet p = (Packet) event;
int rc = 0;
String clientPath = p.clientPath;
if (p.replyHeader.getErr() != 0) {
rc = p.replyHeader.getErr();
}
if (p.cb == null) {
LOG.warn("Somehow a null cb got to EventThread!");
} else if (p.response instanceof ExistsResponse
|| p.response instanceof SetDataResponse
|| p.response instanceof SetACLResponse) {
StatCallback cb = (StatCallback) p.cb;
if (rc == 0) {
if (p.response instanceof ExistsResponse) {
cb.processResult(rc, clientPath, p.ctx,
((ExistsResponse) p.response)
.getStat());
} else if (p.response instanceof SetDataResponse) {
cb.processResult(rc, clientPath, p.ctx,
((SetDataResponse) p.response)
.getStat());
} else if (p.response instanceof SetACLResponse) {
cb.processResult(rc, clientPath, p.ctx,
((SetACLResponse) p.response)
.getStat());
}
} else {
cb.processResult(rc, clientPath, p.ctx, null);
}
} else if (p.response instanceof GetDataResponse) {
DataCallback cb = (DataCallback) p.cb;
GetDataResponse rsp = (GetDataResponse) p.response;
if (rc == 0) {
cb.processResult(rc, clientPath, p.ctx, rsp
.getData(), rsp.getStat());
} else {
cb.processResult(rc, clientPath, p.ctx, null,
null);
}
} else if (p.response instanceof GetACLResponse) {
ACLCallback cb = (ACLCallback) p.cb;
GetACLResponse rsp = (GetACLResponse) p.response;
if (rc == 0) {
cb.processResult(rc, clientPath, p.ctx, rsp
.getAcl(), rsp.getStat());
} else {
cb.processResult(rc, clientPath, p.ctx, null,
null);
}
} else if (p.response instanceof GetChildrenResponse) {
ChildrenCallback cb = (ChildrenCallback) p.cb;
GetChildrenResponse rsp = (GetChildrenResponse) p.response;
if (rc == 0) {
cb.processResult(rc, clientPath, p.ctx, rsp
.getChildren());
} else {
cb.processResult(rc, clientPath, p.ctx, null);
}
} else if (p.response instanceof GetChildren2Response) {
Children2Callback cb = (Children2Callback) p.cb;
GetChildren2Response rsp = (GetChildren2Response) p.response;
if (rc == 0) {
cb.processResult(rc, clientPath, p.ctx, rsp
.getChildren(), rsp.getStat());
} else {
cb.processResult(rc, clientPath, p.ctx, null, null);
}
} else if (p.response instanceof CreateResponse) {
StringCallback cb = (StringCallback) p.cb;
CreateResponse rsp = (CreateResponse) p.response;
if (rc == 0) {
cb.processResult(rc, clientPath, p.ctx,
(chrootPath == null
? rsp.getPath()
: rsp.getPath()
.substring(chrootPath.length())));
} else {
cb.processResult(rc, clientPath, p.ctx, null);
}
} else if (p.response instanceof MultiResponse) {
MultiCallback cb = (MultiCallback) p.cb;
MultiResponse rsp = (MultiResponse) p.response;
if (rc == 0) {
List results = rsp.getResultList();
int newRc = rc;
for (OpResult result : results) {
if (result instanceof ErrorResult
&& KeeperException.Code.OK.intValue()
!= (newRc = ((ErrorResult) result).getErr())) {
break;
}
}
cb.processResult(newRc, clientPath, p.ctx, results);
} else {
cb.processResult(rc, clientPath, p.ctx, null);
}
} else if (p.cb instanceof VoidCallback) {
VoidCallback cb = (VoidCallback) p.cb;
cb.processResult(rc, clientPath, p.ctx);
}
}
} catch (Throwable t) {
LOG.error("Caught unexpected throwable", t);
}
}
问题
EventThread里面为什么queueEvent函数就是直接加入waitingEvents队列,而queuePacket则需要根据标示位状态判断是同步还是异步?
这里感觉是代码的问题,这一点并不理解
refer
http://www.voidcn.com/blog/aBOUNTWINTER/article/p-6400711.html
http://www.cnblogs.com/leesf456/p/6098255.html
https://my.oschina.net/pingpangkuangmo/blog/486780
http://shift-alt-ctrl.iteye.com/blog/1846971
《paxos到zk》