start-dfs.sh:从代码的注释上看,这个代码文件可以分为几个部分:NameNode启动前的环境配置工作、NameNode启动、DataNode启动、Secondary NameNode启动、quorumjournal node启动、ZKFC启动。
在启动前的环境配置工作中,这段代码调用了另一个脚本“hdfs-config.sh"。
if [[ -f "${HADOOP_LIBEXEC_DIR}/hdfs-config.sh" ]]; then
. "${HADOOP_LIBEXEC_DIR}/hdfs-config.sh"
hdfs-config.sh在export了很多变量后它又调用了hadoop-config.sh。
if [[ -n "${HADOOP_COMMON_HOME}" ]] &&
[[ -e "${HADOOP_COMMON_HOME}/libexec/hadoop-config.sh" ]]; then
. "${HADOOP_COMMON_HOME}/libexec/hadoop-config.sh"
elif [[ -e "${HADOOP_LIBEXEC_DIR}/hadoop-config.sh" ]]; then
. "${HADOOP_LIBEXEC_DIR}/hadoop-config.sh"
elif [ -e "${HADOOP_HOME}/libexec/hadoop-config.sh" ]; then
. "${HADOOP_HOME}/libexec/hadoop-config.sh"
转到hadoop-config.sh,这个脚本又会调用hadoop-functions.sh。之后hadoop-config.sh执行了很多在hadoop-functions.sh里定义的函数,这些函数完成了环境配置的工作(例如设置HADOOP_XXX_HOME等环境变量、执行shell profile文件的配置等等),在这里不作分析。
hadoo-functions.sh这个文件非常重要,因为这个文件定义了很多要被用到的函数。
回到start-dfs.sh,可以观察到启动NameNode、DataNode、SecondaryNameNode、JournalNode、ZKFC的代码都相当一致,其核心都是调用hadoop_uservar_su函数。
hadoop_uservar_su hdfs namenode "${HADOOP_HDFS_HOME}/bin/hdfs" \
--workers \
--config "${HADOOP_CONF_DIR}" \
--hostnames "${NAMENODES}" \
--daemon start \
namenode ${nameStartOpt}
HADOOP_HDFS_HOME在hdfs-config.sh里设置,其值就是HADOOP_HOME的值;NAMENODES在这段代码之前被设置,由./bin/hdfs getconf -namenodes查询,如果没有则设置为$(hostname);至于nameStartOpt,要是运行start-dfs.sh时没有带任何参数则为其值为空。
hadoop_uservar_su定义在hadoop-functions.sh:
## @description Execute a command via su when running as root
## @description with extra support for commands that might
## @description legitimately start as root (e.g., datanode)
function hadoop_uservar_su{
...
if hadoop_privilege_check; then
hadoop_su "${!uvar}" "$@"
else
"$@"
...
}
实际上就是和用户权限相关的操作,hadoop_privilege_check就是检查是否运行在root权限。但不管用户权限如何,最终都要运行"$@“,$@的值就是参数列表,”$@"就会把参数作为命令运行。所以上文调用hadoop_uservar_su最终会调用(以本机为例):
hdfs --workers \
--config "${HADOOP_HOME}/etc/hadoop" \
--hostnames "localhost" \
--daemon start \
namenode
hdfs也是个脚本文件。hdfs会调用hdfs-config.sh,而后者会调用hadoop-config.sh。不同于之前没带参数时调用hadoop-config.sh,此次调用一共有8个参数,它们都会传递给hadoop_parse_args函数,最后只有一个剩下了,即是namenode。不过在调用hadoop_parse_args之前,会用变量HADOOP_USER_PARAMS把所有参数保存起来,因为后面会用到。
#hadoop-config.sh
HADOOP_USER_PARAMS=("$@")
hadoop_parse_args "$@"
#hadoop-functions.sh
function hadoop_parse_args
while true; do
case $1 in
--config)
shift
confdir=$1
shift
...
if [[ -d "${confdir}" ]]; then
HADOOP_CONF_DIR="${confdir}"
--daemon)
shift
HADOOP_DAEMON_MODE=$1
shift
--hostnames)
shift
HADOOP_WORKER_NAMES="$1"
shift
--workers)
shift
HADOOP_WORKER_MODE=true
这里设置了一些变量,像HADOOP_DAEMON_MODE用来区分是start还是stop NameNode,HADOOP_WORKER_MODE在后面也会决定代码分支。
之后控制流就回到hdfs上
#hdfs
HADOOP_SUBCMD=$1
shift
HADOOP_SUBCMD_ARGS=("$@")
hdfscmd_case "${HADOOP_SUBCMD}" "${HADOOP_SUBCMD_ARGS[@]}"
由于参数只剩下namenode了,所以HADOOP_SUBCMD的值是namenode,而HADOOP_SUBCMD_ARGS为空值
hdfscmd_case就定义在hdfs中:
function hdfscmd_case
case ${subcmd} in
namenode)
HADOOP_SUBCMD_SUPPORTDAEMONIZATION="true"
HADOOP_CLASSNAME='org.apache.hadoop.hdfs.server.namenode.NameNode'
hadoop_add_param HADOOP_OPTS hdfs.audit.logger "-Dhdfs.audit.logger=${HDFS_AUDIT_LOGGER}"
可以看到这个函数又设置了一些变量,其中重要的是设置了HADOOP_CLASSNAME。
之后的调用:
#hdfs
if [[ ${HADOOP_WORKER_MODE} = true ]]; then
hadoop_common_worker_mode_execute "${HADOOP_HDFS_HOME}/bin/hdfs" "${HADOOP_USER_PARAMS[@]}"
#hadoop-functions.sh
function hadoop_common_worker_mode_execute
hadoop_connect_to_hosts -- "${argv[@]}"
#hadoop-functions.sh
function hadoop_connect_to_hosts
tmpslvnames=$(echo ${HADOOP_WORKER_NAMES} | tr -s ' ' ,)
PDSH_SSH_ARGS_APPEND="${HADOOP_SSH_OPTS}" pdsh \
-f "${HADOOP_SSH_PARALLEL}" \
-w "${tmpslvnames}" $"${@// /\\ }" 2>&1
可以看到最后会调用pdsh,pdsh会在远程目标主机执行操作。在我的机器里pdsh的调用参数,HADOOP_SSH_PARALLEL的值是10,tmpslvnames的值是localhost,$“${@// /\ }“的值是”-- $HADOOP_HOME/bin/hdfs --config $HADOOP_HOME/etc/hadoop --daemon start namenode”
此时在本地shell脚本的执行在打开pdsh后函数返回后就结束了,之后的操作交由远程主机(我这里是localhost)继续处理。
远程主机将运行hdfs,而参数为"–config $HADOOP_HOME/etc/hadoop --daemon start namenode",与之前的hdfs调用最大的不同是没了–workers参数,所以脚本不会调用hadoop_common_worker_mode_execute,而是hadoop_generic_java_subcmd_handler:
#hdfs
# everything is in globals at this point, so call the generic handler
hadoop_generic_java_subcmd_handler
#hadoo-functions.sh
function hadoop_generic_java_subcmd_handler
if [[ "${HADOOP_SUBCMD_SUPPORTDAEMONIZATION}" = true ]]; then
if [[ "${HADOOP_SUBCMD_SECURESERVICE}" = true ]]; then
...
else
hadoop_daemon_handler \
"${HADOOP_DAEMON_MODE}" \
"${HADOOP_SUBCMD}" \
"${HADOOP_CLASSNAME}" \
"${daemon_pidfile}" \
"${daemon_outfile}" \
"${HADOOP_SUBCMD_ARGS[@]}"
这里的参数分别为,HADOOP_DAEMON_MODE=start,HADOOP_SUBCMD=namenode,HADOOP_CLASSNAME=org.apache.hadoop.hdfs.server.namenode.NameNode,而剩下两个参数由机器自行得出,HADOOP_SUBCMD_ARGS为空。
function hadoop_daemon_handler
case ${daemonmode} in
start|default)
...
hadoop_start_daemon_wrapper "${daemonname}" \
"${class}" "${daemon_pidfile}" "${daemon_outfile}" "$@"
//前两个参数分别为namenode, org.apache.hadoop.hdfs.server.namenode.NameNode
function hadoop_start_daemon_wrapper
hadoop_start_daemon "${daemonname}" \
"$class" \
"${pidfile}" \
"$@" >> "${outfile}" 2>&1 < /dev/null &
(( counter=0 ))
function hadoop_start_daemon
exec "${JAVA}" "-Dproc_${command}" ${HADOOP_OPTS} "${class}" "$@"
最后执行运行java,使用选项"-Dproc_namenode",class为"org.apache.hadoop.hdfs.server.namenode.NameNode",HADOOP_OPTS有一长串,$@为pid文件。