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