本文以hadoop2.7.1的源碼分析(主要是最新版本考慮更多因素,源碼不夠純粹)
集羣啓動腳本分析
首先我們從啓動hadoop集羣說起,我們一般在單點hadoop啓動集羣一般直接使用 sbin/start-all.sh 或 sbin/stop-all.sh ,我們直接看啓動腳本:
bin=`dirname "${BASH_SOURCE-$0}"`
bin=`cd "$bin"; pwd`
DEFAULT_LIBEXEC_DIR="$bin"/../libexec
HADOOP_LIBEXEC_DIR=${HADOOP_LIBEXEC_DIR:-$DEFAULT_LIBEXEC_DIR}
. $HADOOP_LIBEXEC_DIR/hadoop-config.sh
# start hdfs daemons if hdfs is present
if [ -f "${HADOOP_HDFS_HOME}"/sbin/start-dfs.sh ]; then
"${HADOOP_HDFS_HOME}"/sbin/start-dfs.sh --config $HADOOP_CONF_DIR
fi
# start yarn daemons if yarn is present
if [ -f "${HADOOP_YARN_HOME}"/sbin/start-yarn.sh ]; then
"${HADOOP_YARN_HOME}"/sbin/start-yarn.sh --config $HADOOP_CONF_DIR
fi
可以看出先後執行啓動了 hadoop-config.sh
、 start-dfs.sh
和 start-yarn.sh
。由於本文主要探討Yarn啓動過程,所以我們直接看 start-yarn.sh
echo "starting yarn daemons"
bin=`dirname "${BASH_SOURCE-$0}"`
bin=`cd "$bin"; pwd`
DEFAULT_LIBEXEC_DIR="$bin"/../libexec
HADOOP_LIBEXEC_DIR=${HADOOP_LIBEXEC_DIR:-$DEFAULT_LIBEXEC_DIR}
. $HADOOP_LIBEXEC_DIR/yarn-config.sh
# start resourceManager
"$bin"/yarn-daemon.sh --config $YARN_CONF_DIR start resourcemanager
# start nodeManager
"$bin"/yarn-daemons.sh --config $YARN_CONF_DIR start nodemanager
# start proxyserver
#"$bin"/yarn-daemon.sh --config $YARN_CONF_DIR start proxyserver
可以看出Yarn先後調用 yarn-daemon.sh 啓動 resourcemanager
組件;調用 yarn-daemons.sh 啓動 nodemanager
組件,只不過 yarn-daemons.sh
也會調用 slaves.sh
後同樣也會調用 yarn-daemon.sh
,所以我們繼續分析 yarn-daemon.sh 腳本,我們看關鍵代碼(如需看完整代碼,點鏈接查閱即可):
...
echo starting $command, logging to $log
cd "$HADOOP_YARN_HOME"
nohup nice -n $YARN_NICENESS "$HADOOP_YARN_HOME"/bin/yarn --config $YARN_CONF_DIR $command "$@" > "$log" 2>&1 < /dev/null &
echo $! > $pid
...
可以看出最終調用 yarn 腳本,由於代碼比較多,我們直接看啓動 resourcemanager
和 nodemanager
組件的代碼部分:
從腳本可以看出,會分別調用 org.apache.hadoop.yarn.server.resourcemanager.ResourceManager
和 org.apache.hadoop.yarn.server.nodemanager.NodeManager
類,調用 exec "$JAVA" -Dproc_$COMMAND $JAVA_HEAP_MAX $YARN_OPTS -classpath "$CLASSPATH" $CLASS "$@"
啓動JVM進程啓動常駐服務,即 ResourceManager
和 NodeManager
是入口類,我們以 ResourceManager
組件,繼續往下分析…
注:上面介紹的一般在單節點上啓動,但是在分佈式情況,ResourceManager和NodeManager等分佈在不同機器上,啓動的進程不相同,
因此 推薦在不同節點分別啓動組件 ,可以參考:
sbin/hadoop-daemons.sh start namenode
單獨啓動NameNode守護進程sbin/hadoop-daemons.sh start datanode
單獨啓動DataNode守護進程sbin/hadoop-daemons.sh start secondarynamenode
單獨啓動SecondaryNameNode守護進程sbin/yarn-daemon.sh start resourcemanager
單獨啓動ResourceManager守護進程sbin/yarn-daemon.sh start nodemanager
單獨啓動NodeManager守護進程
ResourceManager啓動流程
首先我們先看下ResourceManager中的功能模塊,有利於宏觀掌握。ResourceManager在底層代碼實現上將各個功能模塊分的比較細,各個模塊功能具有很強的獨立性。如下圖:
從上面啓動腳本可以看出, org.apache.hadoop.yarn.server.resourcemanager.ResourceManager 是入口類的 main
函數。
其中重點需要分析的就是上面中的 resourceManager.init(conf);
和 resourceManager.start();
,下面分別介紹
初始化流程
如上面的代碼中的 resourceManager.init(conf);
,首先調用父類 AbstractService 中 init
函數
從代碼,可以其服務初始化實現在 ResourceManager
類的 serviceInit
函數,如下
setupDispatcher()
設置的是 Always On 服務,即不考慮HA狀態的一直運行的服務。其初始化的 AsyncDispatcher
內部是 有一個 阻塞的事件隊列,有一個一直運行的執行線程,當阻塞隊列中有事件被放入,執行線程會把事件取出來,並獲取事件的類型,從事件註冊器 Map<Class<? extends Enum>, EventHandler>
中獲取到對應的 EventHandler 對象,並調用該對象的 dispatch 方法分發事件。這樣就完成了一次異步事件調用。
createAndInitActiveServices();
設置的是活動的服務上下文,即需要運行在Active RM節點上的服務,由於本文不重點解析,僅列出啓動服務供參考:
- RMSecretManagerService
- ContainerAllocationExpirer
- AMLivelinessMonitor
- RMNodeLabelsManager
- RMStateStore
- RMApplicationHistoryWriter
- SystemMetricsPublisher
- NodesListManager
- ResourceScheduler
- SchedulerEventDispatcher
- NMLivelinessMonitor
- ResourceTrackerService
- ApplicationMasterService
- ClientRMService
- ApplicationMasterLauncher
- DelegationTokenRenewer
至此,配置加載和子服務初始化工作分析完了,下面我們分析,RM的啓動流程
啓動流程
我們回到ResourceManager類的 resourceManager.start();
繼續分析,同樣首先調用父類 AbstractService 中 start
函數
從代碼,可以其服務初始化實現在 ResourceManager
類的 serviceStart
函數,如下
繼續調用父類 CompositeService 的 serviceStart
函數
循環啓動之前啓動的每個服務。
自此整個Yarn的啓動過程就分析完了…
中央事件調度器-AsyncDispatcher
由於AsyncDispatcher作爲Yarn的一個重要的啓動常駐服務,所以我們在此分析一下。
我們都知道,Yarn是採用了基於事件驅動的併發模型:
- 所有狀態機都實現了EventHandler接口,很多服務(類名通常帶有Service後綴)也實現了該接口,它們都是事件處理器。
- 需要異步處理的事件由中央異步調度器(類名通常帶有Dispatcher後綴)統一接收/派發,需要同步處理的事件直接交給相應的事件處理器。
事件調度就是基於 AsyncDispatcher 實現的,由上面代碼分析可得,AsyncDispatcher也是在yarn啓動過程中 resourceManager.init(conf);
做爲服務初始化的一員,最後在 resourceManager.start();
分別啓動的,因此其啓動最終也是調用 org.apache.hadoop.yarn.event.AsyncDispatcher
的函數 serviceStart
啓動:
從代碼可以看出,其實例化了一個子線程,用於異步處理事件,我們繼續跟進代碼,如下:
可以看出,在線程中循環從阻塞隊列中拿出一個事件,然後分發事件處理,此過程相當於“生產者和消費者模型”中的消費者。而生產者則是由Client通過RPC提交給Yarn後,add到該阻塞隊列,本文不再詳述這部分。
具體的分發原理可以參考:Yarn的事件驅動模型與狀態機