标题Zookeeper@[toc]
Zookeeper
1. 简介
Apache ZooKeeper是Apache软件基金会的一个软件项目,它为大型[分布式计算]提供[开源]的分布式配置服务、同步服务和命名注册。
简言:ZK就是一个管理多个服务的
集群管理者
+文件系统
核心解决问题:
- 同步配置集群配置信息(文件系统)
- 监听服务器的上下线,同时可以做出反应。(监听器)
2. Zookeeper场景需求
需求1:(监听器)
需求2:(文件系统)
需求分析3
3.Zookeeper作用
# 核心解决问题:
1. 文件管理者:同步配置集群配置信息到所有节点(文件系统)
2. 监听服务器的上下线,同时可以做出反应。(监听器即集群管理者)
# zookeeper
1. 存数据,所有zk节点同步数据。
2. 监听器,服务器和数据变化,事件发生可以做出反应(执行代码)
3. zk集群本身 稳定性 数据可靠性 非常高。
补充:
zookeeper以后的客户端实际上是zk要管理的集群服务器(软件程序)。
4. 特点
1主(Leader)多从(Follower),一旦故障就能立刻选出新的主机,继续提供服务。
自身稳定性No单点故障
集群中只要有半数以上zk节点存活,zk集群就能正常使用。
服务节点可靠性
选主的操作,使用
投票机制
。服务的高效性、可靠性、操作的可靠性
任何一台zk主机添加的数据,都会同步到所有所有zk主机。
全局数据同步
zk数据更新要么全部成功,要么全部失败。
事务性,数据一致性
5. 集群角色简介
leader(主机)是zookeeper集群的核心。
1.zookeeper集群工作的核心。
2.事务操作的老大(增删改操作得让他点头)
follower(从机)
1.承担非事务性操作,读操作。
2.参与leader选举的投票
observer(观察者,了解)
1.承担非事务性操作,读操作
2.不能参与leader选举的投票
6.投票选主流程
主机不固定,动态选举,保证主机的可靠性,选举过程中主从之间有通信
# 假设有5台服务器,编号1,2,3,4,5。
服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于(选举状态)。
服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是(选举状态)
服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
服务器4启动,已经有老大,服务器4只能成为小弟。
服务器5启动,后面的逻辑同服务器4成为小弟。
Zookeeper的安装与使用
1. 集群规划
# 关键工作
1. 安装开始未启动,zk集群中谁是leader,不确定
2. 每个zkServer有一个myid,当前zkserver的一个编号,唯一。
(myid的值是ip地址)
3. 心跳:
follower从机,会定时向主机发送心跳。让主机实时掌握从机状态信息。
投票:
启动和有zkServer挂掉,zkServer需要接受投票信息选主。
4. 数据同步
zkServer会自动同步集群中的数据。
5. zkServer中的数据:
保存在内存中。(数据量少,访问频率高)
硬盘空间(data文件夹)
2.安装
-
准备机器3台
1. 设置ip 2. 安装jdk 3. 配置java环境变量 4. 关闭防火墙 5. 设置hostname 6. 设置hosts(3台彼此之间集群互通)
-
解压
tar zxvf zookeeper-3.4.14.tar.gz -C /opt/installs
-
配置path环境变量
export PATH=$PATH:$JAVA_HOME/bin:/opt/installs/zookeeper3.4.14/bin
source /etc/profile
-
初始化配置
-
在zookeeper下创建data文件夹
-
在conf目录下通过拷贝的方式创建zoo.cfg文件
(cp zoo_sample.cfg zoo.cfg)
- 配置zookeeper的文件存放位置
dataDir=/opt/install/zookeeper3.4.14/data
-
-
标记zk的主机号(为当前zk节点定义编号)
在zk的data文件下创建
myid
文件,并指定一个zk主机编号,一般用ip为编号。20
-
配置zk集群的所有节点信息
编辑zoo.cfg配置文件
server.20=192.168.153.20:2888:3888 server.21=192.168.153.21:2888:3888 server.22=192.168.153.22:2888:3888
说明:
-
同步配置到所有zk在的宿主机上
scp -r ./zookeeper root@ip:/opt/installs/
-
修改每个机器上的myid
-
管理zk
- 启动
./zkServer.sh start
- 查看状态
./zkServer.sh status
(包括名称)- 停止
./zkServer.sh stop
- 客户端
./zkCli.sh
登录本机的zk./zkCli.sh -server ip:2181
登录指定ip的zk主机 -
zookeeper进程
-
zookeeper主从
3. ZKshell
文件系统
-
文件系统结构
-
特点
- 每个节点就是一个文件
- 每个节点都有一个名字一个值。
- 每个节点即可以是文件又可以是文件夹,下面可以继续挂节点。
-
图
1.客户端使用基本命令 1. 进入客户端 zkCli.sh 2. 查看帮助命令 [zk: localhost:2181(CONNECTED) 1] help 3. 退出客户端 [zk: localhost:2181(CONNECTED) 1] quit
2.数据管理命令 # 1. 浏览某个节点下的子节点(的名字) ls / # 2. 创建节点,并指定他的值。 [zk: localhost:2181(CONNECTED) 8] create /lihao "李昊" Created /lihao # 3. 查看节点的值 [zk: localhost:2181(CONNECTED) 10] get /lihao 李昊 # 数据 cZxid = 0x200000005 ctime = Fri Apr 10 17:55:04 CST 2020 # 创建时间 mZxid = 0x200000005 mtime = Fri Apr 10 17:55:04 CST 2020 pZxid = 0x200000005 cversion = 0 dataVersion = 0 # 节点数据的更新次数【只要执行set就更新】 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 10 # 节点数据的字节,最大值1M numChildren = 0 # 直接子节点的个数 # 4. 修改节点的值 [zk: localhost:2181(CONNECTED) 45] set /lihao "新值" # 5. 删除节点 [zk: localhost:2181(CONNECTED) 53] delete /lihao # 6. 删除节点及其子节点(递归删除) [zk: localhost:2181(CONNECTED) 53] rmr /lihao
命令 含义 ls / 浏览某个节点下的子节点(的名字) create /节点 “节点值” 创建节点,并指定他的值。 get /节点 查看节点的值 set /节点 “新值” 修改节点的值 delete /节点 删除某个节点 rmr /节点 删除该节点,并递归删除内部所有节点。 -
-
节点类型(文件类型)
# 节点类型 zookeeper可以将节点设置不同的类型 1. 持久化节点 节点只要不删除,会一致存在。 2. 顺序编号节点 每次对相同节点,重复创建,会自动对znode名称进行编号 3. 临时节点 客户端断开,则节点消失。
节点名称 中文 含义 PERSISTENT
持久化节点 客户端与zookeeper断开连接后,该节点依旧存在 PERSISTENT_SEQUENTIAL
持久化顺序编号节点 客户端与zookeeper断开连接后,该节点依旧存在,
只是Zookeeper给该节点名称进行顺序编号EPHEMERAL
临时节点 客户端与zookeeper断开连接后,该节点被删除 EPHEMERAL_SEQUENTIAL
临时顺序编号节点 客户端与zookeeper断开连接后,该节点被删除,
只是Zookeeper给该节点名称进行顺序编号命令 含义 create /节点 “节点值” 持久化节点 create -s /节点 “节点值” 持久化节点+顺序编号节点 create -e /节点 “节点值” 临时节点,客户端断开连接则失效。 create -s -e /节点 “节点值” 顺序编号节点+临时节点
监听通知
-
监听机制
- 如果监听某个节点值,get方式,如果该值发生变化,则监听者会接收到zk的通知。
- 监听某个节点下所有节点,ls方式,如果节点下任意节点发生变化,则监听者会受到zk的通知。
命令 含义 get /节点1/节点2 watch 查看节点内容,并监听该值的变化(修改、失效等) ls /节点 watch 查看某个节点下的所有节点信息,并监听下节点的变化(添加删除子节点)
4. zkShell命令
命令 | 含义 |
---|---|
help | 帮助 |
ls /节点 | 显示指定节点下的所有节点 |
ls /节点 watch | 查看某个节点下的所有节点信息,并监听下节点的变化 |
create /节点 “节点值” | 创建节点,并指定值。 |
create -e /节点 “节点值” | 创建节点,-e是指定为临时节点,客户端断开连接或者重启则失效。 |
create -s /节点 “节点值” | 创建有序节点(数据库集群中id生成器)多次创建该节点,节点名字序号自增 |
get /节点1/节点2 | 获得指定节点的内容值。 |
get /节点1/节点2 watch | 查看节点内容,并监听该值的变化(修改、删除、失效等) |
set /节点 “新值” | 修改节点的值 |
delete /节点 | 删除某个节点 |
rmr /节点 | 删除该节点,并递归删除内部所有节点。 |
Java访问Zookeeper
1. 依赖导入
导入依赖
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
导入log4j.properties
2. 连接zookeeper
//1. 准备参数
String zkClusters = "192.168.199.40:2181,192.168.199.41:2181,192.168.199.42:2181";
//2. 创建zk客户端
zkClient zk = new ZkClient(zkClusters);
//
3. 文件系统相关
1. 获得子节点
public void test1() {
//1.创建zkClient客户端
String zkcluster = "192.168.153.20:2181,192.168.153.21:2181,192.168.153.22:2181";
ZkClient zk = new ZkClient(zkcluster);
//2.获得子节点
List<String> children = zk.getChildren("/");
for (String child : children) {
System.out.println(child);
}
}
2.获得某个节点下以及所有孙子节点
public void test2() {
//1.创建zkClient客户端
String zkcluster = "192.168.153.20:2181,192.168.153.21:2181,192.168.153.22:2181";
ZkClient zk = new ZkClient(zkcluster);
//2.获得子节点
List<String> children = zk.getChildren("/");
for (String child : children) {
List<String> sunzi = zk.getChildren("/" + child);
for (String s : sunzi) {
System.out.println(s);
}
}
}
3. 添加
public void test3() {
//1.创建zkClient客户端
String zkcluster = "192.168.153.20:2181,192.168.153.21:2181,192.168.153.22:2181";
ZkClient zk = new ZkClient(zkcluster);
//2.创建
String s = zk.create("/zookeeper/lihao/haha", "哈哈", CreateMode.PERSISTENT);
System.out.println(s);
}
4.读取(查看节点的值)
public void test4() {
//1.创建zkClient客户端
String zkcluster = "192.168.153.20:2181,192.168.153.21:2181,192.168.153.22:2181";
ZkClient zk = new ZkClient(zkcluster);
//2.读取节点的值
Object o = zk.readData("/zookeeper/lihao/haha");
System.out.println((String)o);
}
5.判断节点是否存在
// 判断节点是否存在
boolean exists = zkClient.exists("/zookeeper/lihao");
// 删除节点
boolean delete = zkClient.delete("/zookeeper/lihao/haha");
//递归删除,适合删除文件夹类节点
boolean delete = zkClient.deleteRecursive("/zookeeper/lihao");
private ZkClient zkClient;
@Before /** 此注解和Test经常一起使用 在Test执行前先执行一下
public void before() {
String zkcluster = "192.168.153.20:2181,192.168.153.21:2181,192.168.153.22:2181";
zkClient = new ZkClient(zkcluster);
}
/**
* 判断是否存在 和删除
*/
@Test
public void test5() {
boolean b = zkClient.exists("/cyf");
System.out.println(b);
}
/**
* 判断是否存在 /root_id,不存在就创建,如果存在就打印该节点的信息值
*/
@Test
public void test6() {
boolean b = zkClient.exists("/root_id");
if(b){
Object o = zkClient.readData("/root_id");
System.out.println(o);
}else{
zkClient.create("/root_id","new created",CreateMode.PERSISTENT);
}
}
6.修改节点数据
/**
* 修改 /zookeeper/lihao/haha 的值
*/
@Test
public void test7() {
zkClient.writeData("/zookeeper/lihao/haha","123456");
}
/**
* 修改 /zookeeper/lihao/haha 的值
*/
@Test
public void test7() {
//zkClient.writeData("/zookeeper/lihao/haha","123456");
Stat stat = zkClient.writeDataReturnStat("/zookeeper/lihao/haha", "654321",-1);
int version = stat.getVersion();
System.out.println(version);
}
7. 删除节点与递归删除
/**
* 删除
*/
@Test
public void test8() {
boolean delete = zkClient.delete("/zookeeper/lihao/haha");
//递归删除
//zkClient.deleteRecursive("/zookeeper/lihao/haha");
System.out.println(delete);
}
8. 创建不同节点类型
//创建持久性节点
String path = zkClient.create("/cluster", "tomcat集群", CreateMode.PERSISTENT);
//创建临时节点
zkClient.create("/eznode", "", CreateMode.EPHEMERAL);
//创建临时有序节点
//客户端断开连接:zkClient.close() 和超时30都算
String path = zkClient.create("/cluster", "tomcat集群", CreateMode.EPHEMERAL_SEQUENTIAL);
//创建持久有序节点
zkClient.create("/pe","ID",CreateMode.PERSISTENT_SEQUENTIAL);
4. 监听服务相关
- 工作机制
1. 监听单节点
监听删除和修改
public class ZnodeListener {
/**
* 监听 /conf/db 的值的变化
* ls|get watch
*/
@Test
public void test1() throws InterruptedException {
//1.创建zkClient客户端
String zkcluster = "192.168.153.20:2181,192.168.153.21:2181,192.168.153.22:2181";
ZkClient zk = new ZkClient(zkcluster);
//监听
zk.subscribeDataChanges("/conf/db", new IZkDataListener() {
//一旦该值发生了变化,会调用该方法
/**
*
* @param s 被监听的节点
* @param o 修改后的新值
* @throws Exception
*/
@Override
public void handleDataChange(String s, Object o) throws Exception {
System.out.println("节点"+s);
System.out.println("新值"+o);
}
//如果节点被删除了 调用此方法
@Override
public void handleDataDeleted(String s) throws Exception {
System.out.println(s+"被删除了");
}
});
Thread.sleep(120000);
}
}
2. 监听子节点(添加和删除)
监听子节点的添加和删除
public void test2() throws InterruptedException {
//1.创建zkClient客户端
String zkcluster = "192.168.153.20:2181,192.168.153.21:2181,192.168.153.22:2181";
ZkClient zk = new ZkClient(zkcluster);
//监听子节点的添加和删除
zk.subscribeChildChanges("/conf", new IZkChildListener() {
/**
* 当子节点发生变化 (添加 删除)的时候,会调用
* @param s 被监听的节点
* @param list 变化后的所有子节点
* @throws Exception
*/
@Override
public void handleChildChange(String s, List<String> list) throws Exception {
System.out.println("监听节点"+s);
System.out.println("所有子节点"+list);
}
});
Thread.sleep(120000);
}
应用场景案例
1. 分布式ID生成器
package demo3;
import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
public class GlobalIDGenerator {
private String zkCluster;
private String rootPath;
private String tablename;
private ZkClient zk;
/**
* 1.第一次使用时候,创建GlobalIDGenerator对象
* @param zkCluster
* @param rootPath
* @param tablename
*/
public GlobalIDGenerator(String zkCluster, String rootPath, String tablename) {
this.zkCluster = zkCluster;
//初始化zkClient
zk = new ZkClient(zkCluster);
this.rootPath = rootPath;
this.tablename = tablename;
}
/**
* 2.第一次使用该对象的生成id方法,初始化(节点信息)
*/
public void init(){
//判断是否已经存在了rootPath节点,不存在则创建
boolean exists = zk.exists(rootPath);
if (!exists){
zk.create(rootPath,"idGenerator随意", CreateMode.PERSISTENT);
}
//判断"/user_id_generator"节点是否存在,不存在则创建
boolean exists1 = zk.exists(rootPath + "/" + tablename + "_id_generator");
if (!exists1){
zk.create(rootPath+"/"+tablename+"_id_generator","随意",CreateMode.PERSISTENT);
}
}
/**
* 3.每次获得id,调用
* @return
*/
public int generateId() {
//修改id_generator的值
Stat stat = zk.writeDataReturnStat(rootPath + "/"+tablename+"_id_generator","", -1);
return stat.getVersion();
}
}
package demo3;
public class DistributedIDGenerator {
public static void main(String[] args) {
//像oeder表添加数据,获得order的id生成器的id值
//1.为order表创建一个id生成器对象
GlobalIDGenerator orderId = new GlobalIDGenerator("192.168.153.20:2181,192.168.153.21:2181,192.168.153.22:2181", "/root_id", "order");
//2.初始化节点
orderId.init();
//3.每次获得id,generateId()
for (int i = 0; i < 300; i++) {
int id = orderId.generateId();
System.out.println(id);
//order.setId(id) 设置值
//XxxDao.insert(order) //插入表中
}
}
}
2. Zookeeper的集群zkServer服务个数
2N+1个
原因:3台zkServer的稳定性要大于4台。
1台的稳定性大于2台的稳定性。
3.注册中心
-
配置信息类
@AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString public class DbConfigure implements Serializable { private String url; private String username; private String password; private String driverClassName; }
-
配置信息同步类
public class DBConfigWatcher implements Runnable { private String dbConfigNode = "/confs/datasource"; private String cluster="192.168.199.40:2181,192.168.199.41:2181,192.168.199.42:2181"; private ZkClient zkClient; private DbConfigure dbConfigure; public void run() { //1. 启动zookeeper的配置文件监听服务。 zkClient = new ZkClient(cluster); zkClient.subscribeDataChanges(dbConfigNode,new IZkDataListener(){ public void handleDataChange(String s, Object o) throws Exception { //修改值。 dbConfigure = zkClient.readData(dbConfigNode); System.out.println(dbConfigure); System.out.println("刷新配置文件"); } public void handleDataDeleted(String s) throws Exception { //删除节点 System.out.println("配置信息被删除"); } }); //2. 监听配置文件节点,如果修改,则修改配置文件信息。 } }
4. 集群管理
监听服务器上下线,和服务器替换
public class ServerStateWatcher implements Runnable{
private String serversNode = "/servers";
private String cluster="192.168.199.40:2181,192.168.199.41:2181,192.168.199.42:2181";
private ZkClient zkClient = new ZkClient(cluster);
/**
* 添加服务
*/
public void regist(String address){
//创建临时有序节点,如果,客户端断开。则
zkClient.create("/servers/server",address, CreateMode.EPHEMERAL_SEQUENTIAL);
}
/**
* 服务器下线
*
*/
public void release(){
zkClient.close();
}
/**
* 监控线程.
*/
public void run() {
//监控服务子节点的增删
zkClient.subscribeChildChanges(serversNode, new IZkChildListener() {
public void handleChildChange(String s, List<String> list) throws Exception {
System.out.println("服务器列表变更目前服务器列表:"+list);
}
});
}
}
底层原理
1. 监听通知工作机制
a.流程
b.机制
2. zk读写数据工作流程
a.读写操作(create、set、delete和读取)
b.流程
3.启动(新集群)zkServer投票选主流程(了解)
选主结果:
1:启动顺序.
2:myid(权重,级别,级别高能够获得到级别低的zk机器的投票)
3: 每个机器启动,会给自己投票。
主机不固定,动态选举,保证主机的可靠性,选举过程中主从之间有通信
# 假设有5台服务器,编号1,2,3,4,5。
服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于(选举状态)。
服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是(选举状态)
服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
服务器4启动,已经有老大,服务器4只能成为小弟。
服务器5启动,后面的逻辑同服务器4成为小弟。
HadoopHA(高可用)集群搭建
规避 数据|服务器 单点故障的问题。(即HA)
# 目前hadoop分布式集群的问题
1. editslog无法做到100%持久化,如果namenode宕机会有少量数据就丢失了。
2. 一旦NameNode宕机,整个hadoop集群还是会停止服务。
3. 如果启动2个NameNode,缺乏自动切换,就算namenode自动切换,客户端访问的ip地址也无法访问(需要虚拟IP包含主备的IP)
Hadoop1.x存在问题及解决办法
HAHadoop架构规划总结以及zkfc的工作补充说明
# HAHadoop集群架构设计
QJM(Quorum Journal Manager)是Hadoop专门为Namenode共享存储基于zookeeper开发的组件
1: 引入了监控Namenode状态的ZookeeperFailController(ZKFC),ZKFC一般运行在Namenode的宿主机器上,与Zookeeper集群协作完成故障的自动转移
2: 提供了Journal Node,每个QJN暴露一个RPC接口(远程调用),可以接受NameNode(主机)写入editslog的请求,也可以接受NameNode(备机)读取editslog的请求。
而且写入数据,也遵循了zk投票机制,安全可靠。
NameNode(备机)
还充当了SecondaryNameNode的职责,完成checkpoint,Fsimage数据的合并的职责。
Hadoop-Zookeeper工作机制
Hadoop(HA)搭建
# 集群规划--搭建思路
准备主机(4台)
ip、防火墙、hostname、hosts
安装必备软件
# 1.安装必备软件
1. 解压jdk
[root@centos8 modules]# tar zxvf jdk-8u221-linux-x64.tar.gz -C /opt/installs/
2. 解压hadoop
[root@centos8 modules]# tar zxvf hadoop-2.9.2.tar.gz -C /opt/installs/
3. 解压zookeeper
[root@centos8 modules]# tar zxvf zookeeper-3.4.14.tar.gz -C /opt/installs/
4. 安装psmisc
[root@centos8 modules]# yum install -y psmisc
# 2.统一配置如上的环境变量
1. ----------------/etc/profile-----------------------------
#JAVA
export JAVA_HOME=/opt/installs/jdk1.8
export PATH=$PATH:$JAVA_HOME/bin
# HADOOP
export HADOOP_HOME=/opt/installs/hadoop2.9.2/
export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
# zookeeper
export PATH=$PATH:/opt/installs/zookeeper3.4.14/bin/
2. 加载配置文件
source /etc/profile
# 3. 同步4台主机的其他配置
0. 设置ip
1. 远程拷贝安装软件
2. 加载配置文件 /etc/profile
3. 设置hostname(4个主机)
hostnamectl set-hostname xxxx
4. 设置hosts映射
192.168.153.30 hadoop30
192.168.153.31 hadoop31
192.168.153.32 hadoop32
192.168.153.33 hadoop33
5. 配置免密登录
# 生成密钥
[root@hadoop30 ~]# ssh-keygen
[root@hadoop31 ~]# ssh-keygen
# 发送公钥(所有NameNode节点都要发送)
[root@hadoop30 ~]# ssh-copy-id hadoop30
[root@hadoop30 ~]# ssh-copy-id hadoop31
[root@hadoop30 ~]# ssh-copy-id hadoop32
[root@hadoop30 ~]# ssh-copy-id hadoop33
[root@hadoop31 ~]# ssh-copy-id hadoop30
[root@hadoop31 ~]# ssh-copy-id hadoop31
[root@hadoop31 ~]# ssh-copy-id hadoop32
[root@hadoop31 ~]# ssh-copy-id hadoop33
zookeeper配置(3台)
# 1. 新建data文件夹
data下myid给分配不同值
# 2. 配置conf下的zoo.cfg配置文件
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/opt/installs/zookeeper3.4.14/data #data文件目录
clientPort=2181
server.30=192.168.153.30:2888:3888 # zk主机信息
server.31=192.168.153.31:2888:3888 # zk主机信息
server.32=192.168.153.32:2888:3888 # zk主机信息
# 3. 拷贝配置信息到其他主机
[root@hadoop30 conf]# scp zoo.cfg root@hadoop31:/opt/installs/zookeeper3.4.14/conf/
[root@hadoop10 conf]# scp zoo.cfg root@hadoop32:/opt/installs/zookeeper3.4.14/conf/
# 4. 修改每个主机的myid
# 5. 启动zkServer验证是否成功。
1. 启动3台zk主机
zkServer.sh start
2. 查看状态
zkServer.sh status
3. 关闭zkServer
HAHadoop配置(HDFS)
# 0. data文件夹
1. 如果新安装的hadoop需要创建data文件夹
2. 如果是已经安装过的,之前旧的,需要删除data文件夹下的文件。
# 1. 配置hadoop-env.sh
export JAVA_HOME=/opt/install/jdk1.8/
# 2. 配置core-site.xml
<configuration>
<!--hdfs入口,设置虚拟地址,具体地址后面配置-->
<property>
<name>fs.defaultFS</name>
<value>hdfs://ns</value>
</property>
<!--hdfs集群的文件位置-->
<property>
<name>hadoop.tmp.dir</name>
<value>/opt/install/hadoop2.9.2/data</value>
</property>
<!--hdfs要访问zookeeper集群-->
<property>
<name>ha.zookeeper.quorum</name>
<value>hadoop30:2181,hadoop31:2181,hadoop32:2181</value>
</property>
</configuration>
# 3. 配置hdfs-site.xml
<configuration>
<!-- 副本数 -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!-- 定义hdfs入口的命名服务 -->
<property>
<name>dfs.nameservices</name>
<value>ns</value>
</property>
<!-- 定义hdfs入口的命名服务下虚拟ip-->
<property>
<name>dfs.ha.namenodes.ns</name>
<value>nn1,nn2</value>
</property>
<!-- 虚拟ip地址1 RPC入口 -->
<property>
<name>dfs.namenode.rpc-address.ns.nn1</name>
<value>hadoop30:9000</value>
</property>
<!-- 虚拟ip地址1 HTTP入口 -->
<property>
<name>dfs.namenode.http-address.ns.nn1</name>
<value>hadoop30:50070</value>
</property>
<!-- 虚拟ip地址2 PRC入口 -->
<property>
<name>dfs.namenode.rpc-address.ns.nn2</name>
<value>hadoop31:9000</value>
</property>
<!-- 虚拟ip地址1 HTTP入口 -->
<property>
<name>dfs.namenode.http-address.ns.nn2</name>
<value>hadoop31:50070</value>
</property>
<!-- namenode要向zk的QJN写入editslog,所以要明确入口地址 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://hadoop30:8485;hadoop31:8485;hadoop32:8485/ns</value>
</property>
<!-- 配置ZKFC故障切换 -->
1.开启ZKFC
2.要使用的zkfc故障切换的代码类
3.故障切换杀死进程使用的方式
4.指定免密登录的私钥地址
<!-- 是否开启故障切换 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<!-- 基于zookeeper的故障切换的代码类 -->
<property>
<name>dfs.client.failover.proxy.provider.ns</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 远程杀死namenode方式(防止namenode假死,导致双主出现) -->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<!-- 指定私钥的文件目录,使用免密登录杀死NN进程 -->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
</configuration>
# 4. 配置slaves
hadoop31
hadoop32
hadoop33
# 5. 同步配置到其他主机上
[root@hadoop30 etc]# scp -r hadoop/ root@hadoop31:/opt/install/hadoop2.9.2/etc/
[root@hadoop30 etc]# scp -r hadoop/ root@hadoop32:/opt/install/hadoop2.9.2/etc/
[root@hadoop30 etc]# scp -r hadoop/ root@hadoop33:/opt/install/hadoop2.9.2/etc/
HAHadoop启动
先启动Zookeeper(30、31、32),再启动HDFS
启动顺序:zkServer—hdfs
# 1. 启动zookeeper(3台)
[root@hadoop30 etc]# zkServer.sh start
[root@hadoop31 etc]# zkServer.sh start
[root@hadoop32 etc]# zkServer.sh start
# 2. 初始化ZKFS故障切换进程(在namenode主节点)【第一次启动需要做】
[root@hadoop30 etc]# hdfs zkfc -formatZK
# 3. 启动qjournalnode进程。(3台)
启动后可以查看journalenode节点进程
[root@hadoop30 etc]# hadoop-daemon.sh start journalnode
[root@hadoop31 etc]# hadoop-daemon.sh start journalnode
[root@hadoop32 etc]# hadoop-daemon.sh start journalnode
# 4. 格式化hdfs主机namenode(在namenode主节点)【第一次启动需要做】
hdfs namenode -format ns
# 5. 启动hdfs集群(namenode主节点)
# 因为配置了HA HDFS的zkfc,所以,会自动启动zkfc故障切换进程
start-dfs.sh
# 6. 格式化hdfs的namenode备机(namenode standby备节点)【第一次启动需要做】
[root@hadoop31 ~]# hdfs namenode -bootstrapStandby
# 7. 启动hdfs的namenode备机(namenode standby备节点)
[root@hadoop31 ~]# hadoop-daemon.sh start namenode
进程图如下
验证
# 1. 查看两个namenode状态
一个active一个standby
# 2. kill掉active
active失效,standby转为active
# 3. 重启namenode,发现启动后变成standby
hadoop-daemon.sh start namenode
# 4. namenode主机添加文件后,杀死,看另一个namenode也有数据,高可用实现。
Java程序访问
package demo4;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.io.IOException;
public class HDFSHaTest {
public static void main(String[] args) throws IOException {
Configuration conf = new Configuration();
conf.addResource("/core-site.xml");
conf.addResource("/hdfs-site.xml");
FileSystem fs = FileSystem.get(conf);
FileStatus[] files = fs.listStatus(new Path("/hdfs"));
for (FileStatus file : files) {
System.out.println(file.getPath());
System.out.println(file);
}
}
}