最近看了讀到了關於hadoop中yarn的編程模型的文章 http://dongxicheng.org/mapreduce-nextgen/programming-model/,想深入瞭解下它的事件機制是怎麼實現的,就看了看其中的AsyncDispatcher類的源碼,幸好該類涉及到的其它類不多,讀起來也不算吃力
我們先來看其繼承關係,AsyncDispatcher繼承了AbstractService類,實現了Dispatcher接口,其中Dispatcher接口主要是定義了兩個方法register以及getEventHandler,以及一個用於配置事件循環是否在錯誤後推出的配置,不用太關注這個。
重點看下AbstractService,AbstractService類實現了Service接口,AbstractService中我們主要關注幾個方法:
服務生命週期部分:init,start,stop,serviceInit,serviceStart,serviceStop
服務監聽器部分:registerServiceListener,unregisterServiceListener,registerGlobalListener,unregisterGlobalListener
其中init,start,stop是繼承自service接口的,是暴露給用戶用於初始化,啓動和停止服務的接口,而serviceInit,serviceStart,serviceStop是空方法,在init,start,stop中被調用,用於子類繼承重寫,從而自定義自己的初始化,啓動以及停止操作。
那麼爲什麼不直接讓子類重寫init,start,stop方法呢,因爲init,start,stop除了調用你們自定義的啓動過程,還做了一些其它的事,就是改變service的狀態以及通知其它監聽了該服務的回掉,其中registerServiceListener,unregisterServiceListener,registerGlobalListener,unregisterGlobalListener就是用於註冊服務狀態回掉的接口,注意,此處的的回掉不是事件回掉,而是服務狀態改變時的回掉,每個服務都有該功能,不要和後文的AbstractService的事件回掉弄混了。
大致瞭解了AbstractService的功能是提供自定義的服務初始化,啓動和停止的接口以及維持服務狀態+狀態改變後通知接聽者,我們接下來就可以開始看AbstractService了,對於AbstractService的大致流程我們大致可以在腦中想一下,不外乎內部有一個事件隊列,同時有一個線程不斷的從事件隊列裏取事件,然後根據註冊情況進行分發,大致思路就是這樣的。
我們重點是看看具體的實現,首先我們看創建取事件的線程的
Runnable createThread() {
return new Runnable() {
@Override
public void run() {
// 首先定義兩個循環終止條件,stopped變量以及線程被Interrupt,其中stopped是volatile,保證可以及時的看到改變
while (!stopped && !Thread.currentThread().isInterrupted()) {
// 任務隊列是否爲空
drained = eventQueue.isEmpty();
// blockNewEvents is only set when dispatcher is draining to stop,
// adding this check is to avoid the overhead of acquiring the lock
// and calling notify every time in the normal run of the loop.
if (blockNewEvents) {
synchronized (waitForDrained) {
if (drained) {
// 任務隊列爲空才提醒,waitForDrained就是一個Object的實例,用於做線程間通信而已
waitForDrained.notify();
}
}
}
Event event;
try {
// 用阻塞的模式從事件隊列的取事件
event = eventQueue.take();
} catch(InterruptedException ie) {
// 如果take被中斷,且不是service被停止導致的,那麼log打印一句警告
if (!stopped) {
LOG.warn("AsyncDispatcher thread interrupted", ie);
}
return;
}
if (event != null) {
// 分發事件
dispatch(event);
}
}
}
};
}
大致就是做了take事件,然後dispatch事件的操作,其它操作則是爲了保證線程優雅推出,以及推出前完成當前事件隊列裏事件的分發
接下來就是dispatch方法了
protected void dispatch(Event event) {
//all events go thru this loop
if (LOG.isDebugEnabled()) {
LOG.debug("Dispatching the event " + event.getClass().getName() + "."
+ event.toString());
}
// 獲取事件類型
Class<? extends Enum> type = event.getType().getDeclaringClass();
try{
// 獲取相應的handler,EventHandler是個接口
EventHandler handler = eventDispatchers.get(type);
if(handler != null) {
// 調用handler的handle方法,
handler.handle(event);
} else {
throw new Exception("No handler for registered for " + type);
}
} catch (Throwable t) {
//TODO Maybe log the state of the queue
LOG.fatal("Error in dispatcher thread", t);
// If serviceStop is called, we should exit this thread gracefully.
if (exitOnDispatchException
&& (ShutdownHookManager.get().isShutdownInProgress()) == false
&& stopped == false) {
Thread shutDownThread = new Thread(createShutDownThread());
shutDownThread.setName("AsyncDispatcher ShutDown handler");
shutDownThread.start();
}
}
}
這個沒啥好說的,注意一點就是,dispatch方法是直接調用了handler的handle方法的,所以如果handle裏會出現要處理長時間的任務,務必記得新開一個線程執行,不然會影響eventloop的
接下來看一下用於註冊事件的接口
public void register(Class<? extends Enum> eventType,
EventHandler handler) {
/* check to see if we have a listener registered */
EventHandler<Event> registeredHandler = (EventHandler<Event>)
eventDispatchers.get(eventType);
LOG.info("Registering " + eventType + " for " + handler.getClass());
if (registeredHandler == null) {
eventDispatchers.put(eventType, handler);
} else if (!(registeredHandler instanceof MultiListenerHandler)){
/* for multiple listeners of an event add the multiple listener handler */
MultiListenerHandler multiHandler = new MultiListenerHandler();
multiHandler.addHandler(registeredHandler);
multiHandler.addHandler(handler);
eventDispatchers.put(eventType, multiHandler);
} else {
/* already a multilistener, just add to it */
MultiListenerHandler multiHandler
= (MultiListenerHandler) registeredHandler;
multiHandler.addHandler(handler);
}
}
這個純粹看英文註釋就知道註冊事件時碰到的三種情況(事件未註冊過,事件已註冊過但事件的handler不是符合handler,事件已註冊過且事件的handler是複合handler)下進行的操作了,提一句MultiListenerHandler是實現了EventHandler接口的類,代表符合handler,裏面維持了一個handler的list
接下來看看怎麼觸發事件,正常來說就是將事件推入事件隊列即可,但是hadoop這裏的實現讓我有點不太明白爲什麼要這麼做,希望有人可以指導一下
首先我們看一下項目裏別人是怎麼觸發事件的
dispatcher.getEventHandler().handle(event);
然後我們看一下getEventHandler方法
@Override
public EventHandler getEventHandler() {
return handlerInstance;
}
handlerInstance則是
private final EventHandler handlerInstance = new GenericEventHandler();
而GenericEventHandler則是實現了EventHandler接口的一個內部類
class GenericEventHandler implements EventHandler<Event> {
public void handle(Event event) {
if (blockNewEvents) {
return;
}
drained = false;
/* all this method does is enqueue all the events onto the queue */
int qSize = eventQueue.size();
if (qSize != 0 && qSize % 1000 == 0
&& lastEventQueueSizeLogged != qSize) {
lastEventQueueSizeLogged = qSize;
LOG.info("Size of event-queue is " + qSize);
}
int remCapacity = eventQueue.remainingCapacity();
if (remCapacity < 1000) {
LOG.warn("Very low remaining capacity in the event-queue: "
+ remCapacity);
}
try {
eventQueue.put(event);
} catch (InterruptedException e) {
if (!stopped) {
LOG.warn("AsyncDispatcher thread interrupted", e);
}
// Need to reset drained flag to true if event queue is empty,
// otherwise dispatcher will hang on stop.
drained = eventQueue.isEmpty();
throw new YarnRuntimeException(e);
}
};
}
沒錯,我們終於在GenericEventHandler的handle方法裏看到了將事件推入事件隊列的操作了,不太理解爲什麼要繞這麼一個大彎,望有人解答。handle裏還做了檢查事件隊列剩餘大小的工作。
最後我們看一下AsyncDispatcher的停止服務的方法
@Override
protected void serviceStop() throws Exception {
// 用一個標準位表示是否要處理完事件隊列裏的事件,該標誌位是有接口可以更改的
if (drainEventsOnStop) {
// 將blockNewEvents設爲true,禁止再向事件隊列裏添加事件了
blockNewEvents = true;
LOG.info("AsyncDispatcher is draining to stop, igonring any new events.");
long endTime = System.currentTimeMillis() + getConfig()
.getLong(YarnConfiguration.DISPATCHER_DRAIN_EVENTS_TIMEOUT,
YarnConfiguration.DEFAULT_DISPATCHER_DRAIN_EVENTS_TIMEOUT);
synchronized (waitForDrained) {
while (!drained && eventHandlingThread != null
&& eventHandlingThread.isAlive()
&& System.currentTimeMillis() < endTime) {
// 等待事件循環線程將事件隊列裏的任務處理完
waitForDrained.wait(1000);
LOG.info("Waiting for AsyncDispatcher to drain. Thread state is :" +
eventHandlingThread.getState());
}
}
}
// 設置服務停止標誌位爲true
stopped = true;
if (eventHandlingThread != null) {
// 中斷事件循環線程
eventHandlingThread.interrupt();
try {
// 等待線程結束
eventHandlingThread.join();
} catch (InterruptedException ie) {
LOG.warn("Interrupted Exception while stopping", ie);
}
}
// stop all the components
super.serviceStop();
}