第一章 Hadoop啟動Shell啟動腳本分析--基于hadoop-0.20.2-cdh3u1
Posted on 2012-04-15 16:37 zljpp 閱讀(243) 評論(0) 編輯 收藏歡迎大家相互交流,共同提高技術(shù)。
第一章 Hadoop啟動Shell啟動腳本分析
第一節(jié) start-all.sh腳本
此腳本很簡單,就是根據(jù)運行此腳本的目錄進(jìn)入安裝hadoop目錄下的bin目錄,然后運行啟動hdfs和mapred的啟動腳本。
- bin=`dirname "$0"`
- bin=`cd "$bin"; pwd`
- . "$bin"/hadoop-config.sh
- # start dfs daemons
- "$bin"/start-dfs.sh --config $HADOOP_CONF_DIR
- # start mapred daemons
- "$bin"/start-mapred.sh --config $HADOOP_CONF_DIR
第二節(jié) Start-dfs.sh腳本
此腳本首先檢查是否帶有參數(shù),代碼如下:
- if [ $# -ge 1 ]; then
- nameStartOpt=$1
- shift
- case $nameStartOpt in
- (-upgrade)
- ;;
- (-rollback)
- dataStartOpt=$nameStartOpt
- ;;
- (*)
- echo $usage
- exit 1
- ;;
- esac
- fi
從以上代碼可以看出此腳本只支持upgrade和rollback兩個選項參數(shù),一個參數(shù)用于更新文件系統(tǒng),另一個是回滾文件系統(tǒng)。
然后就開始啟動namenode、datanode和secondarynamenode節(jié)點,執(zhí)行的腳本代碼如下:
- "$bin"/hadoop-daemon.sh --config $HADOOP_CONF_DIR start namenode $nameStartOpt
- "$bin"/hadoop-daemons.sh --config $HADOOP_CONF_DIR start datanode $dataStartOpt
- "$bin"/hadoop-daemons.sh --config $HADOOP_CONF_DIR --hosts masters start secondarynamenode
代碼中的$HADOOP_CONF_DIR是在另一個腳本中設(shè)置的,這個腳本是hadoop-config.sh,后面會詳細(xì)介紹,因為這個腳本在每一個啟動腳本執(zhí)行中都先執(zhí)行,目的是為了檢查和設(shè)置一些環(huán)境變量,例如JAVA_HOME和HADOOP_HOME等,而這個腳本又會執(zhí)行hadoop-env.sh腳本來設(shè)置用戶配置的相關(guān)環(huán)境變量,后面詳細(xì)介紹這兩個腳本。
從上面的腳本代碼可以看出在啟動namenode節(jié)點是在hadoop-daemon.sh腳本中啟動,下面一節(jié)將分析這個腳本。而datanode和secondarynamenode節(jié)點的啟動又會通過hadoop-daemon.sh腳本來執(zhí)行。后面也將分析這個腳本的運行情況。
第三節(jié) hadoop-daemon.sh腳本
在具體介紹這個腳本以前先介紹幾個環(huán)境變量的意義(在這個腳本的注釋部分有介紹):
- HADOOP_CONF_DIR 選擇配置文件目錄。默認(rèn)是${HADOOP_HOME}/conf。
- HADOOP_LOG_DIR 存放日志文件的目錄。默認(rèn)是 PWD 命令產(chǎn)生的目錄
- HADOOP_MASTER host:path where hadoop code should be rsync'd from
- HADOOP_PID_DIR The pid files are stored. /tmp by default.
- HADOOP_IDENT_STRING A string representing this instance of hadoop. $USER by default
- HADOOP_NICENESS The scheduling priority for daemons. Defaults to 0.
這個腳本首先判斷所帶的參數(shù)是否小于1,如果小于就打印使用此腳本的使用幫助,shell代碼如下:
- usage="Usage: hadoop-daemon.sh [--config <conf-dir>] [--hosts hostlistfile] (start|stop) <had oop-command> <args...>"
- if [ $# -le 1 ]; then
- echo $usage
- exit 1
- fi
然后同其他腳本一樣執(zhí)行hadoop-config.sh腳本檢查和設(shè)置相關(guān)環(huán)境變量。對于此腳本,hadoop-config.sh腳本的作用就是把配置文件和主機列表的文件處理好了并設(shè)置相應(yīng)的環(huán)境變量保存它們。
接著保存啟動還是停止的命令和相關(guān)參數(shù),如下(注意:shift的shell腳本的作用就是將shell腳本所帶參數(shù)向前移動一個):
- startStop=$1
- shift
- command=$1
- shift
繼續(xù)就是定義一個用于滾動日志的函數(shù)了,具體就不詳細(xì)分析了。后面是一些根據(jù)配置文件中的配置選項來設(shè)置前面提到的環(huán)境變量,這些環(huán)境變量會用于具體啟動namenode,例如有調(diào)度優(yōu)先級的環(huán)境變量等。
最后就是根據(jù)命令還是控制namenode的啟停(start或stop)了,具體代碼如下:
- case $startStop in
- (start)
- mkdir -p "$HADOOP_PID_DIR"
- if [ -f $_HADOOP_DAEMON_PIDFILE ]; then
- if kill -0 `cat $_HADOOP_DAEMON_PIDFILE` > /dev/null 2>&1; then
- echo $command running as process `cat $_HADOOP_DAEMON_PIDFILE`. Stop it first.
- exit 1
- fi
- fi
- if [ "$HADOOP_MASTER" != "" ]; then
- echo rsync from $HADOOP_MASTER
- rsync -a -e ssh --delete --exclude=.svn --exclude='logs/*' --exclude='contrib/hod/logs/ *' $HADOOP_MASTER/ "$HADOOP_HOME"
- fi
- hadoop_rotate_log $_HADOOP_DAEMON_OUT
- echo starting $command, logging to $_HADOOP_DAEMON_OUT
- cd "$HADOOP_HOME"
- nice -n $HADOOP_NICENESS "$HADOOP_HOME"/bin/hadoop --config $HADOOP_CONF_DIR $command "$@ " < /dev/null
- ;;
- (stop)
- if [ -f $_HADOOP_DAEMON_PIDFILE ]; then
- if kill -0 `cat $_HADOOP_DAEMON_PIDFILE` > /dev/null 2>&1; then
- echo stopping $command
- kill `cat $_HADOOP_DAEMON_PIDFILE`
- else
- echo no $command to stop
- fi
- else
- echo no $command to stop
- fi
- ;;
- (*)
- echo $usage
- exit 1
- ;;
- esac
如果是start就是啟動namenode的命令,那么首先創(chuàng)建存放pid文件的目錄,如果存放pid的文件已經(jīng)存在說明已經(jīng)有namenode節(jié)點已經(jīng)在運行了,那么就先停止在啟動。然后根據(jù)日志滾動函數(shù)生成日志文件,最后就用nice根據(jù)調(diào)度優(yōu)先級啟動namenode,但是最終的啟動還在另一個腳本hadoop,這個腳本是啟動所有節(jié)點的終極腳本,它會選擇一個帶有main函數(shù)的類用java啟動,這樣才到達(dá)真正的啟動java守護(hù)進(jìn)程的效果,這個腳本是啟動的重點,也是我們分析hadoop源碼的入口處,所以后面章節(jié)重點分析。
如果是stop命令就執(zhí)行簡單的停止命令,其他都是錯誤的,打印提示使用此腳本的文檔。
第四節(jié) hadoop-daemons.sh和slaves.sh腳本
這個腳本簡單,因為他最后也是通過上一節(jié)介紹的腳本來啟動的,只是在這之前做了一些特殊處理,就是執(zhí)行另一個腳本slaves.sh,代碼如下:
- exec "$bin/slaves.sh" --config $HADOOP_CONF_DIR cd "$HADOOP_HOME" \;"$bin/hadoop-daemon.sh" --config $HADOOP_CONF_DIR "$@"
Slaves.sh腳本的主要功能就是通過ssh在所有的從節(jié)點上運行啟動從節(jié)點的啟動腳本,就是上面代碼中的最后兩條命令,進(jìn)入hadoop的目錄運行bin目錄下的hadoop-daemon.sh腳本。執(zhí)行這個功能的代碼如下:
- if [ "$HOSTLIST" = "" ]; then
- if [ "$HADOOP_SLAVES" = "" ]; then
- export HOSTLIST="${HADOOP_CONF_DIR}/slaves"
- else
- export HOSTLIST="${HADOOP_SLAVES}"
- fi
- fi
- for slave in `cat "$HOSTLIST"|sed "s/#.*$//;/^$/d"`; do
- ssh $HADOOP_SSH_OPTS $slave {1}quot;${@// /\\ }" \
- 2>&1 | sed "s/^/$slave: /" &
- if [ "$HADOOP_SLAVE_SLEEP" != "" ]; then
- sleep $HADOOP_SLAVE_SLEEP
- fi
- done
- wait
以上代碼首先找到所有從節(jié)點的主機名稱(在slaves文件中,或者配置文件中配置有),然后通過for循環(huán)依次通過ssh遠(yuǎn)程后臺運行啟動腳本程序,最后等待程序完成才退出此shell腳本。
因此這個腳本主要完成的功能就是在所有從節(jié)點執(zhí)行啟動相應(yīng)節(jié)點的腳本。這個腳本執(zhí)行datanode是從slaves文件中找到datanode節(jié)點,執(zhí)行secondarynamenode是在master文件找到節(jié)點主機(在start-dfs.sh腳本中用-hosts master指定的,不然默認(rèn)會找到slaves文件,datanode就是按默認(rèn)找到的)。
第五節(jié) start-mapred.sh腳本
這個腳本就兩句重要代碼,就是分別啟動jobtracker和tasktracker節(jié)點,其他的環(huán)境變量還是通過相應(yīng)的腳本照樣設(shè)置,如下:
- "$bin"/hadoop-daemon.sh --config $HADOOP_CONF_DIR start jobtracker
- "$bin"/hadoop-daemons.sh --config $HADOOP_CONF_DIR start tasktracker
從代碼可以看出還是通過上一節(jié)相同的方式來啟動,具體就不在分析了,請看前一節(jié)。
第六節(jié) hadoop腳本
這個腳本才是重點,前面的腳本執(zhí)行都是為這個腳本執(zhí)行做鋪墊的,這個腳本的功能也是相當(dāng)強大,不僅僅可以啟動各個節(jié)點的服務(wù),還能夠執(zhí)行很多命令和工具。它會根據(jù)傳入的參數(shù)來決定執(zhí)行什么樣的功能(包括啟動各個節(jié)點服務(wù)),下面詳細(xì)介紹這個腳本的執(zhí)行流程和功能。
第一步:切換到bin目錄下運行腳本hadoop-config.sh,代碼如下:
- bin=`dirname "$0"`
- bin=`cd "$bin"; pwd`
- . "$bin"/hadoop-config.sh
第二步:得到hadoop運行實例的名稱和檢測運行hadoop的環(huán)境是否是windows下的linux模擬環(huán)境cygwin,代碼如下:
- HADOOP_IDENT_STRING=${HADOOP_IDENT_STRING:-$USER}
- cygwin=false
- case "`uname`" in
- CYGWIN*) cygwin=true;;
- esac
第三步:判斷參數(shù)個數(shù)是否為0個,是的話打印腳本使用方式并退出,否則就獲得具體命令,獲得命令的代碼如下:
- COMMAND=$1
- shift
第四步:判斷配置文件所在的目錄下是否有hadoop-env.sh腳本,有就執(zhí)行,代碼如下:
- if [ -f "${HADOOP_CONF_DIR}/hadoop-env.sh" ]; then
- . "${HADOOP_CONF_DIR}/hadoop-env.sh"
- fi
第五步:設(shè)置java執(zhí)行的相關(guān)參數(shù),例如JAVA_HOME變量、運行jvm的最大堆空間等,代碼如下:
- if [ "$JAVA_HOME" != "" ]; then
- #echo "run java in $JAVA_HOME"
- JAVA_HOME=$JAVA_HOME
- fi
- if [ "$JAVA_HOME" = "" ]; then
- echo "Error: JAVA_HOME is not set."
- exit 1
- fi
- JAVA=$JAVA_HOME/bin/java
- JAVA_HEAP_MAX=-Xmx1000m
- if [ "$HADOOP_HEAPSIZE" != "" ]; then
- JAVA_HEAP_MAX="-Xmx""$HADOOP_HEAPSIZE""m"
- fi
第六步:設(shè)置CLASSPATH,這一步很重要,因為不設(shè)置的話很多類可能找不到,具體設(shè)置了那些路徑到CLASSPATH看下面的具體代碼:
- CLASSPATH="${HADOOP_CONF_DIR}"
- CLASSPATH=${CLASSPATH}:$JAVA_HOME/lib/tools.jar
- if [ "$HADOOP_USER_CLASSPATH_FIRST" != "" ] && [ "$HADOOP_CLASSPATH" != "" ] ; then
- CLASSPATH=${CLASSPATH}:${HADOOP_CLASSPATH}
- fi
- if [ -d "$HADOOP_HOME/build/classes" ]; then
- CLASSPATH=${CLASSPATH}:$HADOOP_HOME/build/classes
- fi
- if [ -d "$HADOOP_HOME/build/webapps" ]; then
- CLASSPATH=${CLASSPATH}:$HADOOP_HOME/build
- fi
- if [ -d "$HADOOP_HOME/build/test/classes" ]; then
- CLASSPATH=${CLASSPATH}:$HADOOP_HOME/build/test/classes
- fi
- if [ -d "$HADOOP_HOME/build/tools" ]; then
- CLASSPATH=${CLASSPATH}:$HADOOP_HOME/build/tools
- fi
上面代碼省略很大一部分,具體還有那些可以看具體的hadoop腳本。
第七步:根據(jù)第三步保存的命令選擇對應(yīng)的啟動java類,如下:
- if [ "$COMMAND" = "classpath" ] ; then
- if $cygwin; then
- CLASSPATH=`cygpath -p -w "$CLASSPATH"`
- fi
- echo $CLASSPATH
- exit
- elif [ "$COMMAND" = "namenode" ] ; then
- CLASS='org.apache.hadoop.hdfs.server.namenode.NameNode'
- HADOOP_OPTS="$HADOOP_OPTS $HADOOP_NAMENODE_OPTS"
- elif [ "$COMMAND" = "secondarynamenode" ] ; then
- CLASS='org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode'
- HADOOP_OPTS="$HADOOP_OPTS $HADOOP_SECONDARYNAMENODE_OPTS"
- elif [ "$COMMAND" = "datanode" ] ; then
- CLASS='org.apache.hadoop.hdfs.server.datanode.DataNode'
- HADOOP_OPTS="$HADOOP_OPTS $HADOOP_DATANODE_OPTS"
- elif [ "$COMMAND" = "fs" ] ; then
- CLASS=org.apache.hadoop.fs.FsShell
- .....省略很多
- elif [[ "$COMMAND" = -* ]] ; then
- # class and package names cannot begin with a -
- echo "Error: No command named \`$COMMAND' was found. Perhaps you meant \`h adoop ${COMMAND#-}'"
- exit 1
- else
- CLASS=$COMMAND
- fi
具體可以執(zhí)行那些命令從以上代碼完全可以看出來,而且執(zhí)行哪一個命令具體對應(yīng)哪一個類都很有清楚的對應(yīng),讓我們在分析某一個具體功能的代碼的時候能夠很塊找到入口點。從上面代碼最后第二行可以看出hadoop腳本也可以直接運行一個java的jar包或類,這樣方便開發(fā)者測試自己開發(fā)的基于hadoop平臺的程序,看樣子小腳本能夠?qū)W到大量知識。
第八步:如果是cygwin環(huán)境需要轉(zhuǎn)換路徑,代碼如下:
- if $cygwin; then
- CLASSPATH=`cygpath -p -w "$CLASSPATH"`
- HADOOP_HOME=`cygpath -w "$HADOOP_HOME"`
- HADOOP_LOG_DIR=`cygpath -w "$HADOOP_LOG_DIR"`
- TOOL_PATH=`cygpath -p -w "$TOOL_PATH"`
- JAVA_LIBRARY_PATH=`cygpath -p -w "$JAVA_LIBRARY_PATH"`
- fi
第九步:設(shè)置java執(zhí)行需要的本地庫路徑JAVA_LIBRARY_PATH,具體代碼如下:
- if [ -d "${HADOOP_HOME}/build/native" -o -d "${HADOOP_HOME}/lib/native" -o - d "${HADOOP_HOME}/sbin" ]; then
- JAVA_PLATFORM=`CLASSPATH=${CLASSPATH} ${JAVA} -Xmx32m ${HADOOP_JAVA_PLATFO RM_OPTS} org.apache.hadoop.util.PlatformName | sed -e "s/ /_/g"`
- if [ -d "$HADOOP_HOME/build/native" ]; then
- if [ "x$JAVA_LIBRARY_PATH" != "x" ]; then
- JAVA_LIBRARY_PATH=${JAVA_LIBRARY_PATH}:${HADOOP_HOME}/build/native/$ {JAVA_PLATFORM}/lib
- else
- JAVA_LIBRARY_PATH=${HADOOP_HOME}/build/native/${JAVA_PLATFORM}/lib
- fi
- fi
- if [ -d "${HADOOP_HOME}/lib/native" ]; then
- if [ "x$JAVA_LIBRARY_PATH" != "x" ]; then
- JAVA_LIBRARY_PATH=${JAVA_LIBRARY_PATH}:${HADOOP_HOME}/lib/native/${JAV A_PLATFORM}
- else
- JAVA_LIBRARY_PATH=${HADOOP_HOME}/lib/native/${JAVA_PLATFORM}
- fi
- fi
- _JSVC_PATH=${HADOOP_HOME}/sbin/${JAVA_PLATFORM}/jsvc
- fi
- 如果是cygwin環(huán)境需要轉(zhuǎn)換路徑:
- if $cygwin; then
- JAVA_LIBRARY_PATH=`cygpath -p "$JAVA_LIBRARY_PATH"`
- fi
第十步:設(shè)置hadoop可選項變量:HADOOP_OPTS;
第十一步:首先判斷是運行節(jié)點的啟動節(jié)點運行命令還是普通的客戶端命令,然后根據(jù)相關(guān)條件設(shè)置運行的模式(有三種:jvsc、su和normal),代碼如下:
- if [[ "$COMMAND" == *node ]] || [[ "$COMMAND" == *tracker ]]; then
- command_uc=$(echo $COMMAND| tr a-z A-Z) #轉(zhuǎn)換為大寫
- user_var="HADOOP_${command_uc}_USER"
- _HADOOP_DAEMON_USER=$(eval "echo \$user_var")
- _HADOOP_DAEMON_USER=${_HADOOP_DAEMON_USER:-$(id -un)}
- if [ -z "$_HADOOP_DAEMON_USER" ]; then
- echo Please specify a user to run the $COMMAND by setting $user_var
- exit 1
- elif [ "$_HADOOP_DAEMON_USER" == "root" ]; then
- echo May not run daemons as root. Please specify $user_var
- exit 1
- fi
- if [ "$EUID" = "0" ] ; then
- if [ "$COMMAND" == "datanode" ] && [ -x "$_JSVC_PATH" ]; then
- _HADOOP_RUN_MODE="jsvc"
- elif [ -x /bin/su ]; then
- _HADOOP_RUN_MODE="su"
- else
- echo "Daemon wants to run as $_HADOOP_DAEMON_USER but script is runnin g as root"
- echo "and su is not available."
- exit 1
- fi
- else
- if [ "$_HADOOP_DAEMON_USER" != "$(whoami)" ]; then
- echo Daemon wants to run as $_HADOOP_DAEMON_USER but not running as th at user or root.
- exit 1
- fi
- _HADOOP_RUN_MODE="normal"
- fi
- else
- _HADOOP_RUN_MODE="normal"
- fi
第十二步:最后一步就是根據(jù)上面確定的運行模式具體運行命令,只有datanode節(jié)點能夠使用jsvc運行,如下代碼所示:
- case "$_HADOOP_RUN_MODE" in
- jsvc)
- case "$COMMAND" in
- datanode)
- _JSVC_STARTER_CLASS=org.apache.hadoop.hdfs.server.datanode.SecureDat aNodeStarter
- ;;
- *)
- echo "Cannot start $COMMAND with jsvc"
- exit 1
- ;;
- esac
- if [ "$_HADOOP_DAEMON_DETACHED" = "true" ]; then
- _JSVC_FLAGS="-pidfile $_HADOOP_DAEMON_PIDFILE
- -errfile &1
- -outfile $_HADOOP_DAEMON_OUT"
- ese
- .....省略一些代碼,最終執(zhí)行還是下面這一句代碼:
- exec "$_JSVC_PATH" -Dproc_$COMMAND \
- $_JSVC_FLAGS \
- -user "$_HADOOP_DAEMON_USER" \
- -cp "$CLASSPATH" \
- $JAVA_HEAP_MAX $HADOOP_OPTS \
- $_JSVC_STARTER_CLASS "$@"
- ;;
如果是su和normal模式運行,所有的命令都可以正常的使用java來執(zhí)行,如下代碼:
- normal | su)
- # If we need to su, tack the command into a local variable
- if [ $_HADOOP_RUN_MODE = "su" ]; then
- _JAVA_EXEC="su $_HADOOP_DAEMON_USER -s $JAVA --"
- else
- _JAVA_EXEC="$JAVA"
- fi
- if [ "$_HADOOP_DAEMON_DETACHED" = "true" ]; then
- unset _HADOOP_DAEMON_DETACHED
- touch $_HADOOP_DAEMON_OUT
- nohup $_JAVA_EXEC -Dproc_$COMMAND $JAVA_HEAP_MAX $HADOOP_OPTS -classpa th "$CLASSPATH" $CLASS "$@" > "$_HADOOP_DAEMON_OUT" 2>&1 < /dev/null &
- if [ "$EUID" == "0" ]; then
- chown $_HADOOP_DAEMON_USER $_HADOOP_DAEMON_OUT
- fi
- echo $! > "$_HADOOP_DAEMON_PIDFILE"
- sleep 1
- head "$_HADOOP_DAEMON_OUT"
- else
- exec $_JAVA_EXEC -Dproc_$COMMAND $JAVA_HEAP_MAX $HADOOP_OPTS -classpat h "$CLASSPATH" $CLASS "$@"
- fi
- ;;
到此為止所有腳本執(zhí)行完畢,剩余就是不能識別模式的錯誤處理和提示。在執(zhí)行具體命令的時候可能涉及到用戶名的檢測,例如su可以指定一個用戶名來運行,如果不指定就按照linux上的用戶名來運行。
第七節(jié) hadoop-config.sh和hadoop-env.sh腳本
這兩個腳本基本上在上面分析的所有腳本都涉及到,他們的主要功能就是根據(jù)命令行參數(shù)來設(shè)置一些配置文件的路徑已經(jīng)環(huán)境變量的值,都是一些公共設(shè)置,所以在執(zhí)行每個腳本的時候都設(shè)置一遍。具體的代碼就不詳細(xì)分析了!
第八節(jié) 總結(jié)
這個啟動腳本還是比較復(fù)雜,從這個啟動腳本我學(xué)習(xí)到很多知識,第一就是學(xué)到很多有關(guān)于shell編程的知識,里面很多shell編程的技巧值得學(xué)習(xí)和借鑒;第二,通過整個啟動過程的了解,知道了運行hadoop需要設(shè)置的很多東西,包括我們在配置文件中配置的一些選項是怎么起作用的、設(shè)置了哪些classpath路徑等,第三,詳細(xì)了解了所有能夠通過hadoop執(zhí)行的命令。還有其他許多收獲竟在不言中。