zookeeper
ZooKeeper 本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。
ZooKeeper 适用于存储和协同相关的关键数据,不适合用于大数据量存储。
是一个分布式的小文件管理系统,管理分布式服务(Web Service)
zookeeper的发展历程
ZooKeeper 最早起源于雅虎研究院的一个研究小组。当时研究人员发现,在雅虎内部很多大型系统基本都需要依赖一个系统来进行分布式协同,但是这些系统往往都存在分布式单点问题。
所以,雅虎的开发人员就开发了一个通用的无单点问题的分布式协调框架,这就是ZooKeeper。ZooKeeper之后在开源界被大量使用,很多著名开源项目都在使用zookeeper,例如:
- Hadoop:使用ZooKeeper 做Namenode 的高可用。
- HBase:保证集群中只有一个master,保存hbase:meta表的位置,保存集群中的RegionServer列表。
- Kafka:集群成员管理,controller 节点选举。
什么是分布式
1、集中式系统
集中式系统,集中式系统中整个项目就是一个独立的应用,整个应用也就是整个项目,所有的东西都在一个应用里面。部署到一个服务器上。
布署项目时,放到一个tomcat里的。也称为单体架构
2、分布式系统
分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。
其目的是利用更多的机器,处理更多的数据
我们可以把大项目按功能划分为很多的模块,比如说单独一个系统处理订单,一个处理用户登录,一个处理后台等等,然后每一个模块都单独在一个tomcat中跑,合起来就是一个完整的大项目,这样每一个tomcat都非常轻松。
一、zookeeper的应用场景
1、 注册中心
解决服务的单点问题
分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构。通过调用Zookeeper提供的创建节点的API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。
阿里巴巴集团开源的分布式服务框架Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表。
2、配置中心
解决配置麻烦问题,统一管理配置
数据发布/订阅即所谓的配置中心:发布者将数据发布到ZooKeeper一系列节点上面,订阅者进行数据订阅,当数据有变化时,可以及时得到数据的变化通知,达到动态获取数据的目的。
ZooKeeper 采用的是推拉结合的方式。
1、推: 服务端会推给注册了监控节点的客户端 Wathcer 事件通知
2、拉: 客户端获得通知后,然后主动到服务端拉取最新的数据
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
4、分布式队列
解决惊群效应
在传统的单进程编程中,我们使用队列来存储一些数据结构,用来在多线程之间共享或传递数据。分布式环境下,我们同样需要一个类似单进程队列的组件,用来实现跨进程、跨主机、跨网络的数据共享和数据传递,这就是我们的分布式队列。
RabbitMQ
5、负载均衡
通过负载均衡算法,用来把对某种资源的访问分摊给不同的设备,从而减轻单点的压力。
让分布式中服务被调用的次数相对就均衡
- 随机
- 轮循
- 最小活跃数
- 一致性哈希
二、zookeeper基本操作
1、zookeeper数据结构
ZooKeeper 的数据模型是层次模型。层次模型常见于文件系统。
层次模型和key-value 模型是两种主流的数据模型。ZooKeeper 使用文件系统模型主要基于以下两点考虑:
-
文件系统的树形结构便于表达数据之间的层次关系。
-
文件系统的树形结构便于为不同的应用分配独立的命名空间(namespace 路径url 唯一)。
ZooKeeper 的层次模型称作data tree。Datatree 的每个节点叫作znode(Zookeeper node)。不同于文件系统,每个节点都可以保存数据。每个节点都有一个版本(version)。版本从0 开始计数。
如图所示,data tree中有两个子树,用于应用1( /app1)和应用2(/app2)。
每个客户端进程pi 创建一个znode节点 p_i 在 /app1下, /app1/p_1就代表一个客户端在运行。
2、节点的分类
2.1 一个znode可以是持久性的,也可以是临时性的
-
持久性znode[PERSISTENT],这个znode一旦创建不会丢失,无论是zookeeper宕机,还是client宕机。(默认)
-
临时性的znode[EPHEMERAL],如果zookeeper宕机了,或者client在指定的timeout时间内没有连接server或者client断开(close命令),都会被认为丢失。 -e
2.2 znode也可以是顺序性的,每一个顺序性的znode关联一个唯一的单调递增整数。这个单调递增整数是znode名字的后缀。
-
持久顺序性的znode(PERSISTENT_SEQUENTIAL):znode 处理具备持久性的znode的特点之外,znode的名称具备顺序性。 -s
-
临时顺序性的znode(EPHEMERAL_SEQUENTIAL):znode处理具备临时性的znode特点,znode的名称具备顺序性。-s
3、安装zookeeper
下载地址:http://zookeeper.apache.org
3.1 解压后修改配置文件
conf路径中复制一份zoo_sample.cfg,改名为 zoo.cfg
在zookeeper路径下创建一个data目录及log目录
指定保存数据的目录:data目录和log存储日志
3、客户端命令
3.1 查询所有命令 help
3.2 查询根路径下的节点
ls /
3.3 创建普通永久节点
#create [-s] [-e] path data acl path以/开始,也就是绝对路径
create /app1 "helloworld"
3.5 创建带序号永久节点
create -s /hello "helloworld"
3.5 创建普通临时节点
create -e /app3 'app3'
-e:表示普通临时节点
3.6 创建带序号临时节点
create -e -s /app4 'app4'
3.7 查询节点数据
# get path [watch]
get /app1
# 节点的状态信息,也称为stat结构体
# 创建该znode的事务的zxid(ZooKeeper Transaction ID)
# 事务ID是ZooKeeper为每次更新操作/事务操作分配一个全局唯一的id,表示zxid,值越小,表示越先执行
cZxid = 0x4454 # 0x0表示十六进制数0
ctime = Thu Jan 01 08:00:00 CST 1970 # 表示从1970-01-01T00:00:00Z开始以毫秒为单位的znode创建时间。
mZxid = 0x4454 # 最后一次更新的zxid
mtime = Thu Jan 01 08:00:00 CST 1970 # 最后一次更新的时间
pZxid = 0x4454 # 数据节点的子节点列表最后一次被修改(是子节点列表变更,而不是子节点内容变更)时的事务ID
cversion = 5 # 子节点的变化号,表示子节点被修改的次数
dataVersion = 0 # 表示当前节点的数据变化号,0表示当前节点从未被修改过
aclVersion = 0 # 访问控制列表的变化号 access control list
# 如果临时节点,表示当前节点的拥有者的sessionId
ephemeralOwner = 0x0 # 如果不是临时节点,则值为0
dataLength = 13 # 数据长度
numChildren = 1 # 子节点的数量
3.8 修改节点数据
#set path data [version]
set /app1 'hello'
3.9 删除节点
# delete path [version]
delete /hello
3.10 递归删除节点
#rmr path
rmr /hello
3.11 查看节点状态
#stat path [watch]
stat /zookeeper
3.12 日志的可视化
日志都是以二进制文件存储的,使用记事本打开,无意义
为了能正常查看日志,把查看日志需要的jar包放到日志文件的目录下
zookeeper-3.4.13.jar 在zookeeper目录
slf4j-api-1.7.25.jar在lib目录
cmd进入日志文件的目录,执行以下命令可以直接查看正常日志
java -classpath ".;*" org.apache.zookeeper.server.LogFormatter log.1
三、zookeeper 的java Api
常用api
- 原生Java API(不推荐使用)
ZooKeeper 原生Java API位于org.apache.ZooKeeper包中
ZooKeeper-3.x.x. Jar (这里有多个版本)为官方提供的 java API
- Apache Curator(推荐使用)
Apache Curator是 Apache ZooKeeper的Java客户端库。
Curator.项目的目标是简化ZooKeeper客户端的使用。
另外 Curator为常见的分布式协同服务提供了高质量的实现。
Apache Curator最初是Netflix研发的,后来捐献了 Apache基金会,目前是 Apache的顶级项目
- ZkClient(不推荐使用)
Github上一个开源的ZooKeeper客户端,由datameer的工程师Stefan Groschupf和Peter Voss一起开发。 zkclient-x.x.Jar也是在源生 api 基础之上进行扩展的开源 JAVA 客户端。
导入依赖
<!--zookeeper的依赖-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.7</version>
</dependency>
<!-- zookeeper CuratorFramework 是Netflix公司开发一款连接zookeeper服务的框架,通过封装的一套高级API 简化了ZooKeeper的操作,提供了比较全面的功能,除了基础的节点的操作,节点的监听,还有集群的连接以及重试。-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
</dependency>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
java 实现
/**
* RetryPolicy: 失败的重试策略的公共接口
* ExponentialBackoffRetry是 公共接口的其中一个实现类
* 参数1 baseSleepTimeMs: 初始化sleep的时间,失败时,睡眠多长时间后再重试
* 参数2 maxRetries:最大重试次数
参数3 maxSleepMs(可以省略):最大sleep时间,如果上述的当前sleep计算出来比这个大,那么sleep用这个时间
*/
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3,10);
//创建客户端
/**
* 参数1:连接的ip地址和端口号
* 参数2:会话超时时间,单位毫秒 (连接上后,长时间不操作)
* 参数3:连接超时时间,单位毫秒 客户端连接服务器,一直连不上,超过时间后就放弃连接
* 参数4:失败重试策略
*/
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181",3000,1000,retryPolicy);
//开启客户端(会阻塞到会话连接成功为止)
client.start();
/**
* 创建节点
*/
//1. 创建一个空节点(a)(只能创建一层节点)值为ip地址
// client.create().forPath("/a");
//2. 创建一个有内容的b节点(只能创建一层节点)"这是b节点的内容".getBytes() 原始数据没有丢,当再次转utf-8时,能显示出中文
// client.create().forPath("/b", "这是b节点的内容".getBytes());
//3. 创建多层节点
// (creatingParentsIfNeeded)是否需要递归创建节点
// withMode(CreateMode.PERSISTENT) 创建持久性 b节点
// client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/abc/g");
//4. 创建带有的序号的节点
// client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/e");
//5. 创建临时节点(客户端关闭,节点消失),设置延时5秒关闭
// client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/f");
//6. 创建临时带序号节点(客户端关闭,节点消失),设置延时5秒关闭
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/f");
Thread.sleep(5000);
//关闭客户端
client.close();
修改节点数据
//修改节点
client.setData().forPath("/a/b", "abc".getBytes());
查询节点数据
// 查询节点数据
byte[] bytes = client.getData().forPath("/a/b");
System.out.println(new String(bytes));
删除节点
//删除一个子节点
client.delete().forPath("/a");
// 删除节点并递归删除其子节点
client.delete().deletingChildrenIfNeeded().forPath("/a");
//强制保证删除一个节点
//只要客户端会话有效,那么Curator会在后台持续进行删除操作,直到节点删除成功。
// 比如遇到一些网络异常的情况,此guaranteed的强制删除就会很有效果。
client.delete().guaranteed().deletingChildrenIfNeeded().forPath("/a");