Flink 1.13 源码解析——启动脚本解析

本文详细解析了Flink1.13版本的启动流程,包括standalone模式下主节点和从节点的启动过程。从start-cluster.sh脚本开始,逐步介绍了如何寻找并启动主节点和从节点。

Flink 1.13 源码解析 目录汇总

目录

前言:

寻找主节点:start-cluster.sh

寻找从节点:config.sh TMWorkers

主节点启动准备:jobmanager.sh

从节点启动准备:taskmanager.sh

1、根据传入的ENTRYPOINT参数确定入口类

2、将入口类作为参数启动jar


前言:

其实看这些大数据分布式应用的流程大致都差不多,拿到一个分布式应用源码,第一个问题一般都是考虑我应该去从哪个类入手?整个应用的入口类又是什么?针对这些问题,我们都能从应用的启动脚本里找到答案。下面我们以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做了什么

这个脚本很长,但是里面主要分为两部分:

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源码的入口来一探究竟了。

下一章:Flink 1.13 源码解析——JobManager启动流程概览

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EdwardsWang丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值