我是靠谱客的博主 俭朴滑板,最近开发中收集的这篇文章主要介绍ZooKeeper - 分布式服务管理概述ZooKeeper 内部原理Zookeeper实战,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
概述
- ZooKeeper是一个分布式的,开源的分布式应用程序协调服务,是Hadoop、Hbase、kafka、dubbo等重要组件。
- ZooKeeper是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。ZooKeeper包含一个简单的原语集,提供Java和C的接口。
- 特点
- 简单:ZooKeeper的核心是一个精简的文件系统 ,它支持一些简单的操作和一些抽象操作
- 丰富:ZooKeeper的操作是很丰富的,可实现一些协调数据结构和协议。
- 高可靠:ZooKeeper支持集群模式,可以很容易的解决单点故障问题
- 松耦合交互:不同进程间的交互不需要了解彼此,甚至可以不必同时存在,某进程在ZooKeeper中留下消息后,该进程结束后其它进程还可以读这条消息
- 资源库:ZooKeeper实现了一个关于通用协调模式的开源共享存储库,能使开发者免于编写这类通用协议
数据结构
- ZooKeeper的角色
- leader(领导者):负责进行投票的发起和决议,更新系统状态
- learner (学习者):包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并向客户端返回结果,在选举过程中参与投票。Observer可以接受客户端连接,将写请求转发给leader,但observer不参与投票过程,只同步leader的状态,observe r的目的是为了扩展系统,提高读取速度
- 客户端(client):请求发起方
- ZooKeeper数据模型
- 层次化的目录结构,命名符合常规文件系统规范
- 每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识,节点znode可以包含数据和子节点,但是EPHEMERAL类型的节点不能有子节点
- znode中的数据可以有多个版本,比如某一个路径下存有多个数据版本,那么查询这个路径下的数据就需要带上版本
- 客户端应用可以在节点上设置监视器,节点不支持部分读写,而是一次性完整读写
- ZooKeeper的节点
- zookeeper中包含两个类型的节点
- 临时节点(ephemeral):在节点客户端会话结束时,ephemeral节点自动删除,没有子节点
- 持久节点(persistent):不依赖与客户端会话,只有当客户端明确要删除该persistent节点时才会被删除
- znode的类型在创建时确定并且之后不能再修改,znode默认指定类型是临时节点
- ZooKeeper的客户端和服务器通信采用长连接方式 ,每个客户端和服务器通过心跳来保持连接,这个连接状态称之为session ,如果znode是临时节点,这个seesion失效,znode也就删除了
- zookeeper中包含两个类型的节点
应用场景
- 提供的服务包括:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡等。
- 统一命名服务
- 在分布式环境下,经常需要对应用/服务进行统一命名,便于识别不同服务
- 类似于域名与ip之间对应关系,ip不容易记住,而域名容易记住
- 通过名称来获取资源或服务的地址,提供者等信息
- 在分布式环境下,经常需要对应用/服务进行统一命名,便于识别不同服务
- 统一配置管理
- 分布式环境下,配置文件管理和同步是一个常见问题
- 一个集群中,所有节点的配置信息是一致的,比如 Hadoop 集群
- 对配置文件修改后,希望能够快速同步到各个节点上
- 配置管理可交由ZooKeeper实现
- 可将配置信息写入ZooKeeper上的一个Znode
- 各个节点监听这个Znode
- 一旦Znode中的数据被修改,ZooKeeper将通知各个节点
- 分布式环境下,配置文件管理和同步是一个常见问题
- 统一集群管理
- 分布式环境中,实时掌握每个节点的状态是必要的
- 可根据节点实时状态做出一些调整
- 可交由ZooKeeper实现
- 可将节点信息写入ZooKeeper上的一个Znode
- 监听这个Znode可获取它的实时状态变化
- 典型应用
- HBase中Master状态监控与选举
- 分布式环境中,实时掌握每个节点的状态是必要的
- 服务器动态上下线
- 客户端能实时洞察到服务器上下线的变化
- 软负载均衡
ZooKeeper 内部原理
ZooKeeper中的选举机制
- 服务器的ID
- 分别1,2,3,编号越大在选择算法中的权重越大
- 数据的ID
- 服务器中存放的最大数据ID,值越大说明数据越新,在选举算法中数据越新权重越大
- 编辑时钟
- 投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断
- 选举状态
- LOOKING,竞选状态
- FOLLOWING,随从状态,同步leader状态,参与投票
- OBSERVING,观察状态,同步leader状态,不参与投票
- LEADING,领导者状态
- 选举信息的内容
- 在投票完成后,需要将投票信息发送给集群中的所有服务器,它包含如下内容
- 服务器ID
- 数据ID
- 逻辑时钟
- 选举状态
- 在投票完成后,需要将投票信息发送给集群中的所有服务器,它包含如下内容
- 选举机制的结果:zk启动之后通过选举机制,来选举出来一个leader
- 半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器
- Zookeeper虽然在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的
- 选举过程举例
- 服务器1启动,此时只有它一台服务器启动了,它发出去的报文没有任何响应,所以它的选举状态一直是LOOKING状态
- 服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1、2还是继续保持LOOKING状态
- 服务器3启动,根据前面的理论分析,服务器3成为服务器1、2、3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的Leader
- 服务器4启动,根据前面的分析,理论上服务器4应该是服务器1、2、3、4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了
- 服务器5启动,同4一样当小弟
Stat结构体
- czxid:创建节点的事务zxid
- 每次修改ZooKeeper状态都会收到一个zxid形式的时间戳,也就是ZooKeeper事务ID。事务ID是ZooKeeper中所有修改总的次序。每个修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前发生。
- ctime:znode被创建的毫秒数(从1970年开始)
- mzxid:znode最后更新的事务zxid
- mtime:znode最后修改的毫秒数(从1970年开始)
- pZxid:znode最后更新的子节点zxid
- cversion:znode子节点变化号,znode子节点修改次数
- dataversion:znode数据变化号
- aclVersion:znode访问控制列表的变化号
- ephemeralOwner:如果是临时节点,这个是znode拥有者的session id。如果不是临时节点则是0
- dataLength:znode的数据长度
- numChildren:znode子节点数量
监听器原理
- 监听原理
- 首先要有一个main()线程
- 在main线程中创建Zookeeper客户端,这时就会创建两个线程,一个负责网络连接通信(connet),一个负责监听(listener)
- 通过connect线程将注册的监听事件发送给Zookeeper
- 在Zookeeper的注册监听器列表中将注册的监听事件添加到列表中
- Zookeeper监听到有数据或路径变化,就会将这个消息发送给listener线程
- listener线程内部调用了process()方法
- 常见的监听
- 监听节点数据的变化:get path [watch]
- 监听子节点增减的变化:ls path [watch]
写数据流程
- Client 向 ZooKeeper 的 Server1 上写数据,发送一个写请求
- 如果Server1不是Leader,那么Server1 会把接受到的请求进一步转发给Leader,因为每个ZooKeeper的Server里面有一个是Leader。这个Leader 会将写请求广播给各个Server,比如Server1和Server2,各个Server写成功后就会通知Leader
- 当Leader收到大多数 Server 数据写成功了,那么就说明数据写成功了。如果这里三个节点的话,只要有两个节点数据写成功了,那么就认为数据写成功了。写成功之后,Leader会告诉Server1数据写成功了
- Server1会进一步通知 Client 数据写成功了,这时就认为整个写操作成功。ZooKeeper 整个写数据流程就是这样的
Zookeeper实战
分布式安装部署
- 集群规划:在hadoop102、hadoop103和hadoop104三个节点上部署Zookeeper
- 解压安装
- 解压Zookeeper安装包到/opt/module/目录下
- 同步/opt/module/zookeeper-3.4.10目录内容到hadoop103、hadoop104:xsync zookeeper-3.4.10/
- 配置服务器编号
- 在/opt/module/zookeeper-3.4.10/这个目录下创建zkData
- 在/opt/module/zookeeper-3.4.10/zkData目录下创建一个myid的文件
- 编辑myid文件,在文件中添加与server对应的编号
- 拷贝配置好的zookeeper到其他机器上,并分别在hadoop102、hadoop103上修改myid文件中内容为3、4
- 配置zoo.cfg文件
- 重命名/opt/module/zookeeper-3.4.10/conf这个目录下的zoo_sample.cfg为zoo.cfg
- 打开zoo.cfg文件,修改数据存储路径配置,dataDir=/opt/module/zookeeper-3.4.10/zkData
- 增加如下配置
#######################cluster########################## server.2=hadoop102:2888:3888 server.3=hadoop103:2888:3888 server.4=hadoop104:2888:3888
- 同步zoo.cfg配置文件:xsync zoo.cfg
- 配置参数解读:Server.A=B:C:D
- A是一个数字,表示这个是第几号服务器
- B是这个服务器的ip地址
- C是这个服务器与集群中的Leader服务器交换信息的端口
- D是万一集群中的Leader服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口
- 集群模式下配置一个文件myid,这个文件在dataDir目录下,这个文件里面有一个数据就是A的值,Zookeeper启动时读取此文件,拿到里面的数据与zoo.cfg里面的配置信息比较从而判断到底是哪个server
- 集群操作
- 在/opt/module/zookeeper-3.4.10/zkData目录下创建一个myid的文件
- 编辑myid文件:vim myid
- 拷贝配置好的zookeeper到其他机器上
- scp -r zookeeper-3.4.10/ root@hadoop103.gc.com:/opt/app/
- scp -r zookeeper-3.4.10/ root@hadoop104.gc.com:/opt/app/
- 并分别修改myid文件中内容为3、4
- 分别启动zookeeper
[gc@hadoop102 zookeeper-3.4.10]$ bin/zkServer.sh start [gc@hadoop103 zookeeper-3.4.10]$ bin/zkServer.sh start [gc@hadoop104 zookeeper-3.4.10]$ bin/zkServer.sh start
客户端命令行操作
- bin/zkCli.sh:启动客户端,例:zkCli.sh -server mini05:2181
- help:显示所有操作命令
- ls path [watch]:查看当前znode中所包含的内容
- ls2 path [watch]:查看当前节点数据并能看到更新次数等数据
- create -s 路径 节点名:普通创建
- -s 含有序列
- -e 临时(重启或者超时消失)
- get path [watch]:获得节点的值
- set:设置节点的具体值
- stat:查看节点状态
- delete:删除节点
- rmr:递归删除节点
- quit:退出当前客户端
- setquota -b 长度配额:设置节点长度配额
- setquota -n 数量配额:设置节点数量配额
- listquoat path:列出配额
- delquota path:删除配额
- zookeeper中的配额管理超出配额的大小之后依然可以进行操作
zookeeper原生的API操作
public class ZookeeperTest {
private ZooKeeper zooKeeper;
/**
* 创建ZooKeeper客户端
*/
@Before
public void init() throws Exception {
String connStr = "mini03:2181,mini04:2181,mini05:2181";
zooKeeper = new ZooKeeper(connStr, 3000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("watch..." + watchedEvent.getType());
}
});
}
/**
* 创建节点
*/
@Test
public void testCreateZNode() throws Exception {
String path = "/test01";
zooKeeper.exists(path, true);
String ret = zooKeeper.create(path, "HELLO2".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(ret);
}
/**
* 获取节点
*/
@Test
public void getChildrenZNode() throws Exception {
List<String> children = zooKeeper.getChildren("/", true);
for (String child : children) {
System.out.println(child);
}
// 延时阻塞
Thread.sleep(Long.MAX_VALUE);
}
/**
* 判断节点
*/
@Test
public void exist() throws Exception {
Stat stat = zkClient.exists("/eclipse", false);
System.out.println(stat == null ? "not exist" : "exist");
}
@Test
public void testSetZnode() throws KeeperException, InterruptedException {
zooKeeper.setData("/test02", "mini02".getBytes(), 1);
}
@Test
public void testGetZnode() throws KeeperException, InterruptedException {
byte[] data = zooKeeper.getData("/test02", true, null);
System.out.println(new String(data, 0, data.length));
}
@Test
public void testDeleteZnode() throws KeeperException, InterruptedException {
zooKeeper.delete("/test02", -1);
}
@After
public void destory() throws Exception {
zooKeeper.close();
}
}
最后
以上就是俭朴滑板为你收集整理的ZooKeeper - 分布式服务管理概述ZooKeeper 内部原理Zookeeper实战的全部内容,希望文章能够帮你解决ZooKeeper - 分布式服务管理概述ZooKeeper 内部原理Zookeeper实战所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复