2021-07-09

hadoop源码_hdfs启动流程_1_NameNode

执行start-dfs.sh脚本后,集群是如何启动的?
本文阅读并注释了start-dfs脚本,以及namenode和datanode的启动主要流程流程源码。

阅读源码前准备

源码获取

  1. 拉取Apache Hadoop官方源码

    https://github.com/apache/hadoop

  2. 用idea打开...

  3. 切换到想看的版本...

    这里用的最新版本3.3.1

阅读目标

​ 本篇的阅读目标是搞明白hadoop中的start-dfs.sh启动脚本执行后都做了什么,hadoop中的NameNode,DataNode启动过程等大致流程,不会细追细节

start-dfs.sh 干了什么

hdfs集群的启动命令为:start-dfs.sh, 脚本的位置在下图中:

![image-

​ 脚本中大致分位两块内容,第一部分是调用hdfs-config.sh脚本配置hdfs以及hadoop的参数以及环境等,第二部分是启动datanode、namenode以及secondary namenode等等。我们的重点是看第二部分的启动流程。

hdfs-config 简述

​ start-dfs.sh中启动hdfs-config.sh的代码如下:

# let's locate libexec...if [[ -n "${HADOOP_HOME}" ]]; then HADOOP_DEFAULT_LIBEXEC_DIR="${HADOOP_HOME}/libexec"else HADOOP_DEFAULT_LIBEXEC_DIR="${bin}/../libexec"fiHADOOP_LIBEXEC_DIR="${HADOOP_LIBEXEC_DIR:-$HADOOP_DEFAULT_LIBEXEC_DIR}"# shellcheck disable=SC2034HADOOP_NEW_CONFIG=trueif [[ -f "${HADOOP_LIBEXEC_DIR}/hdfs-config.sh" ]]; then . "${HADOOP_LIBEXEC_DIR}/hdfs-config.sh"else echo "ERROR: Cannot execute ${HADOOP_LIBEXEC_DIR}/hdfs-config.sh." 2>&1 exit 1fi

​ 在hdfs-config.sh脚本中会尝试启动hdfs-evn.sh脚本(如果存在)
​ 之后会检查以及设置HDFS的各种参数,例如:

 # turn on the defaults export HDFS_AUDIT_LOGGER=${HDFS_AUDIT_LOGGER:-INFO,NullAppender} export HDFS_NAMENODE_OPTS=${HDFS_NAMENODE_OPTS:-"-Dhadoop.security.logger=INFO,RFAS"} export HDFS_SECONDARYNAMENODE_OPTS=${HDFS_SECONDARYNAMENODE_OPTS:-"-Dhadoop.security.logger=INFO,RFAS"} export HDFS_DATANODE_OPTS=${HDFS_DATANODE_OPTS:-"-Dhadoop.security.logger=ERROR,RFAS"} export HDFS_PORTMAP_OPTS=${HDFS_PORTMAP_OPTS:-"-Xmx512m"} # depending upon what is being used to start Java, these may need to be # set empty. (thus no colon) export HDFS_DATANODE_SECURE_EXTRA_OPTS=${HDFS_DATANODE_SECURE_EXTRA_OPTS-"-jvm server"} export HDFS_NFS3_SECURE_EXTRA_OPTS=${HDFS_NFS3_SECURE_EXTRA_OPTS-"-jvm server"}

​ 再之后会启动hadoop-config.sh脚本:

# shellcheck source=./hadoop-common-project/hadoop-common/src/main/bin/hadoop-config.shif [[ -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"else echo "ERROR: Hadoop common not found." 2>&1 exit 1fi

hadoop-config.sh是最基本的、公用的环境变量配置脚本,会再调用etc/hadoop/hadoop-env.sh脚本。主要是配置java启动项相关参数,比如java执行环境的classpath等。

hdfs-config.sh一系列脚本的整体功能就是保证启动hdfs集群前对hdfs和hadoop的各种环境变量进行配置。

start-dfs.sh后续就是逐步启动各个节点(namenodes,datanodes,secondary namenodes,quorumjournal nodes,quorumjournal nodes),如果是ha集群还会启动ZK Failover controllers

NameNode 启动流程

脚本代码

start-dfs.sh中启动namenode的代码:

#---------------------------------------------------------# namenodes# 找到配置中(如果没有配置取当前节点为)的namenode节点NAMENODES=$("${HADOOP_HDFS_HOME}/bin/hdfs" getconf -namenodes 2>/dev/null)if [[ -z "${NAMENODES}" ]]; then NAMENODES=$(hostname)fi# 执行启动命令echo "Starting namenodes on [${NAMENODES}]"hadoop_uservar_su hdfs namenode "${HADOOP_HDFS_HOME}/bin/hdfs" \ --workers \ --config "${HADOOP_CONF_DIR}" \ --hostnames "${NAMENODES}" \ --daemon start \ namenode ${nameStartOpt}HADOOP_JUMBO_RETCOUNTER=$?

hadoop-hdfs > src > mian > bin > hdfs中查看namenode命令:

# 命令描述:用于启动namenode hadoop_add_subcommand "namenode" daemon "run the DFS namenode" # 命令处理程序 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}" ;; # 执行函数命令# everything is in globals at this point, so call the generic handlerhadoop_generic_java_subcmd_handler

这里就定位到了具体的处理类org.apache.hadoop.hdfs.server.namenode.NameNode。继续跟进到hadoop_generic_java_subcmd_handler函数定义的地方-脚本hdfs

## @description Handle subcommands from main program entries## @audience private## @stability evolving## @replaceable yesfunction hadoop_generic_java_subcmd_handler{# ...... 省略 # do the hard work of launching a daemon or just executing our interactive # java class if [[ "${HADOOP_SUBCMD_SUPPORTDAEMONIZATION}" = true ]]; then if [[ "${HADOOP_SUBCMD_SECURESERVICE}" = true ]]; then  hadoop_secure_daemon_handler \  "${HADOOP_DAEMON_MODE}" \  "${HADOOP_SUBCMD}" \  "${HADOOP_SECURE_CLASSNAME}" \  "${daemon_pidfile}" \  "${daemon_outfile}" \  "${priv_pidfile}" \  "${priv_outfile}" \  "${priv_errfile}" \  "${HADOOP_SUBCMD_ARGS[@]}" else  hadoop_daemon_handler \  "${HADOOP_DAEMON_MODE}" \  "${HADOOP_SUBCMD}" \  "${HADOOP_CLASSNAME}" \  "${daemon_pidfile}" \  "${daemon_outfile}" \  "${HADOOP_SUBCMD_ARGS[@]}" fi exit $? else hadoop_java_exec "${HADOOP_SUBCMD}" "${HADOOP_CLASSNAME}" "${HADOOP_SUBCMD_ARGS[@]}" fi}function hadoop_java_exec{ # run a java command. this is used for # non-daemons local command=$1 local class=$2 shift 2 hadoop_debug "Final CLASSPATH: ${CLASSPATH}" hadoop_debug "Final HADOOP_OPTS: ${HADOOP_OPTS}" hadoop_debug "Final JAVA_HOME: ${JAVA_HOME}" hadoop_debug "java: ${JAVA}" hadoop_debug "Class name: ${class}" hadoop_debug "Command line options: $*" export CLASSPATH #shellcheck disable=SC2086 exec "${JAVA}" "-Dproc_${command}" ${HADOOP_OPTS} "${class}" "$@"}

可以看到最终还是利用java命令来执行该类。

NameNode 源码

​ 在源码中定位到org.apache.hadoop.hdfs.server.namenode.NameNode类。按照类的加载顺序来看NameNode启动流程:

静态代码块

 static{ HdfsConfiguration.init(); }// 继续跟进代码,进入HdfsConfiguration类中:
@InterfaceAudience.Privatepublic class HdfsConfiguration extends Configuration { static { addDeprecatedKeys(); // 加载默认的配置文件 Configuration.addDefaultResource("hdfs-default.

mian方法

 public static void main(String argv[]) throws Exception { //分析传入的参数,是否是帮助参数 if (DFSUtil.parseHelpArgument(argv, NameNode.USAGE, System.out, true)) {  System.exit(0); } try {  //打印一些启动日志信息  StringUtils.startupShutdownMessage(NameNode.class, argv, LOG);  //创建namenode  NameNode namenode = createNameNode(argv, null);  if (namenode != null) {  //加入集群,HA,联邦集群都是有多个NameNode  namenode.join();  } } catch (Throwable e) {  LOG.error("Failed to start namenode.", e);  terminate(1, e); } }

需要关注的是NameNode namenode = createNameNode(argv, null);

 public static NameNode createNameNode(String argv[], Configuration conf)  throws IOException { // 日志信息 LOG.info("createNameNode " + Arrays.asList(argv)); if (conf == null)  // 准备配置文件对象  conf = new HdfsConfiguration(); // 将一些通用参数解析到配置中。 GenericOptionsParser hParser = new GenericOptionsParser(conf, argv); argv = hParser.getRemainingArgs(); // 解析其余的NameNode特定参数,放到配置中。 StartupOption startOpt = parseArguments(argv); if (startOpt == null) {  printUsage(System.err);  return null; } setStartupOption(conf, startOpt); boolean aborted = false; // 针对startup对象进行switch case 选择 switch (startOpt) { case FORMAT:  // 格式化  // 安装hadoop后第一次启动之前要执行的格式化命令 hadoop namenode -format  aborted = format(conf, startOpt.getForceFormat(),   startOpt.getInteractiveFormat());  terminate(aborted ? 1 : 0);  return null; // avoid javac warning case GENCLUSTERID:  String clusterID = NNStorage.newClusterID();  LOG.info("Generated new cluster id: {}", clusterID);  terminate(0);  return null; case ROLLBACK:  aborted = doRollback(conf, true);  terminate(aborted ? 1 : 0);  return null; // avoid warning case BOOTSTRAPSTANDBY:  String[] toolArgs = Arrays.copyOfRange(argv, 1, argv.length);  int rc = BootstrapStandby.run(toolArgs, conf);  terminate(rc);  return null; // avoid warning case INITIALIZESHAREDEDITS:  aborted = initializeSharedEdits(conf,   startOpt.getForceFormat(),   startOpt.getInteractiveFormat());  terminate(aborted ? 1 : 0);  return null; // avoid warning case BACKUP: case CHECKPOINT:  NamenodeRole role = startOpt.toNodeRole();  DefaultMetricsSystem.initialize(role.toString().replace(" ", ""));  return new BackupNode(conf, role); case RECOVER:  NameNode.doRecovery(startOpt, conf);  return null; case METADATAVERSION:  printMetadataVersion(conf);  terminate(0);  return null; // avoid javac warning case UPGRADEONLY:  DefaultMetricsSystem.initialize("NameNode");  new NameNode(conf);  terminate(0);  return null; default:  // 正常启动的时候就会走到这里  // metrics:度量系统记录详细运行信息  DefaultMetricsSystem.initialize("NameNode");  // 初始化NameNode  return new NameNode(conf); } }

NameNode构造方法

	public NameNode(Configuration conf) throws IOException { // 默认为正常的NameNode this(conf, NamenodeRole.NAMENODE); } protected NameNode(Configuration conf, NamenodeRole role)  throws IOException { // 将配置文件赋值到父类的静态变量中 super(conf); // 初始化Tracer // 在"进程"中使用一个Tracer实例来收集和分发它的跟踪范围。Tracer实例是所有跟踪的一站式商店。 this.tracer = new Tracer.Builder("NameNode").  conf(TraceUtils.wrapHadoopConf(NAMENODE_HTRACE_PREFIX, conf)).  build(); // TracerConfigurationManager类提供了通过RPC协议在运行时管理跟踪器配置的函数。 this.tracerConfigurationManager =  new TracerConfigurationManager(NAMENODE_HTRACE_PREFIX, conf); this.role = role; // clientNamenodeAddress : 客户端将用来访问这个namenode或名称服务的namenode地址。对于使用逻辑URI的HA配置,它将是逻辑地址。 String nsId = getNameServiceId(conf); String namenodeId = HAUtil.getNameNodeId(conf, nsId); clientNamenodeAddress = NameNodeUtils.getClientNamenodeAddress(  conf, nsId); // 虚拟机中搭建集群启动日志打印为: // 2021-07-07 17:45:46,560 INFO org.apache.hadoop.hdfs.server.namenode.NameNode: Clients are to use wanglj01:9000 to access this namenode/service. if (clientNamenodeAddress != null) {  LOG.info("Clients should use {} to access"   + " this namenode/service.", clientNamenodeAddress); } // ha集群相关 this.haEnabled = HAUtil.isHAEnabled(conf, nsId); state = createHAState(getStartupOption(conf)); this.allowStaleStandbyReads = HAUtil.shouldAllowStandbyReads(conf); this.haContext = createHAContext(); try {  // 给联邦模式下准备的,主要是设置联邦模式下namenode的地址和RPC地址  initializeGenericKeys(conf, nsId, namenodeId);  // 初始化namenode的核心方法  initialize(getConf());  // HA相关内容  state.prepareToEnterState(haContext);  try {  haContext.writeLock();  state.enterState(haContext);  } finally {  haContext.writeUnlock();  } } catch (IOException e) {  this.stopAtException(e);  throw e; } catch (HadoopIllegalArgumentException e) {  this.stopAtException(e);  throw e; } // 启动成功 notBecomeActiveInSafemode = conf.getBoolean(  DFS_HA_NN_NOT_BECOME_ACTIVE_IN_SAFEMODE,  DFS_HA_NN_NOT_BECOME_ACTIVE_IN_SAFEMODE_DEFAULT); this.started.set(true); }

NameNode#initialize

 protected void initialize(Configuration conf) throws IOException { if (conf.get(HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS) == null) {  String intervals = conf.get(DFS_METRICS_PERCENTILES_INTERVALS_KEY);  if (intervals != null) {  conf.set(HADOOP_USER_GROUP_METRICS_PERCENTILES_INTERVALS,   intervals);  } } // UserGroupInformation类作用: // Hadoop的用户和组信息。该类封装了一个JAAS Subject,并提供了确定用户用户名和组的方法。它同时支持Windows、Unix和Kerberos登录模块。 // 方法简介:设置UGI的静态配置。特别是设置安全身份验证机制和组查找服务。 UserGroupInformation.setConfiguration(conf); // 以NameNode配置的用户登录。 loginAsNameNodeUser(conf); // 初始化namemode的度量系统 NameNode.initMetrics(conf, this.getRole()); StartupProgressMetrics.register(startupProgress); // 初始化jvm监听的度量系统 // JvmPauseMonitor类的作用: // 此类建立一个简单的线程。在此线程中,在循环中运行sleep一段时间方法,如果sleep花费的时间比传递给sleep方法的时间长, // 就意味着JVM或者宿主机已经出现了停顿处理现象,可能会导致其它问题,如果这种停顿被监测出来,线程会打印一个消息。 pauseMonitor = new JvmPauseMonitor(); pauseMonitor.init(conf); pauseMonitor.start(); metrics.getJvmMetrics().setPauseMonitor(pauseMonitor); if (conf.getBoolean(DFS_NAMENODE_GC_TIME_MONITOR_ENABLE,  DFS_NAMENODE_GC_TIME_MONITOR_ENABLE_DEFAULT)) {  long observationWindow = conf.getTimeDuration(   DFS_NAMENODE_GC_TIME_MONITOR_OBSERVATION_WINDOW_MS,   DFS_NAMENODE_GC_TIME_MONITOR_OBSERVATION_WINDOW_MS_DEFAULT,   TimeUnit.MILLISECONDS);  long sleepInterval = conf.getTimeDuration(   DFS_NAMENODE_GC_TIME_MONITOR_SLEEP_INTERVAL_MS,   DFS_NAMENODE_GC_TIME_MONITOR_SLEEP_INTERVAL_MS_DEFAULT,   TimeUnit.MILLISECONDS);  gcTimeMonitor = new Builder().observationWindowMs(observationWindow)   .sleepIntervalMs(sleepInterval).build();  gcTimeMonitor.start();  metrics.getJvmMetrics().setGcTimeMonitor(gcTimeMonitor); } // 启动httpserver if (NamenodeRole.NAMENODE == role) {  startHttpServer(conf); } // 启动nameNode时从磁盘加载fsimage以及edits文件,初始化FsNamesystem、FsDirectory、 LeaseManager等 loadNamesystem(conf); startAliasMapServerIfNecessary(conf); // 创建rpcserver,支持namenode与datanode,client进行通信的协议 // 封装了NameNodeRpcServer clientRpcServer,支持 ClientNamenodeProtocol、DatanodeProtocolPB等协议 // 啥是rpc看这里:https://www.zhihu.com/question/25536695 rpcServer = createRpcServer(conf); initReconfigurabl......

原文转载:http://www.shaoqun.com/a/859309.html

跨境电商:https://www.ikjzd.com/

skyee:https://www.ikjzd.com/w/290

捷汇:https://www.ikjzd.com/w/419

文化衫事件:https://www.ikjzd.com/w/1932


执行start-dfs.sh脚本后,集群是如何启动的?本文阅读并注释了start-dfs脚本,以及namenode和datanode的启动主要流程流程源码。阅读源码前准备源码获取拉取ApacheHadoop官方源码https://github.com/apache/hadoop用idea打开...切换到想看的版本...这里用的最新版本3.3.1阅读目标​ 本篇的阅读目标是搞明白hadoop中的st
tineye:https://www.ikjzd.com/w/448
亚马逊账号如何轻松过"二审"?掌握这几点,和二审说再见:https://www.ikjzd.com/articles/126701
亚马逊卖家清货曙光,上线却翻车?:https://www.ikjzd.com/articles/126769
分享7个亚马逊选品小妙招!:https://www.ikjzd.com/articles/126793
确定了!亚马逊印度站Prime Day将于8月初率先举办,美国待定!:https://www.ikjzd.com/articles/126771
女班长把内裤掀起来给我们看 女同桌让我闻她内裤:http://lady.shaoqun.com/a/247605.html
情人那个又粗又硬 最舒服的一次出轨经历:http://lady.shaoqun.com/a/247301.html
男朋友边摸边吃奶边做 小荡货夹得好紧太爽了:http://lady.shaoqun.com/m/a/247589.html
女海王阵型!不告诉男朋友就勾搭富二代!数百万粉丝,Tik Tok女人,网络名人,做了很多轮:http://lady.shaoqun.com/a/410933.html
清迈的一个天桥小舒林成同志,被居民举报:http://lady.shaoqun.com/a/410934.html
女人为什么选择"暧昧"?倾听中年妇女的心声:http://lady.shaoqun.com/a/410935.html
独立站高昂流量成废品,如何操作才能起死回生?(上):https://www.ikjzd.com/articles/146487

No comments:

Post a Comment