☰
从“jps找不到SNN”看Hadoop URI解析与fs.defaultFS配置
2026/10/17 4:08:38 网站建设 项目流程

前段日子搭一套离线数仓用的Hadoop集群,start-dfs.sh跑完,我习惯性用jps扫一眼进程:NameNode和DataNode都利索地起来了,唯独SecondaryNameNode没影。起初以为是启动慢,又等了两分钟还是没见着,这才意识到出事了。到logs目录翻SecondaryNameNode专属日志,一行IllegalArgumentException直接把我点醒——Does not contain a valid host:port authority: hdfs://。这种报错,十有八九是core-site.xml里fs.defaultFS被填成了裸的hdfs://,少写了host:port。这个问题本身不难,但背后牵扯到SecondaryNameNode的启动机制、URI解析规则、配置生效优先级等一堆细节,新手容易卡在“报错看懂了但不知道改哪里”。下面我按实际排查顺序,把整个链路完整过一遍。

1. 报错现场与根因速判

1.1 现象:jps里找不到SecondaryNameNode

启动完集群后,我先看标准输出,start-dfs.sh并没有直接打印“启动失败”之类的红字。这是第一个容易迷惑人的地方——很多新手以为脚本没报错就是所有角色都起来了。

实际上start-dfs.sh会通过SSH到各节点分别启动NameNode、DataNode和SecondaryNameNode,每个角色的日志都独立落在各自的logs目录。脚本本身不会主动去检查SNN进程是否存活,只要SSH命令执行了、Java进程那边没把异常打印到终端,脚本就当作“启动过了”。所以SNN启动失败时,你在终端往往看不到一丁点异常提示。

用jps看进程时,正常情况应该看到三个关键角色:

  • NameNode
  • DataNode
  • SecondaryNameNode

如果输出里只有前两个,基本可以确定SNN没有起来。为了排除jps偶发抽风,我一般会再用ps -ef | grep secondarynamenode补一刀。如果连这里都没有输出,那就是进程压根没存活或者启动后立刻退出了。

还有一种情况:如果你是多节点部署,SNN可能不在你执行命令的这台机器上。jps只能查看当前机器上当前用户启动的Java进程,需要到SNN所在的节点上去看。我这次是普通分布式,SNN和NameNode在同一台物理机上,所以jps一搜就能定位。

1.2 日志中的关键异常

确定SNN没起来后,下一步就是翻日志。SNN的日志文件名有固定规律,通常是$HADOOP_HOME/logs/hadoop-${USER}-secondarynamenode-${HOSTNAME}.log。比如我的环境里就是:

/usr/local/hadoop/logs/hadoop-root-secondarynamenode-nn1.internal.log

用tail -50看末尾,发现了核心异常堆栈:

2025-06-12 15:33:22,112 ERROR org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode: Failed to start secondary namenode java.lang.IllegalArgumentException: Does not contain a valid host:port authority: hdfs:// at org.apache.hadoop.hdfs.DFSUtilClient.getNNAddress(DFSUtilClient.java:445) at org.apache.hadoop.hdfs.DFSUtilClient.getNNAddress(DFSUtilClient.java:416) at org.apache.hadoop.hdfs.DistributedFileSystem.initialize(DistributedFileSystem.java:156) at org.apache.hadoop.hdfs.DistributedFileSystem.initialize(FileSystem.java:...) at org.apache.hadoop.fs.FileSystem.createFileSystem(FileSystem.java:...) at org.apache.hadoop.fs.FileSystem.access$200(FileSystem.java:...) at org.apache.hadoop.fs.FileSystem$Cache.getInternal(FileSystem.java:...) at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:...) at org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode.<init>(SecondaryNameNode.java:...)

看到DFSUtilClient.getNNAddress这个位置,我心里基本就有数了。这不是权限问题、不是端口冲突,而是客户端在解析NameNode的RPC地址时,拿到的URI根本不是一个“可用的地址”。

1.3 异常的本质是URI缺少authority

这个报错的关键词是authority。URI的标准结构是:

scheme://authority/path

以hdfs://namenode.internal:8020/为例:

  • scheme是hdfs
  • authority是namenode.internal:8020
  • path是/

而日志里的hdfs://只有scheme,authority是空的。Hadoop客户端拿到这个URI后,会尝试从中解析出NameNode的主机名和端口,结果发现host是null,直接触发IllegalArgumentException。

打个生活化的比方:快递单上写了“通过顺丰寄送”,但收货地址栏是空的。快递员知道该用哪家公司,却不知道往哪送,只能把单子退回去。这里的hdfs://就相当于“知道用HDFS协议,但不知道连哪台机器”。

在Hadoop源码里,DFSUtilClient.getNNAddress(URI)的实现逻辑大致是:

public static InetSocketAddress getNNAddress(URI nameNodeUri) { String authority = nameNodeUri.getAuthority(); if (authority == null || authority.isEmpty()) { throw new IllegalArgumentException( "Does not contain a valid host:port authority: " + nameNodeUri); } // 解析 address,返回 InetSocketAddress }

所以只要fs.defaultFS被配成hdfs://,任何需要从它构建NameNode地址的客户端代码都会在这一步爆炸。SecondaryNameNode作为HDFS客户端去连接NameNode时,自然也逃不过。

2. SecondaryNameNode的启动逻辑与配置依赖

2.1 SecondaryNameNode的职责边界

先说清楚一件事:SecondaryNameNode并不是NameNode的“热备”,它也不承担自动故障转移。它的核心职责是定期拉取NameNode上的edits log和fimage,在本地做合并,生成新的fsimage,再传回NameNode。

这个机制解决的是一个非常实际的问题:NameNode的元数据变更都记录在edits log里,如果不做检查点合并,edits越来越大,NameNode重启时恢复元数据的时间会越来越长,甚至直接把进程拖死。SNN的存在相当于一个“定期执行归档整理”的角色。

因为要和NameNode交互,SNN启动时必然要先建立一套能正常访问HDFS的客户端上下文。而要访问HDFS,第一步就是知道NameNode在哪,这个信息从哪里来?绝大多数场景下就是从fs.defaultFS来。

这也是为什么一台机器上NameNode和DataNode都能起来、唯独SNN起不来的原因:NameNode自身启动时不需要以“客户端”身份连接别人,它只要绑定自己的地址就能提供服务;DataNode启动时虽然要连接NameNode,但走的是独立的RPC配置逻辑,通常能自己兜底。而SNN走的是标准FileSystem客户端初始化路径,对fs.defaultFS的完整性要求极其严格。

2.2 SNN启动时的配置清单

SecondaryNameNode启动和运行期间,涉及的配置项不少,我梳理了一份关键清单:

配置项所在文件作用默认值
fs.defaultFScore-site.xml指定默认HDFS的完整URI,客户端依赖它找到NameNode无,必须配置
dfs.namenode.http-addresshdfs-site.xmlNameNode的HTTP监听地址,SNN用它拉取fsimage和editsHadoop 2.x默认0.0.0.0:50070,Hadoop 3.x默认0.0.0.0:9870
dfs.namenode.secondary.http-addresshdfs-site.xmlSNN自己的HTTP服务地址,用于接收NameNode的回调请求Hadoop 2.x默认0.0.0.0:50090,Hadoop 3.x默认0.0.0.0:9868
dfs.namenode.checkpoint.dirhdfs-site.xmlSNN保存检查点数据的本地目录默认file://${hadoop.tmp.dir}/dfs/namesecondary
dfs.namenode.checkpoint.periodhdfs-site.xml检查点触发间隔(秒)默认3600

这里面,fs.defaultFS是最容易被配错的一项。很多人在搭建环境时,随手填了个hdfs://就完事,觉得“我指定了要用HDFS协议就行了”,完全忽略了后面的host:port部分。

而dfs.namenode.http-address如果配错,SNN虽然能起来,但后续拉取镜像时大概率会报连接超时或者404,是另一个层面的坑,后面我会再展开。

2.3 从异常堆栈反推启动链路

结合上面的堆栈,我们可以还原一下SNN启动失败的完整链路:

  1. SecondaryNameNode构造器启动,初始化阶段需要创建一个能访问HDFS的文件系统客户端。
  2. 它调用FileSystem.get(conf),传入当前生效配置。
  3. FileSystem.get根据fs.defaultFS的scheme(这里是hdfs),选择DistributedFileSystem实现。
  4. DistributedFileSystem.initialize内部调用DFSUtilClient.getNNAddress,尝试把fs.defaultFS里的URI转换成NameNode的InetSocketAddress。
  5. 解析URI时发现authority为空,直接抛出IllegalArgumentException: Does not contain a valid host:port authority: hdfs://。

这段链路解释了为什么NameNode没事、DataNode没事,只有SNN退出。也解释了为什么修复方法非常简单——只要把fs.defaultFS补成完整URI,让客户端能解析出host和port,整个启动链路就通了。

3. 完整排查步骤与修复方案

3.1 第一步:找到SNN自己的日志

很多人在这个报错上绕远路,是因为没看到日志就开始瞎猜。正确做法是第一时间找到SNN自己的日志文件。

ls -l $HADOOP_HOME/logs/*secondarynamenode* tail -100 $HADOOP_HOME/logs/hadoop-$(whoami)-secondarynamenode-$(hostname).log

注意两个细节:

  • 日志文件名里的$(whoami)是启动集群的用户。如果你用root启动,文件里就是hadoop-root-*;如果你用普通用户启动,就是hadoop-username-*。
  • .log文件保存的是完整异常堆栈,.out保存的是标准输出。排查异常优先看.log,.out里往往什么都没有。

还有一点:如果你在logs目录下看到多个名称相近的文件,别认错。比如hadoop-root-secondarynamenode-nn1.log才是SNN的日志,不要去看成hadoop-root-namenode-nn1.log——那是NameNode的,里面不会有这条异常。

3.2 第二步:用getconf验证生效配置

拿到报错后,下一步是核实当前生效的fs.defaultFS。直接grep XML文件有时候会被坑,因为可能出现多份配置文件、多个同名property互相覆盖的情况。最权威的方式是问Hadoop自己:

hdfs getconf -confKey fs.defaultFS

我这次执行的结果是:

hdfs://

屏幕上只输出一个裸的hdfs://,问题实锤了。如果你的环境输出的是hdfs://namenode:8020这样的完整地址,但仍然报同样的错,那就说明系统实际读取的配置和你grep到的不是同一份,需要继续查配置覆盖问题(见4.1节)。

同时可以再验证一下SNN相关的两个关键地址:

hdfs getconf -confKey dfs.namenode.secondary.http-address hdfs getconf -confKey dfs.namenode.http-address

确保这两个值不是空,也不是像hdfs://这种带scheme的残缺写法。它们需要的格式是host:port,不带协议前缀。

3.3 第三步:修正fs.defaultFS并核对相关项

确认问题后,打开core-site.xml,找到fs.defaultFS这一项。修复前先确认你的NameNode RPC端口是多少。最稳的办法是问配置:

hdfs getconf -confKey dfs.namenode.rpc-address

如果这条输出是空的,那说明没单独配置RPC地址,fs.defaultFS里的host:port就是客户端找NameNode的唯一依据。这时需要确认实际NameNode RPC监听端口。常见的默认值有两个版本区间:

  • Hadoop 2.x系列的默认RPC端口是8020
  • Hadoop 3.x同样沿用了8020作为默认RPC端口,但部分发行版或自定义环境会改成9820、9000等

我的建议是不要凭记忆写端口,直接用ss -lntp看NameNode实际监听端口:

ss -lntp | grep java

找到NameNode进程监听的8020或自定义端口,然后把它填进fs.defaultFS。修改后的core-site.xml片段如下:

<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://nn1.internal.example:8020</value> </property> </configuration>

这里有几个要点:

  • nn1.internal.example是NameNode所在机器的主机名,必须能被集群内所有节点解析。你可以用getent hosts nn1.internal.example验证。
  • 不要写localhost或127.0.0.1,除非你确定是单机伪分布式。多节点环境下,SNN和DataNode可能在其他机器上,它们拿到localhost后连接的是自己,必然连不上NameNode。
  • 端口必须和dfs.namenode.rpc-address保持一致。如果这两处不一致,客户端优先从fs.defaultFS取host和port,就可能出现“地址对不上”的连锁问题。

改完core-site.xml后,再顺带看一眼hdfs-site.xml里dfs.namenode.http-address和dfs.namenode.secondary.http-address的格式。这两项应该都是host:port格式。如果某项被配成了hdfs://host:port这种带scheme的值,也需要一并修正,否则SNN真正发起检查点请求时会继续踩坑。

3.4 第四步:重启集群并验证检查点

配置改好之后,需要重启集群让配置生效。注意顺序:

stop-dfs.sh start-dfs.sh jps

重启后jps应该能看到SecondaryNameNode。这个成功后,再看一眼日志确认没有新异常:

tail -50 $HADOOP_HOME/logs/hadoop-$(whoami)-secondarynamenode-$(hostname).log

如果日志末尾显示Checkpoint done之类的字样,说明SNN已经正常工作了。

我这次还手动触发了一次检查点验证:

hdfs dfsadmin -triggerCheckpoint

这个命令会请求NameNode触发一次检查点。执行后观察SNN日志或者检查点目录时间戳变化。正常情况下,几分钟内能看到新检查点生成,说明不只是进程起来了,功能链路也是通的。

3.5 配置本身没错时的进阶排查

有一种情况比较讨厌:你用hdfs getconf -confKey fs.defaultFS查出来是完整的hdfs://nn1:8020,但SNN日志照样报Does not contain a valid host:port authority。这时候问题大概率出在“配置覆盖”上。

最典型的场景是同一台机器上存在多个core-site.xml,其中一份部署目录的配置是正确的,另一份却被读取了。Hadoop的配置加载遵循classpath和环境变量顺序,常见顺序是:$HADOOP_CONF_DIR指定的目录、$HADOOP_HOME/etc/hadoop、CLASSPATH里的其他位置。

如果你改了文件但getconf输出的还是旧值,先检查这两个地方:

echo $HADOOP_CONF_DIR echo $HADOOP_HOME

看看环境变量指向的配置目录是不是你想要的那个。还可以显式指定配置目录来排查:

hdfs --config /path/to/your/conf getconf -confKey fs.defaultFS

另外要检查XML文件里是不是有重复的<property>。在Hadoop的配置解析规则里,如果同一个key出现多次,后面的会把前面的覆盖掉:

<property> <name>fs.defaultFS</name> <value>hdfs://</value> </property> <property> <name>fs.defaultFS</name> <value>hdfs://nn1.internal.example:8020</value> </property>

这种情况下生效的是最后一个值,但假如最后一个是错的,就会把前面正确的覆盖掉。我见过不止一次因为这种“增删改时忘了删旧段落”导致的诡异问题。判断方法很简单:只信任getconf的输出,不要信任眼睛对XML的扫描。

4. 高频配置坑位与实战避坑

4.1 多份配置文件与生效优先级

配置文件加载顺序是Hadoop运维里最容易踩的暗坑之一。除了环境变量HADOOP_CONF_DIR,还要注意有些发行版会在/etc/hadoop下放一份全局配置,而$HADOOP_HOME/etc/hadoop下又有另一份。到底读哪一份,取决于启动脚本里HADOOP_CONF_DIR怎么设置。

我见过一个实际案例:某开发者的NameNode和DataNode都正常,唯独SNN起不来,排查半天发现是/etc/hadoop/core-site.xml里fs.defaultFS是坏的,而$HADOOP_HOME/etc/hadoop/core-site.xml里是对的。NameNode进程是用$HADOOP_HOME下的配置启的,但SNN因为某个启动脚本额外设置了HADOOP_CONF_DIR=/etc/hadoop,所以读到了错误的配置。

遇到这类问题,统一的自检思路是:

  1. echo $HADOOP_CONF_DIR确认实际配置目录。
  2. 用hdfs getconf -confKey fs.defaultFS读取最终生效值。
  3. 如果不想被环境变量干扰,启动之前主动unset HADOOP_CONF_DIR,让Hadoop回到默认路径,或者export HADOOP_CONF_DIR显式指向你修改的那份配置。

总之,不要用“我改了配置文件所以应该生效了”这种想法代替验证。

4.2 端口占用与绑定问题

SNN起不来,除了URI解析错误,还有一个高频原因是端口被占。SNN自己监听的HTTP端口在Hadoop 2.x默认是50090,在Hadoop 3.x默认是9868。如果这个端口被其他进程占了,SNN会直接抛BindException退出。

虽然你这次看到的报错是IllegalArgumentException,但排查SNN问题时我习惯把端口占用一并扫掉,避免修完一个又撞另一个:

ss -lntp | grep -E '50090|9868'

如果端口确实被占,两种处理方式:

  • 杀掉占用端口的进程(注意别误杀别的服务)
  • 修改dfs.namenode.secondary.http-address换一个空闲端口,例如改成0.0.0.0:9869

另外提醒一句:dfs.namenode.secondary.http-address如果配成0.0.0.0:9868是没问题的,表示监听所有网卡。如果写成127.0.0.1:9868,那么其他机器上的NameNode回调时就连不上这个SNN了,虽然进程能起来,但检查点会失败。很多人把这条例外修好了,又卡在检查点上,往往就是这个原因。

4.3 HA与SNN的兼容问题

如果你的集群已经配置了NameNode高可用(HA),即两个NameNode + JournalNode的架构,那么逻辑上就不需要SecondaryNameNode。HA模式下,检查点功能由两个NameNode通过JournalNode协作完成,SNN不仅多余,还可能引入配置冲突。

具体表现可能有两种:

  • 你仍然在hdfs-site.xml里配置了dfs.namenode.secondary.http-address,导致SNN进程尝试启动但和HA的逻辑相悖,异常五花八门。
  • SNN进程能起来,但NameNode根本不会向它发检查点请求,这个进程白白占着资源。

如果你在HA环境里还想着“保留SNN作为备份”,建议直接放弃这个念头。SNN不是热备,HA也不需要它。正确做法是把SNN相关角色从start-dfs.sh的启动范围里去掉,或者调整dfs.namenode.secondary.http-address的配置让它不参与。

从运维角度看,配置HA的集群还强行启动SNN,等于给系统塞了一个“看似在干活实则没用的组件”,出了问题还会干扰排查视线。

4.4 主机名解析与hosts映射

URI里的host到底能不能被解析,是另一个容易被忽略的因素。比如你把fs.defaultFS配置成了hdfs://node01:8020,但集群里的机器对node01这个主机名根本无法解析,那么客户端在拿到URI之后,可能在更晚的阶段报UnknownHostException,也可能在启动阶段就异常退出。

有些机器上会有这个现象:主机名本身变了,但/etc/hosts里还保留着旧映射,或者把本机主机名映射到了127.0.0.1。这在单机伪分布式下能跑通,一旦改成多节点分布式,其他机器连过来就会发现地址指向了本机回环,连接直接失败。

排查命令:

hostname -f getent hosts node01

确保集群内所有节点都能通过主机名解析出正确的内网IP。如果/etc/hosts里写错了,修正后建议在每台节点上重新验证一遍。临时用IP替代主机名也可以,但我不推荐长期这样做,主机名的可维护性远好于IP,尤其是NameNode迁移时。

4.5 常见问题速查表

把这类启动问题整理成速查表,方便以后遇到类似故障时直接对照:

现象报错关键词可能原因排查命令修复建议
SNN进程不存在Does not contain a valid host:port authorityfs.defaultFS缺少host:porthdfs getconf -confKey fs.defaultFS补全为hdfs://host:port
SNN进程不存在BindExceptionSNN默认端口被占用ss -lntp | grep 50090改dfs.namenode.secondary.http-address
SNN进程存在但报权限错Permission denied检查点目录属主不对ls -ld dfs/namesecondarychown到启动用户
SNN进程存在但检查点失败Connection refused / timeoutdfs.namenode.http-address不可达curl http://nn-host:9870修正NN的HTTP地址和网络策略
改了配置不生效实际读取的还是旧值配置覆盖或HADOOP_CONF_DIR指错echo $HADOOP_CONF_DIR统一配置目录,显式指定

5. 快速自查命令与配置参考

5.1 命令清单

为了让你以后遇到类似问题能少走弯路,我把这一整套排查动作沉淀成命令清单:

# 1. 看SNN进程是否存在 jps | grep SecondaryNameNode ps -ef | grep secondarynamenode # 2. 快速定位SNN日志 ls -l $HADOOP_HOME/logs/*secondarynamenode* tail -100 $HADOOP_HOME/logs/hadoop-$(whoami)-secondarynamenode-$(hostname).log # 3. 查看生效配置 hdfs getconf -confKey fs.defaultFS hdfs getconf -confKey dfs.namenode.rpc-address hdfs getconf -confKey dfs.namenode.http-address hdfs getconf -confKey dfs.namenode.secondary.http-address # 4. 检查端口监听情况 ss -lntp | grep -E '8020|50070|9870|50090|9868' # 5. 检查主机名解析 hostname -f getent hosts <namenode主机名>

这套命令在任何Hadoop 2.x/3.x版本上都适用,区别只在默认端口号不同。

5.2 最小可用配置示例

如果你是从零开始搭集群,下面这份最小配置可以作为参考基线。core-site.xml:

<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://nn1.internal.example:8020</value> </property> </configuration>

hdfs-site.xml关键配置:

<configuration> <property> <name>dfs.namenode.rpc-address</name> <value>nn1.internal.example:8020</value> </property> <property> <name>dfs.namenode.http-address</name> <value>0.0.0.0:9870</value> </property> <property> <name>dfs.namenode.secondary.http-address</name> <value>0.0.0.0:9868</value> </property> <property> <name>dfs.namenode.checkpoint.dir</name> <value>file:///data/hadoop/namesecondary</value> </property> </configuration>

注意这里我把dfs.namenode.rpc-address显式写出来了,这样一来fs.defaultFS和RPC地址保持一致,客户端解析时不会存在两套信息来源打架的问题。检查点目录dfs.namenode.checkpoint.dir建议显式指定到一个独立数据盘,不要放在系统盘的临时目录下,否则清临时文件时容易把检查点一起清了。

5.3 用检查脚本把坑挡在启动前

吃了一次亏之后,我写了个简单的检查脚本,放到部署目录里,每次启动集群前先跑一遍:

#!/bin/bash default_fs=$(hdfs getconf -confKey fs.defaultFS 2>/dev/null) if [[ "$default_fs" =~ ^hdfs://[^/]+:[0-9]+$ ]]; then echo "[OK] fs.defaultFS = $default_fs" else echo "[ERROR] fs.defaultFS 格式异常: $default_fs" echo "期望格式: hdfs://<namenode-host>:<port>" exit 1 fi for key in dfs.namenode.http-address dfs.namenode.secondary.http-address; do val=$(hdfs getconf -confKey $key 2>/dev/null) echo "[INFO] $key = $val" done

这个脚本的正则只认hdfs://host:port这种完整格式,不够完整就报警。后续你还可以在脚本里扩展端口连通性检查、/etc/hosts解析检查等。花十几分钟把这类检查固化下来,比每次手动翻日志高效得多,尤其适合团队协作环境——防止有人改坏配置后带着坏配置去重启集群,把全组人都坑一遍。

我个人实际排查这类SNN启动故障的体会是:报错越是看着复杂,越要先沉住气看日志。Does not contain a valid host:port authority本质上就是一个URI完整性问题,而不是什么深不可测的底层故障。修好之后也别急着跑业务,先手动触发一次检查点,确认SNN确实在干活。最后再提醒一句:SNN起不来时千万不要头脑发热去执行namenode -format,格式化只会清掉已有元数据,而这种配置问题根本不需要动元数据。把配置链路上的每一环都验证了,再谈重启,才是稳妥的姿势。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询