ZooKeeper
Zookeeper简介
-
概述
zookeeper是一个开源的分布式的,为分布式应用提供协调服务地Aoache项目。
zookeeper是基于观察者模式的分布式服务管理框架。
它负责存储和管理大家都关心的数据,然后接受观察者的注册。一旦这些数据发生变化,zk就负责通知所有已经在zk上注册的那些观察者做出的响应的反应。
zookeeper = 文件系统 + 通知系统
-
特点
- zk是由一个leader和多个follower组成的集群
- 集群中只要有半数以上的节点存活,zk集群就能正常服务
- 全局数据一致,每个server保存一份相同的数据副本,client不论连接那个额server,得到的数据都是一样的
- 更新请求一致:来自同一个client的更新请求按其发送的顺序依次执行
- 数据更新原子性
- 实时性:在一定时间范围内,client能读取到最新的数据
-
数据结构
zookeeper的数据模型与Unix文件系统很类似,整体上可以看作一棵树,每个节点称作一个znode。每个znode默认能够存储1m的数据,每个znode都可以i通过其路径唯一标识。
-
应用场景
提供诸如统一命名、统一配置、统一集群管理、服务器动态上下线、软负载均衡
-
统一命名服务:在分布式环境下对应用、服务等统一命名、便于识别。
例如:ip不好记忆而域名容易记住
-
统一配置管理:一般一个集群中的配置文件要求所有节点一致,如Kafka的集群。这个配置管理就可以由zookeeper实现
-
统一集群管理:在分布式中,实时掌握每个节点状态是必要的,zk可以实现实时监控节点状态变化
-
服务器动态上下线:客户端能实时洞察到服务器的上下线的变化
-
软负载均衡:zk中记录了Metairie服务器的访问数,让访问数最少的服务器处理最新的客户端请求
- 注意:软负载均衡指通过软件代码实现负载均衡,对应的,硬负载均衡对应着通过对硬件的调整是西安负载均衡
-
-
下载官网:https://zookeeper.apache.org/
zookeeper的安装
考虑到zk的安装基本是机械操作且网上教材甚多,此处略过不表
-
在配置文件zoo.cfg的时候有配置到
server.A=B:C:D
- 其中A是一个数字,表示这个是第几号服务器
- B是这个服务器的地址
- C是这个服务器Follower与集群中的Leader服务器交换信息的端口
- D是万一集群中的Leader服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口
-
集群启动
zk需要单点分别启动:
bin/zkServer.sh start
类似的通过如下指令查看状态
bin/zkServer.sh status
zookeeper实战练习
客户端命令行操作
命令基本语法 | 功能描述 |
---|---|
help | 显示所有操作命令 |
ls path [watch] | 使用 ls 命令来查看当前znode中所包含的内容 |
ls2 path [watch] | 查看当前节点数据并能看到更新次数等数据 |
create | 普通创建 -s 含有序列 -e 临时(重启或者超时消失) |
get path [watch] | 获得节点的值 |
set | 设置节点的具体值 |
stat | 查看节点状态 |
delete | 删除节点 |
rmr | 递归删除节点 *rm -r |
API应用
-
环境的搭建
-
pom文件依赖:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>RELEASE</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.8.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper --> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.10</version> </dependency> </dependencies>
-
在ersouces目录下创建log4j.properties文件,文件中写入:
log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n log4j.appender.logfile=org.apache.log4j.FileAppender log4j.appender.logfile.File=target/spring.log log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
-
-
通过API对znode的增删改查功能
- 略
-
模拟监听服务器节点的动态上下线
-
服务器
public class DistributedServer { //zookeeper的服务器访问路径 private String path = "hadoop201:2181,hadoop202:2181,hadoop203:2181"; //连接超时时间 private int sessionTimeout = 2000; //由zookeeper提供的连接类 private ZooKeeper zkCli; //这里将/servers作为znode的父节点, //所有服务器上线都在父节点下注册一个临时有序的子节点 private String parentNode = "/servers"; /*** * 获取连接 */ public void getConnect() throws Exception{ zkCli = new ZooKeeper(path, sessionTimeout, new Watcher() { public void process(WatchedEvent watchedEvent) {} }); } /*** * 服务器注册方法,为对应的服务器在父节点中注册一个znode * @param info 此条为创建节点是用到的数据,一般为服务器的hostname */ public void register(String info) throws KeeperException, InterruptedException { String serverNode = zkCli.create( parentNode + "/server", info.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); System.out.println(info+" is online: "+serverNode); } /*** * 模拟服务器的业务方法 * 负责将服务器进程进行死循环阻塞,防止服务器进程关闭 */ public void business() throws Exception{ System.out.println("server start working ..."); while(true){ Thread.sleep(5000); System.out.println("keep alive ..."); } } /*** * 通过建立服务器进程摸拟服务器的上下线 * @param args * @throws Exception */ public static void main(String[] args) throws Exception{ //创建一个服务器对象 DistributedServer ds = new DistributedServer(); //获取zk连接 ds.getConnect(); //注册服务器 ds.register("hs101:login"); //服务器开始业务流程(阻塞模拟) ds.business(); } }
-
客户端
public class Client { //四个属性同服务器属性 private String path = "hadoop201:2181,hadoop202:2181,hadoop203:2181"; private int sessionTimeout = 2000; private ZooKeeper zkCli; private String parentNode = "/servers"; /*** * 同服务器,获取连接 */ public void getConnect() throws Exception{ zkCli = new ZooKeeper(path, sessionTimeout, new Watcher() { public void process(WatchedEvent watchedEvent) {} }); } /*** * 检测父节点“/servers”是否存在,如果不存在,则创建之 */ public void checkExist() throws Exception{ Stat exists = zkCli.exists("/servers", false); if (exists == null){ zkCli.create("/servers","servers".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } /*** * 获取服务器端数据,创建监听器 */ public void getServers() throws Exception{ //捕获父节点下子节点的变动情况,并将返回值存入list中 List<String> children = zkCli.getChildren( "/servers", new Watcher() { public void process(WatchedEvent watchedEvent) { System.out.println("servers changed."); try { //递归调用本方法在创建一个监听器保持对zk中 //“/servers”端口下znode变化的持续监听 getServers(); } catch (Exception e) { e.printStackTrace(); } } }); //新建list用于分析children对应的znode中存储的数据 List<String> result = new ArrayList<String>(); for (String child : children) { //通过zkclient的gerData方法获取每一个子节点的数据 byte[] info = zkCli.getData( parentNode + "/" + child, false, null); result.add(new String(info)); } //输出 System.out.println("最新的信息是:"+result); } //模拟业务造成线程阻塞,防止线程关闭 public void business() throws Exception{ while (true){ Thread.sleep(5000); System.out.println("keep alive ..."); } } public static void main(String[] args) throws Exception{ //新建客户端对象 Client client = new Client(); //与zk取得连接 client.getConnect(); //检查/servers父节点是否存在 client.checkExist(); //建立监听器,获取每一次/servers下每一次znode的变化情况 client.getServers(); //客户端业务 client.business(); } }
-