Flink 1.13 源码解析 目录汇总
目录
前言:
其实看这些大数据分布式应用的流程大致都差不多,拿到一个分布式应用源码,第一个问题一般都是考虑我应该去从哪个类入手?整个应用的入口类又是什么?针对这些问题,我们都能从应用的启动脚本里找到答案。下面我们以Flink入手,来看看Flink的启动脚本都做了什么。
寻找主节点:start-cluster.sh
我们本次的源码分析是基于Flink 1.13 standalone模式进行,以此模式启动,我们首先来看start-cluster.sh脚本
#!/usr/bin/env bash
bin=`dirname "$0"`
bin=`cd "$bin"; pwd`
. "$bin"/config.sh
# Start the JobManager instance(s)
shopt -s nocasematch
# 判断是否为HA,若是则读取配置文件看哪个是主节点
if [[ $HIGH_AVAILABILITY == "zookeeper" ]]; then
# HA Mode
readMasters
echo "Starting HA cluster with ${#MASTERS[@]} masters."
# 逐一启动Master
for ((i=0;i<${#MASTERS[@]};++i)); do
master=${MASTERS[i]}
webuiport=${WEBUIPORTS[i]}
# 判断本机是否为Master,若是则直接调用脚本启动
if [ ${MASTERS_ALL_LOCALHOST} = true ] ; then
# 通过jobmanager.sh脚本启动,该脚本就是用来启动主节点的
"${FLINK_BIN_DIR}"/jobmanager.sh start "${master}" "${webuiport}"
else
#若不是则发送远程命令启动Master
ssh -n $FLINK_SSH_OPTS $master -- "nohup /bin/bash -l \"${FLINK_BIN_DIR}/jobmanager.sh\" start ${master} ${webuiport} &"
fi
done
else
# 若不是HA则直接启动
echo "Starting cluster."
# Start single JobManager on this machine
"$FLINK_BIN_DIR"/jobmanager.sh start
fi
shopt -u nocasematch
# Start TaskManager instance(s)
# 启动TaskManager,这是一个函数,该函数在config.sh里面,我们去看config.sh
TMWorkers start
内容明确也很简单,总共做了以下几件事:
1、先判断是否是HA模式,如果是,则去读flink-conf.yaml文件来判断哪个是主节点
2、若是HA模式,则遍历所有Master,逐一启动
3、若不是HA模式则直接调用本地jobmanager.sh脚本来启动
4、若是HA模式,则判断本机是否为Master,如果是则直接调用本地的jobmanager.sh脚本来启动
5、若是HA模式,且如果本机不是Master,则调用命令来远程启动Master
6、最后,调用TMWorkers函数,这个函数在config.sh里
通过上面6个步骤我们不难看出,启动主节点,使用的脚本就是jobmanager.sh脚本,我们在稍后再去看jobmanager.sh的内容,我们先去config.sh里看看TMWorkers函数都做了什么
寻找从节点:config.sh TMWorkers
config.sh里内容很多,我们主要看TMWorkers函数内容,直接ctrl+f搜索
# 启动从节点
# starts or stops TMs on all workers
# TMWorkers start|stop
TMWorkers() {
CMD=$1
readWorkers
# 判断本机是否为Worker
if [ ${WORKERS_ALL_LOCALHOST} = true ] ; then
# all-local setup
# 如果是则直接起动,调用taskmanager.sh脚本来启动,我们去看taskmanager.sh脚本
for worker in ${WORKERS[@]}; do
"${FLINK_BIN_DIR}"/taskmanager.sh "${CMD}"
done
else
# non-local setup
# start/stop TaskManager instance(s) using pdsh (Parallel Distributed Shell) when available
command -v pdsh >/dev/null 2>&1
if [[ $? -ne 0 ]]; then
# 如果本机不是Worker则调用命令远程启动
for worker in ${WORKERS[@]}; do
ssh -n $FLINK_SSH_OPTS $worker -- "nohup /bin/bash -l \"${FLINK_BIN_DIR}/taskmanager.sh\" \"${CMD}\" &"
done
else
PDSH_SSH_ARGS="" PDSH_SSH_ARGS_APPEND=$FLINK_SSH_OPTS pdsh -w $(IFS=, ; echo "${WORKERS[*]}") \
"nohup /bin/bash -l \"${FLINK_BIN_DIR}/taskmanager.sh\" \"${CMD}\""
fi
fi
}
TMWorkers函数主要做了以下几件事:
1、判断本机是否为worker
2、如果本机是worker则直接调用本地taskmanager.sh脚本启动
3、如果本机不是worker,则通过调用命令远程启动
功能也很明确,就是准备从节点taskmanager的启动,可以看到不论本机是不是Worker,它都会去调用taskmanager.sh这个脚本,我们稍后去看看这个脚本的内容,这里先看看Jobmanager.sh脚本都做了什么
主节点启动准备:jobmanager.sh
jobmanager.sh的内容也不多
#!/usr/bin/env bash
# Start/stop a Flink JobManager.
USAGE="Usage: jobmanager.sh ((start|start-foreground) [host] [webui-port])|stop|stop-all"
STARTSTOP=$1
HOST=$2 # optional when starting multiple instances
WEBUIPORT=$3 # optional when starting multiple instances
if [[ $STARTSTOP != "start" ]] && [[ $STARTSTOP != "start-foreground" ]] && [[ $STARTSTOP != "stop" ]] && [[ $STARTSTOP != "stop-all" ]]; then
echo $USAGE
exit 1
fi
bin=`dirname "$0"`
bin=`cd "$bin"; pwd`
. "$bin"/config.sh
# 注意该变量的值,如果使用start-cluster脚本启动flink,启动的就是一个standalonesession模式的集群
ENTRYPOINT=standalonesession
if [[ $STARTSTOP == "start" ]] || [[ $STARTSTOP == "start-foreground" ]]; then
# Add JobManager-specific JVM options
export FLINK_ENV_JAVA_OPTS="${FLINK_ENV_JAVA_OPTS} ${FLINK_ENV_JAVA_OPTS_JM}"
parseJmArgsAndExportLogs "${ARGS[@]}"
args=("--configDir" "${FLINK_CONF_DIR}" "--executionMode" "cluster")
if [ ! -z $HOST ]; then
args+=("--host")
args+=("${HOST}")
fi
if [ ! -z $WEBUIPORT ]; then
args+=("--webui-port")
args+=("${WEBUIPORT}")
fi
if [ ! -z "${DYNAMIC_PARAMETERS}" ]; then
args+=(${DYNAMIC_PARAMETERS[@]})
fi
fi
# 判断是否为前台模式启动
if [[ $STARTSTOP == "start-foreground" ]]; then
exec "${FLINK_BIN_DIR}"/flink-console.sh $ENTRYPOINT "${args[@]}"
else
# 若不是,则调用flink-daemon.sh脚本,并将ENTRYPOINT=standalonesession作为参数传入
"${FLINK_BIN_DIR}"/flink-daemon.sh $STARTSTOP $ENTRYPOINT "${args[@]}"
fi
这个脚本里最重要的内容,就是配置了ENTRYPOINT=standalonesession这个变量,确定了集群启动模式,从这里我们也可以看出,如果使用start-cluster.sh脚本启动flink,那么我们启动的就是一个standalonesession模式的集群。
我们继续往下看,在配置完ENTRYPOINT的值后,脚本去调用了flink-daemon.sh脚本,并将ENTRYPOINT的值传入。flink-daemon.sh脚本的内容我们稍等下去看,先来看看taskmanager.sh脚本做了什么。
从节点启动准备:taskmanager.sh
#!/usr/bin/env bash
# Start/stop a Flink TaskManager.
USAGE="Usage: taskmanager.sh (start|start-foreground|stop|stop-all)"
STARTSTOP=$1
ARGS=("${@:2}")
if [[ $STARTSTOP != "start" ]] && [[ $STARTSTOP != "start-foreground" ]] && [[ $STARTSTOP != "stop" ]] && [[ $STARTSTOP != "stop-all" ]]; then
echo $USAGE
exit 1
fi
bin=`dirname "$0"`
bin=`cd "$bin"; pwd`
. "$bin"/config.sh
# 注意该变量的值
ENTRYPOINT=taskexecutor
if [[ $STARTSTOP == "start" ]] || [[ $STARTSTOP == "start-foreground" ]]; then
# if no other JVM options are set, set the GC to G1
if [ -z "${FLINK_ENV_JAVA_OPTS}" ] && [ -z "${FLINK_ENV_JAVA_OPTS_TM}" ]; then
export JVM_ARGS="$JVM_ARGS -XX:+UseG1GC"
fi
# Add TaskManager-specific JVM options
export FLINK_ENV_JAVA_OPTS="${FLINK_ENV_JAVA_OPTS} ${FLINK_ENV_JAVA_OPTS_TM}"
# Startup parameters
parseTmArgsAndExportLogs "${ARGS[@]}"
if [ ! -z "${DYNAMIC_PARAMETERS}" ]; then
ARGS=(${DYNAMIC_PARAMETERS[@]} "${ARGS[@]}")
fi
ARGS=("--configDir" "${FLINK_CONF_DIR}" "${ARGS[@]}")
fi
if [[ $STARTSTOP == "start-foreground" ]]; then
exec "${FLINK_BIN_DIR}"/flink-console.sh $ENTRYPOINT "${ARGS[@]}"
else
if [[ $FLINK_TM_COMPUTE_NUMA == "false" ]]; then
# Start a single TaskManager
"${FLINK_BIN_DIR}"/flink-daemon.sh $STARTSTOP $ENTRYPOINT "${ARGS[@]}"
else
# Example output from `numactl --show` on an AWS c4.8xlarge:
# policy: default
# preferred node: current
# physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
# cpubind: 0 1
# nodebind: 0 1
# membind: 0 1
read -ra NODE_LIST <<< $(numactl --show | grep "^nodebind: ")
for NODE_ID in "${NODE_LIST[@]:1}"; do
# Start a TaskManager for each NUMA node
# 依然是调用 flink-daemon.sh 脚本,并将ENTRYPOINT=taskexecutor作为参数传入
numactl --membind=$NODE_ID --cpunodebind=$NODE_ID -- "${FLINK_BIN_DIR}"/flink-daemon.sh $STARTSTOP $ENTRYPOINT "${ARGS[@]}"
done
fi
fi
脚本里有很多内容,我们来看最重要的两点:
1、为ENTRYPOINT赋值taskexecutor
2、调用flink-daemon.sh脚本,并将ENTRYPOINT传入
这里我们看到,再一次的调用了flink-daemon.sh脚本,现在我们去看看flink-daemon.sh做了什么
启动:flink-daemon.sh
这个脚本很长,但是里面主要分为两部分:
1、根据传入的ENTRYPOINT参数确定入口类
在前面的jobmanager.sh我们将standalonesession作为参数传入了该脚本,在taskmanager.sh脚本中我们将taskexecutor作为参数传入了该脚本,可以看到这个操作就是为了确定主节点和从节点的入口类分别为什么
# 判断传入变量
case $DAEMON in
(taskexecutor)
# 启动从节点taskexecutor的主类
CLASS_TO_RUN=org.apache.flink.runtime.taskexecutor.TaskManagerRunner
;;
(zookeeper)
CLASS_TO_RUN=org.apache.flink.runtime.zookeeper.FlinkZooKeeperQuorumPeer
;;
(historyserver)
CLASS_TO_RUN=org.apache.flink.runtime.webmonitor.history.HistoryServer
;;
(standalonesession)
# 启动主节点的主类
CLASS_TO_RUN=org.apache.flink.runtime.entrypoint.StandaloneSessionClusterEntrypoint
;;
(standalonejob)
CLASS_TO_RUN=org.apache.flink.container.entrypoint.StandaloneApplicationClusterEntryPoint
;;
(*)
echo "Unknown daemon '${DAEMON}'. $USAGE."
exit 1
;;
esac
2、将入口类作为参数启动jar
在拿到入口类后,直接启动
case $STARTSTOP in
(start)
# Print a warning if daemons are already running on host
if [ -f "$pid" ]; then
active=()
while IFS='' read -r p || [[ -n "$p" ]]; do
kill -0 $p >/dev/null 2>&1
if [ $? -eq 0 ]; then
active+=($p)
fi
done < "${pid}"
count="${#active[@]}"
if [ ${count} -gt 0 ]; then
echo "[INFO] $count instance(s) of $DAEMON are already running on $HOSTNAME."
fi
fi
# Evaluate user options for local variable expansion
FLINK_ENV_JAVA_OPTS=$(eval echo ${FLINK_ENV_JAVA_OPTS})
echo "Starting $DAEMON daemon on host $HOSTNAME."
# 启动传入参数的主类
"$JAVA_RUN" $JVM_ARGS ${FLINK_ENV_JAVA_OPTS} "${log_setting[@]}" -classpath "`manglePathList "$FLINK_TM_CLASSPATH:$INTERNAL_HADOOP_CLASSPATHS"`" ${CLASS_TO_RUN} "${ARGS[@]}" > "$out" 200<&- 2>&1 < /dev/null &
mypid=$!
# Add to pid file if successful start
if [[ ${mypid} =~ ${IS_NUMBER} ]] && kill -0 $mypid > /dev/null 2>&1 ; then
echo $mypid >> "$pid"
else
echo "Error starting $DAEMON daemon."
exit 1
fi
;;
到此为止,我们找到了真正启动主节点和从节点的地方,也找到了主节点的入口类为org.apache.flink.runtime.entrypoint.StandaloneSessionClusterEntrypoint,从节点的入口类为org.apache.flink.runtime.taskexecutor.TaskManagerRunner。接下来我们就可以将这两个类作为Flink源码的入口来一探究竟了。