Hadoop HA高可用原理及实战

3,114 阅读5分钟

高可用集群介绍

什么是HA?

高可用集群(High Availability Cluster,简称HA Cluster),是指以减少服务中断时间为目的的服务器集群技术。它通过保护用户的业务程序对外不间断提供的服务,把因软件、硬件、人为造成的故障对业务的影响降低到最小程度。

高可用的衡量标准

要保证集群服务100%时间永远完全可用,几乎可以说是一件不可能完成的任务。100%的高可用只是一个理论上的抽象数字。

业内简称可用性全年可停机时间
2个999%87.6H
3个999.9%8.8H
4个999.99%53Min
5个999.999%5Min

HDFS可能存在的问题

  • NameNode单点故障,难以应用于在线场景,也不利于生产上维护集群
  • NameNode压力过大,且内存受限,影响系统扩展性

QJM

在QJM出现之前,为保障集群的HA,设计的是一种基于NAS的共享存储机制,即主备NameNode间通过NAS进行元数据的同步。但由于部署繁琐、客户端UI差劲、需要专用的硬件设备备受诟病。因此QJM应运而生。

QJM全称是Quorum JournalManager,由JournalNode(JN)组成,一般是奇数点结点组成。每个JournalNode对外有一个简易的RPC接口,以供NameNode读写EditLog到JN本地磁盘。当写EditLog时,NameNode会同时向所有JournalNode并行写文件,只要有N/2 + 1节点写成功则认为此次写操作成功,遵循Paxos协议。

HDFS HA

工作原理

  • Active NN: 接收client的rpc的请求,在自己把元数据操作写入editlog文件的同时,也向jn集群写一份。

  • Standby NN: 接受到jn集群的记录,使自己的元数据和active nn的元数据保持一致。

  • ZKFC: ZooKeeperFailoverController,Hadoop中通过ZK实现FC功能的一个实用工具。它作为一个ZK集群的客户端,用来监控NN的状态信息,定期发送心跳保证自己可被选举为active。每个NN节点都需要运行ZKFC。

  • JN: JournalNode。用于Active NN以及Standby NN的数据同步。

  • ZK: Zookeeper分布式应用程序协调服务。用于NN的失败检测以及NN的选举。

集群规划

HostNNZKFCZKJNDN
xinxing001
xinxing002
xinxing003

实战部署

1、准备工作

[root@localhost ~]# systemctl stop firewalld                //关闭防火墙
[root@localhost ~]# systemctl disabled firewalld            //开机不启动防火墙
[root@localhost ~]# /etc/sysconfig/selinux                  //把enforcing改为disabled
[root@localhost ~]# vim /etc/hostname                       //更改每台机器的主机名
[root@xxdata001 ~]# vim /etc/hosts                          //配置主机域名解析(三台主机)
192.168.0.193 xxdata001
192.168.0.106 xxdata002
192.168.0.131 xxdata003

[root@localhost ~]# reboot

2、三台机器部署JDK

juejin.cn/post/684490…

3、创建hadoop用户及所需目录

[root@xxdata001 /]# useradd hadoop
[root@xxdata001 /]# su - hadoop
[hadoop@xxdata001 ~]$ mkdir -p data tmp sourcecode software shell lib app log

4、配置三台机器相互SSH免密登录

[root@xxdata001 /]# su - hadoop
[hadoop@xxdata001 ~]$ ssh-keygen
[hadoop@xxdata001 .ssh]$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys            //三台机器都做

做完后相互把自己的公钥拷贝给另外两台主机,切记公钥是一行内容,不要有空格,否则不生效
我这里做好第一台以后分别分发给第二台和第三台
[hadoop@xxdata001 ~]$ scp ~/.ssh/authorized_keys root@xxdata002:/home/hadoop/.ssh/authorized_keys
[hadoop@xxdata001 ~]$ scp ~/.ssh/authorized_keys root@xxdata003:/home/hadoop/.ssh/authorized_keys

[hadoop@xxdata001 .ssh]$ chmod 0600 ~/.ssh/authorized_keys                          //赋予权限

//测试可用性,只需要第一次输入yes
[hadoop@xxdata001 .ssh]$ ssh xxdata001 date                                         
[hadoop@xxdata001 .ssh]$ ssh xxdata002 date
[hadoop@xxdata001 .ssh]$ ssh xxdata003 date

5、zookeeper集群搭建(三台机器都做)

//解压软件包
[hadoop@xxdata001 software]$ tar -zxvf zookeeper-3.4.5-cdh5.16.2.tar.gz -C ../app/

//设置软连接
[hadoop@xxdata001 app]$ ln -s zookeeper-3.4.5-cdh5.16.2/ zookeeper

//进入配置文件目录,并拷贝示例配置文件为zoo.cfg编辑
[hadoop@xxdata001 conf]$ cd ~/app/zookeeper/conf
[hadoop@xxdata001 conf]$ cp zoo_sample.cfg zoo.cfg
[hadoop@xxdata001 conf]$ mkdir -p /home/hadoop/data/zookeeper
[hadoop@xxdata001 conf]$ vim zoo.cfg 

//指定data路径
# example sakes.
dataDir=/home/hadoop/data/zookeeper

//新增三句
server.1=xxdata001:2888:3888
server.2=xxdata002:2888:3888
server.3=xxdata003:2888:3888
//此步骤看清楚主机标识,并不是三台都做

[hadoop@xxdata001 conf]$ echo 1 > /home/hadoop/data/zookeeper/myid
[hadoop@xxdata002 conf]$ echo 2 > /home/hadoop/data/zookeeper/myid
[hadoop@xxdata003 conf]$ echo 3 > /home/hadoop/data/zookeeper/myid
//三台都分别启动服务
[hadoop@xxdata001-3 ~]$ /home/hadoop/app/zookeeper/bin/zkServer.sh start

//查看服务启动状态及选举状态
[hadoop@xxdata001-3 ~]$ /home/hadoop/app/zookeeper/bin/zkServer.sh status

6、hadoop集群搭建

//解压缩软件包
[hadoop@xxdata001 software]$ tar -zxvf hadoop-2.6.0-cdh5.16.2.tar.gz -C ../app/

//建立软连接
ln -s hadoop-2.6.0-cdh5.16.2/ hadoop

//显性指定java目录
[hadoop@xxdata001 sbin]$ cd /home/hadoop/app/hadoop/etc/hadoop
[hadoop@xxdata001 hadoop]$ vim hadoop-env.sh 

export JAVA_HOME=/usr/java/jdk1.8.0_45

编辑slaves文件

[hadoop@xxdata001 hadoop]$ vim slaves
xxdata001
xxdata002
xxdata003

core-site.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
	<!--Yarn 需要使用 fs.defaultFS 指定NameNode URI -->
        <property>
                <name>fs.defaultFS</name>
                <value>hdfs://dw2</value>
        </property>
        <!--==============================Trash机制======================================= -->
        <property>
                <!--多长时间创建CheckPoint NameNode截点上运行的CheckPointer 从Current文件夹创建CheckPoint;默认:0 由fs.trash.interval项指定 -->
                <name>fs.trash.checkpoint.interval</name>
                <value>0</value>
        </property>
        <property>
                <!--多少分钟.Trash下的CheckPoint目录会被删除,该配置服务器设置优先级大于客户端,默认:0 不删除 -->
                <name>fs.trash.interval</name>
                <value>1440</value>
        </property>

         <!--指定hadoop临时目录, hadoop.tmp.dir 是hadoop文件系统依赖的基础配置,很多路径都依赖它。如果hdfs-site.xml中不配 置namenode和datanode的存放位置,默认就放在这>个路径中 -->
        <property>   
                <name>hadoop.tmp.dir</name>
                <value>/home/hadoop/tmp/hadoop</value>
        </property>

         <!-- 指定zookeeper地址 -->
        <property>
                <name>ha.zookeeper.quorum</name>
                <value>xxdata001:2181,xxdata002:2181,xxdata003:2181</value>
        </property>
         <!--指定ZooKeeper超时间隔,单位毫秒 -->
        <property>
                <name>ha.zookeeper.session-timeout.ms</name>
                <value>2000</value>
        </property>

        <property>
           <name>hadoop.proxyuser.hadoop.hosts</name>
           <value>*</value> 
        </property> 
        <property> 
            <name>hadoop.proxyuser.hadoop.groups</name> 
            <value>*</value> 
       </property> 
</configuration>

hdfs-site.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
	<!--HDFS超级用户 -->
	<property>
		<name>dfs.permissions.superusergroup</name>
		<value>hadoop</value>
	</property>

	<!--开启web hdfs -->
	<property>
		<name>dfs.webhdfs.enabled</name>
		<value>true</value>
	</property>
	<property>
		<name>dfs.namenode.name.dir</name>
		<value>/home/hadoop/data/dfs/name</value>
		<description> namenode 存放name table(fsimage)本地目录(需要修改)</description>
	</property>
	<property>
		<name>dfs.namenode.edits.dir</name>
		<value>${dfs.namenode.name.dir}</value>
		<description>namenode粗放 transaction file(edits)本地目录(需要修改)</description>
	</property>
	<property>
		<name>dfs.datanode.data.dir</name>
		<value>/home/hadoop/data/dfs/data</value>
		<description>datanode存放block本地目录(需要修改)</description>
	</property>
	<property>
		<name>dfs.replication</name>
		<value>3</value>
	</property>
	<!-- 块大小128M (默认128M) -->
	<property>
		<name>dfs.blocksize</name>
		<value>134217728</value>
	</property>
	<!--======================================================================= -->
	<!--HDFS高可用配置 -->
	<!--指定hdfs的nameservice为dw2,需要和core-site.xml中的保持一致 -->
	<property>
		<name>dfs.nameservices</name>
		<value>dw2</value>
	</property>
	<property>
		<!--设置NameNode IDs 此版本最大只支持两个NameNode -->
		<name>dfs.ha.namenodes.dw2</name>
		<value>nn1,nn2</value>
	</property>

	<!-- Hdfs HA: dfs.namenode.rpc-address.[nameservice ID] rpc 通信地址 -->
	<property>
		<name>dfs.namenode.rpc-address.dw2.nn1</name>
		<value>xxdata001:8020</value>
	</property>
	<property>
		<name>dfs.namenode.rpc-address.dw2.nn2</name>
		<value>xxdata002:8020</value>
	</property>

	<!-- Hdfs HA: dfs.namenode.http-address.[nameservice ID] http 通信地址 -->
	<property>
		<name>dfs.namenode.http-address.dw2.nn1</name>
		<value>xxdata001:50070</value>
	</property>
	<property>
		<name>dfs.namenode.http-address.dw2.nn2</name>
		<value>xxdata002:50070</value>
	</property>

	<!--==================Namenode editlog同步 ============================================ -->
	<!--保证数据恢复 -->
	<property>
		<name>dfs.journalnode.http-address</name>
		<value>0.0.0.0:8480</value>
	</property>
	<property>
		<name>dfs.journalnode.rpc-address</name>
		<value>0.0.0.0:8485</value>
	</property>
	<property>
		<!--设置JournalNode服务器地址,QuorumJournalManager 用于存储editlog -->
		<!--格式:qjournal://<host1:port1>;<host2:port2>;<host3:port3>/<journalId> 端口同journalnode.rpc-address -->
		<name>dfs.namenode.shared.edits.dir</name>
		<value>qjournal://xxdata001:8485;xxdata002:8485;xxdata003:8485/dw2</value>
	</property>

	<property>
		<!--JournalNode存放数据地址 -->
		<name>dfs.journalnode.edits.dir</name>
		<value>/home/hadoop/data/dfs/jn</value>
	</property>
	<!--==================DataNode editlog同步 ============================================ -->
	<property>
		<!--DataNode,Client连接Namenode识别选择Active NameNode策略 -->
                             <!-- 配置失败自动切换实现方式 -->
		<name>dfs.client.failover.proxy.provider.dw2</name>
		<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
	</property>
	<!--==================Namenode fencing:=============================================== -->
	<!--Failover后防止停掉的Namenode启动,造成两个服务 -->
	<property>
		<name>dfs.ha.fencing.methods</name>
		<value>sshfence</value>
	</property>
	<property>
		<name>dfs.ha.fencing.ssh.private-key-files</name>
		<value>/home/hadoop/.ssh/id_rsa</value>
	</property>
	<property>
		<!--多少milliseconds 认为fencing失败 -->
		<name>dfs.ha.fencing.ssh.connect-timeout</name>
		<value>30000</value>
	</property>

	<!--==================NameNode auto failover base ZKFC and Zookeeper====================== -->
	<!--开启基于Zookeeper  -->
	<property>
		<name>dfs.ha.automatic-failover.enabled</name>
		<value>true</value>
	</property>
	<!--动态许可datanode连接namenode列表 -->
	 <property>
	   <name>dfs.hosts</name>
	   <value>/home/hadoop/app/hadoop/etc/hadoop/slaves</value>
	 </property>
</configuration>

mapred-site.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
	<!-- 配置 MapReduce Applications -->
	<property>
		<name>mapreduce.framework.name</name>
		<value>yarn</value>
	</property>
	<!-- JobHistory Server ============================================================== -->
	<!-- 配置 MapReduce JobHistory Server 地址 ,默认端口10020 -->
	<property>
		<name>mapreduce.jobhistory.address</name>
		<value>xxdata001:10020</value>
	</property>
	<!-- 配置 MapReduce JobHistory Server web ui 地址, 默认端口19888 -->
	<property>
		<name>mapreduce.jobhistory.webapp.address</name>
		<value>xxdata001:19888</value>
	</property>

<!-- 配置 Map段输出的压缩,snappy-->
<!--
  <property>
      <name>mapreduce.map.output.compress</name> 
      <value>true</value>
  </property>
              
  <property>
      <name>mapreduce.map.output.compress.codec</name> 
      <value>org.apache.hadoop.io.compress.SnappyCodec</value>
   </property>
-->

</configuration>

7、启动集群

// 先在部署 journalnode 的节点机器上启动 JournalNode进程
[hadoop@xxdata001 sbin]$ /home/hadoop/app/hadoop/sbin/hadoop-daemon.sh start  journalnode

//格式化NameNode
[hadoop@xxdata001 ~]$  hadoop namenode -format 

//把第一台NN初始化数据拷贝到NN2上
[hadoop@xxdata001 dfs]$ scp -r /home/hadoop/data/dfs/name/ root@xxdata002:/home/hadoop/data/dfs/name/
//或者在StandbyNN上使用命令同步元数据信息
[hadoop@xxdata002 ~t]$ hdfs namenode -bootstrapStandby


//xxdata002修正一下所属
[root@xxdata002 dfs]# chown -R hadoop:hadoop name

//第一台机器节点初始化ZKFC
[hadoop@xxdata001 hadoop]$ hdfs zkfc -formatZK

//第一台机器启动hdfs集群(第一台会去调第二台和第三台)
[hadoop@xxdata001 /]$ /home/hadoop/app/hadoop/sbin/start-dfs.sh 

//JPS命令查看所有服务运行是否正常
29633 JournalNode
30131 DataNode
30004 NameNode
30489 Jps
29370 QuorumPeerMain
30383 DFSZKFailoverController

8、Web页面查看服务

Yarn HA

工作原理

YARN将共享存储系统抽象成RMStateStore,以保存恢复ResouceManager所必须的信息。ResouceManager并不会保存已经分配给每个ApplicationMaster的资源信息和每个NodeManager的资源使用信息,这些均可通过相应的心跳汇报机制重构出来,也正因如此,ResouceManager HA的实现是非常轻量级的。

ResouceManager提供了四种RMStateStore实现(管理员可通过参数yarn.resourcemanager.store.class设置),分别是NullRMStateStore、MemoryRMStateStore、FileSystemRMStateStore、ZKRMStateStore,从目前来看YARN HA最佳实践是采用基于ZKRMStateStore的共享存储方案

  • NullRMStateStore:不存储任何状态信息,在不启动恢复机制时,它是默认实现的
  • MemoryRMStateStore:将状态信息存储到内存中,在启用恢复机制时,它是默认实现的
  • FileSystemRMStateStore:将状态信息存储到HDFS中
  • ZKRMStateStore:将状态信息存储到zookeeper中

1、服务启动时会向zk的/RMStateStore中写lock文件,写成功为active,否则为standby。ResourceManager的ZKFC会一直监控lock文件是否存在。

2、当ZKFC被选举为active时, 会从RMStateStore中读取相应的作业信息,重新构建作业的内存信息,启动内部服务,开始接受NodeManager的心跳, 构建集群的资源信息, 并接受客户端的作业请求。

3、当active的ResourceManager挂了, 另一个standby RecourseManager通过ZKFC选举为active, 从RMStateStore 读取相应的作业信息, 重新构建作业的内存信息, 启动内部的服务

4、active ResourceManager 向RMStateStore中写Application信息

5、RM接收和监控ApplicationMaster on NodeManager节点的container(作业的主程序)

集群规划

HostRMZKNM
xinxing001
xinxing002
xinxing003

实战部署

1、yan-site.xml配置文件

[hadoop@xxdata001 hadoop]$ vim /home/hadoop/app/hadoop/etc/hadoop/yarn-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
	<!-- nodemanager 配置 ================================================= -->
	<property>
		<name>yarn.nodemanager.aux-services</name>
		<value>mapreduce_shuffle</value>
	</property>
	<property>
		<name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
		<value>org.apache.hadoop.mapred.ShuffleHandler</value>
	</property>
	<property>
		<name>yarn.nodemanager.localizer.address</name>
		<value>0.0.0.0:23344</value>
		<description>Address where the localizer IPC is.</description>
	</property>
	<property>
		<name>yarn.nodemanager.webapp.address</name>
		<value>0.0.0.0:23999</value>
		<description>NM Webapp address.</description>
	</property>

	<!-- HA 配置 =============================================================== -->
	<!-- Resource Manager Configs -->
	<property>
		<name>yarn.resourcemanager.connect.retry-interval.ms</name>
		<value>2000</value>
	</property>
	<property>
		<name>yarn.resourcemanager.ha.enabled</name>
		<value>true</value>
	</property>
	<property>
		<name>yarn.resourcemanager.ha.automatic-failover.enabled</name>
		<value>true</value>
	</property>
	<!-- 使嵌入式自动故障转移。HA环境启动,与 ZKRMStateStore 配合 处理fencing -->
	<property>
		<name>yarn.resourcemanager.ha.automatic-failover.embedded</name>
		<value>true</value>
	</property>
	<!-- 集群名称,确保HA选举时对应的集群 -->
	<property>
		<name>yarn.resourcemanager.cluster-id</name>
		<value>yarn-cluster</value>
	</property>
	<property>
		<name>yarn.resourcemanager.ha.rm-ids</name>
		<value>rm1,rm2</value>
	</property>


    <!--这里RM主备结点需要单独指定,(可选)
	<property>
		 <name>yarn.resourcemanager.ha.id</name>
		 <value>rm2</value>
	 </property>
	 -->

	<property>
		<name>yarn.resourcemanager.scheduler.class</name>
		<value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value>
	</property>
	<property>
		<name>yarn.resourcemanager.recovery.enabled</name>
		<value>true</value>
	</property>
	<property>
		<name>yarn.app.mapreduce.am.scheduler.connection.wait.interval-ms</name>
		<value>5000</value>
	</property>
	<!-- ZKRMStateStore 配置 -->
	<property>
		<name>yarn.resourcemanager.store.class</name>
		<value>org.apache.hadoop.yarn.server.resourcemanager.recovery.ZKRMStateStore</value>
	</property>
	<property>
		<name>yarn.resourcemanager.zk-address</name>
		<value>xxdata001:2181,xxdata002:2181,xxdata003:2181</value>
	</property>
	<property>
		<name>yarn.resourcemanager.zk.state-store.address</name>
		<value>xxdata001:2181,xxdata002:2181,xxdata003:2181</value>
	</property>
	<!-- Client访问RM的RPC地址 (applications manager interface) -->
	<property>
		<name>yarn.resourcemanager.address.rm1</name>
		<value>xxdata001:23140</value>
	</property>
	<property>
		<name>yarn.resourcemanager.address.rm2</name>
		<value>xxdata002:23140</value>
	</property>
	<!-- AM访问RM的RPC地址(scheduler interface) -->
	<property>
		<name>yarn.resourcemanager.scheduler.address.rm1</name>
		<value>xxdata001:23130</value>
	</property>
	<property>
		<name>yarn.resourcemanager.scheduler.address.rm2</name>
		<value>xxdata002:23130</value>
	</property>
	<!-- RM admin interface -->
	<property>
		<name>yarn.resourcemanager.admin.address.rm1</name>
		<value>xxdata001:23141</value>
	</property>
	<property>
		<name>yarn.resourcemanager.admin.address.rm2</name>
		<value>xxdata002:23141</value>
	</property>
	<!--NM访问RM的RPC端口 -->
	<property>
		<name>yarn.resourcemanager.resource-tracker.address.rm1</name>
		<value>xxdata001:23125</value>
	</property>
	<property>
		<name>yarn.resourcemanager.resource-tracker.address.rm2</name>
		<value>xxdata002:23125</value>
	</property>
	<!-- RM web application 地址 -->
	<property>
		<name>yarn.resourcemanager.webapp.address.rm1</name>
		<value>xxdata001:8088</value>
	</property>
	<property>
		<name>yarn.resourcemanager.webapp.address.rm2</name>
		<value>xxdata002:8088</value>
	</property>
	<property>
		<name>yarn.resourcemanager.webapp.https.address.rm1</name>
		<value>xxdata001:23189</value>
	</property>
	<property>
		<name>yarn.resourcemanager.webapp.https.address.rm2</name>
		<value>xxdata002:23189</value>
	</property>



	<property>
	   <name>yarn.log-aggregation-enable</name>
	   <value>true</value>
	</property>
	<property>
		 <name>yarn.log.server.url</name>
		 <value>http://xxdata001:19888/jobhistory/logs</value>
	</property>


	<property>
		<name>yarn.nodemanager.resource.memory-mb</name>
		<value>3072</value>
	</property>
	<property>
		<name>yarn.scheduler.minimum-allocation-mb</name>
		<value>1024</value>
		<discription>单个任务可申请最少内存,默认1024MB</discription>
	 </property>

  
  <property>
	<name>yarn.scheduler.maximum-allocation-mb</name>
	<value>3072</value>
	<discription>单个任务可申请最大内存,默认8192MB</discription>
  </property>

   <property>
       <name>yarn.nodemanager.resource.cpu-vcores</name>
       <value>2</value>
    </property>

</configuration>

2、启动yarn服务(xxdata001启动)

[hadoop@xxdata001 ~]$ /home/hadoop/app/hadoop/sbin/start-yarn.sh 

//手动启动备机(RM2),备机不会自动启动。
[hadoop@xxdata002 ~]$ ~/app/hadoop/sbin/yarn-daemon.sh start resourcemanager

//启动或停止某个单独进程
[hadoop@xxdata002 ~]$ ~/app/hadoop/sbin/yarn-daemon.sh [start,stop] [resourcemanager,nodemanager]

//手动启动JobHistory
[hadoop@xxdata001 sbin]$ ~/app/hadoop/sbin/mr-jobhistory-daemon.sh  start historyserver

3、查看MR1、MR2、JobHistory web页面

HDFS命名空间

hdfs高可用被连接时该如何保证?如果NameNode Active垮掉了连接就断了。

所以引进了nameservices,设置命名空间,该空间中包含了所有的namenode,当NameNode Active垮掉了之后,会自动连接到NameNode Standby(这时候Standby会自主变为Active)上,从而保障了连接hdfs时的高可用。

本段配置是我从刚才hdfs-site.xml中截取的,看完此段xml后相信你对命名空间这个概念就清晰了

<!--HDFS高可用配置 -->
        <!--指定hdfs的nameservice为dw2,需要和core-site.xml中的保持一致 -->
        <property>
                <name>dfs.nameservices</name>
                <value>dw2</value>
        </property>
        <property>
                <!--设置NameNode IDs 此版本最大只支持两个NameNode -->
                <name>dfs.ha.namenodes.dw2</name>
                <value>nn1,nn2</value>
        </property>

<!-- Hdfs HA: dfs.namenode.rpc-address.[nameservice ID] rpc 通信地址 -->
        <property>
                <name>dfs.namenode.rpc-address.dw2.nn1</name>
                <value>xxdata001:8020</value>
        </property>
        <property>
                <name>dfs.namenode.rpc-address.dw2.nn2</name>
                <value>xxdata002:8020</value>
        </property>