Zookeeper
保证的是CP。P(Partition Tolerance
分区容错性)是必须要有的,所以只能从C(Consistency一致性)和A(Available可用性)中抉择。鱼和熊掌不可兼得,Zookeeper
选择了C,Zookeeper
可以保证数据是一致的,无法保证服务一直处于可用状态,可能会因为网络原因导致一些请求丢失,服务宕机超过半数等,同时在Leader选举时对外也是不可用的。
-
一个领导者(
Leader
),多个跟随者(Follower
),Leader
负责读写,并向Follower
发起写同步指令,Follower
只负责读,如果Follower
接收到写请求,需要转发给Leader
。 -
集群中只要有半数以上节点存活,
Zookeeper
集群就能正常服务,所以Zookeeper
适合安装奇数台服务器。 -
数据全局强一致性,每个
Server
保存一份相同的数据副本,Client
无论连接到哪个Server
,数据都是一致的。 -
更新请求顺序执行,从
Follower
转发的写请求或者Leader
自己接收的写请求按先后顺序执行。 -
数据更新原子性,一次数据更新要么成功,要么失败。
-
实时性,在一定时间范围内,
Client
能读到最新数据。
3、数据结构
ZooKeeper
的数据结构是类UNIX文件系统目录树结构,其中,每个节点称为ZNode
,每个ZNode
通过其路径(Path)唯一标识,每个ZNode
可以存储少量二进制数据,默认最大为1MB,通常不建议在ZNode
上存储大量的数据,因为在数据量比较大时,数据在多节点同步的性能较低。
ZNode
有四种类型,节点类型不同,生命周期也不同:
-
持久节点(
PERSISTENT
),节点在创建后会一直存在,即使客户端断开连接,zk重启都不会删除该节点,直到有删除操作来主动删除这个节点。持久节点可以持久保存数据,最典型的场景是存储配置信息,集合Watch机制,可以实现配置信息实时生效的特性。 -
临时节点(
EPHEMERAL
),临时节点的生命周期和创建这个节点的客户端会话绑定,也就是说,如果客户端会话失效(客户端宕机或下线),这个节点就被自动删除。临时节点跟随端客户端启动创建和客户端停止而删除,典型场景是感知服务的上下线,判断服务是否存活。 -
时序节点(
SEQUENTIAL
),创建节点时,节点的名称会自动加上一个递增数字后缀,这个数字后缀的范围为整型的最大值。该节点也是持久节点。每个节点都有唯一编号,类似于数据库的自增长主键,典型场景是生成全局唯一的分布式Id。 -
临时时序节点(
EPHEMERAL_SEQUENTIAL
),同时具备临时节点与时序节点的特性,可用于分布式锁的实现。
ZooKeeper
的应用得益于它的节点存储特性和Watch机制。比较典型的几个应用场景,如配置管理,命名服务,服务注册中心,服务上下线感知,集群通信与控制子系统,分布式锁,分布式Id等。
1、配置管理
配置管理利用ZooKeeper
的持久节点存储配置信息,结合Watch机制,实现配置信息实时生效的特性。配置管理在实际生产中叫配置中心,可以统一管理几乎所有的可配置的,动态的配置信息,配置中心的应用也是ZooKeeper
最广泛、最基础的使用场景。
推荐一个基于ZooKeeper
实现的,可直接用于生产的配置中心开源框架Qihoo360/QConf,好用,高性能,支持多种语言SDK。
2、命名服务
命名服务是为系统中的资源提供标识能力。ZooKeeper
的命名服务主要是利用ZooKeeper
节点的树状分层结构(路径唯一)和子节点的顺序维护能力,来为分布式系统中的资源命名。
用到命名服务的典型场景有:分布式API目录JNDI
,分布式ID生成器,分布式节点命名等。
(1)分布式API目录JNDI
著名的Dubbo
分布式框架就是利用ZooKeeper
实现分布式的JNDI
(Java Naming and Directory Interface)功能:
-
服务提供者(Service Provider)在启动的时候,向
ZooKeeper
上的指定节点/dubbo/${serviceName}/providers
写入自己的API地址,这个操作就相当于服务的公开。 -
服务消费者(
Consumer
)启动的时候,订阅节点/dubbo/{serviceName}/providers
下的服务提供者的URL地址,获得所有服务提供者的API。
(2)分布式ID生成器
分布式ID主要用于分布式系统中对数据做全局唯一标识,典型的应用场景是,调用链路追踪。分布式ID的生成方式也很多,UUID
、数据库自增(mysql
,redis
等)、雪花算法等。
ZooKeeper
是个天生的发号器,其时序节点(无论持久和临时),每个节点都会为它的第一级子节点维护一份递增编号,且是分布式全局唯一的。例如,在创建节点时,传入节点前缀/myID/id_
,ZooKeeper
自动会在/myID/id_
后面补充数字顺序,例如/myID/id_00000001
。
根据这个特性,可以简单实现一个分布式Id生成器。比较著名的雪花算法,可以利用ZooKeeper
来动态管理机器Id。
3、服务注册中心
服务注册中心,其实也是命名服务的一种应用,即用ZooKeeper
来存储服务的注册信息。服务的注册信息有:服务名称,服务类型,服务的IP、端口,一个服务可以有多个实例,每个实例为一个节点,节点有状态(实例运行状态),权重、类型(实例角色类型)等。
服务注册中心是服务注册发现架构实现的基础,这个架构中还有两个非常重要的角色:服务提供者和服务消费者。
服务提供者在启动时,将自身的服务信息注册到服务注册中心,服务消费者启动时,会主动从服务注册中心拉取感兴趣的服务信息。ZooKeeper
作为服务注册中心还有一个好处是,可以利用Watch
机制,当服务注册信息更新时,及时通知服务消费者拉取最新数据。
一般服务消费者不会直接连注册中心,而是异步监听注册中心的变化,并将注册信息拉取到本地缓存,这样可以大大降低ZooKeeper
的压力,推荐使用 Qihoo360/QConf 实现注册中心。
4、服务上下线感知
服务上下线感知,主要利用ZooKeeper
的临时节点实现。应服务在启动时会将自己的IP地址作为临时节点创建在某个节点(如/Cluster
)下,当服务因为某些原因如断网或者宕机,使得它与ZooKeeper
的会话过期时,这个临时节点就会被删除,可以通过这个特性来感知服务的集群有哪些机器可用了。
同时,可以对这些临时节点做监听,感知服务的上下线,并作出相应反应。
5、集群通信与控制子系统
利用ZooKeeper
的Watch机制,可以很容易实现集群间的高效通信和控制子系统。
-
在
ZooKeeper
里规划一个用于存放控制命令和应答的·路径(如/Commands
); -
集群中的所有节点在启动后都监听此路径(
/Commands
); -
命令行程序(CLI)发给集群节点的命令及参数被包装成一个
ZNode
节点(如上图中的command1
),写入/Commands
路径下,同时在command1
上监听事件。 -
集群中的所有节点都通过
/Commands
上的Watch事件收到此命令,然后开始执行command1
命令对应的逻辑; -
在某个节点执行完成后就在
command1
路径下新建一个ZNode
节点(如node1result
)作为应答。 -
由于CLI之前在
command1
上监听,所以很快就被通知此命令已经有节点执行完成,CLI就可以实时输出结果,在所有节点的应答都返回后(或者等待超时),命令行结束。
6、分布式锁
使用ZooKeeper
实现分布式锁的原理是利用时序节点和Watch机制:
(1)一个ZNode
节点代表一把锁,如果锁对应的ZNode
节点不存在,那么先创建ZNode
节点。需要使用分布式锁功能先申请一个业务路径,如/lock/app1
(不同服务和不同业务隔离),如果不区分锁的业务路径,也需要区分锁的前缀。
(2)抢占锁的所有客户端,使用锁的ZNode
节点的子节点列表来表示;如果某个客户端需要占用锁,则在/lock/app1
下创建一个临时有序的子节点。
(3)如何判断客户端是否占有锁?客户端创建子节点后,需要判断自己创建的子节点是否为当前子节点列表中序号最小的子节点。如果是,则认为加锁成功;如果不是,则监听前一个ZNode
子节点的变更消息,等待前一个节点释放锁。
(4)一旦队列中后面的节点获得前一个子节点的变更通知,则开始进行判断,判断自己是否为当前子节点列表中序号最小的子节点,如果是,则认为加锁成功;如果不是,则持续监听,一直到获得锁。
(5)获取锁后,开始处理业务流程。在完成业务流程后,删除对应的子节点,完成释放锁的工作,以便后面的节点监听到节点的变更通知,获得分布式锁。
ZooKeeper
实现的分布式锁是公平的,且一般也会是可重入的。流程上非常类似AQS的实现,也是CLH的变体,只不过释放锁后节点通知不是本地,而是利用ZooKeeper
的Watch机制远程通知。
为了后续的源码学习,体验到最新的功能,这里下载最新版本的ZooKeeper
(至今最新3.7.0),实际生产中可以安装历史版本。
(安装ZK前,请先安装JDK)
1、下载与安装
复制下载链接,到Centos
中下载安装。
cd /usr/local/
mkdir zookeeper
cd zookeeper
下载
wget https://dlcdn.apache.org/zookeeper/zookeeper-3.7.0/apache-zookeeper-3.7.0-bin.tar.gz
解压在当前目录
tar -zxvf apache-zookeeper-3.7.0-bin.tar.gz
下载的zk安装,解压之后就可以用了:
修改解压之后的目录名称(也可以不修改)
mv apache-zookeeper-3.7.0-bin apache-zookeeper-3.7.0
cd apache-zookeeper-3.7.0
data/zoo用于存储zk数据
mkdir -p data/zoo
修改zoo_sample.cfg 为zoo.cfg,因为bin/zkServer.sh默认用的是zoo.cfg
mv conf/zoo_sample.cfg conf/zoo.cfg
修改zoo.cfg 中的dataDir为/usr/local/zookeeper/apache-zookeeper-3.7.0/data/zoo
不能用/tmp 存储zk数据,因为机器会定时清理/tmp目录下的数据
vim conf/zoo.cfg
启动,默认使用的conf/zoo.cfg启动,单机版。
bin/zkServer.sh start
直接运行bin/zkServer.sh
,可以看到该命令的使用说明,常用的有start
,status
,restart
,stop
,还可以在启动时指定配置文件,如:
指定配置文件启动,配置文件路径必须放在后面
bin/zkServer.sh start conf/zoo.cfg
2、配置说明
ZooKeeper
的启动配置文件zoo.cfg
的配置内容说明:
The number of milliseconds of each tick
通信心跳时间,Leader和Follower或者Zookeeper服务器与客户端心跳时间,单位毫秒
tickTime=2000
The number of ticks that the initial
synchronization phase can take
Leader和Follower初始连接时能容忍的最多心跳数(tickTime的数量)
initLimit=10
The number of ticks that can pass between
sending a request and getting an acknowledgement
Leader和Follower之间通信时间如果超过syncLimit * tickTime,
Leader认为Follwer死掉,从服务器列表中删除Follwer。
syncLimit=5
the directory where the snapshot is stored.
do not use /tmp for storage, /tmp here is just
example sakes.
保存zk数据的目录,默认是/tmp,一般要修改为其他路径,因为Linux系统会定期请求/tmp下的数据
dataDir=/usr/local/zookeeper/apache-zookeeper-3.7.0/data/zoo
the port at which the clients will connect
客户端连接的端口
clientPort=2181
the maximum number of client connections.
increase this if you need to handle more clients
最大客户端连接数,默认60
#maxClientCnxns=60
The number of snapshots to retain in dataDir
dataDir中保留的快照数量
#autopurge.snapRetainCount=3
Purge task interval in hours
Set to “0” to disable auto purge feature
自动清除任务运行时间间隔,单位为小时,
若设置为 0 则禁用自动清除功能
#autopurge.purgeInterval=1
zk 指标监控提供者,官方推荐用prometheus普罗米修斯
Metrics Providers
https://prometheus.io Metrics Exporter
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true
zoo.cfg
配置内容最后面是ZooKeeper
指标数据监控配置,官方推荐用prometheus
,如果用生产中用influxdb
存储zk指标数据,本人推荐用Telegraf。
ZooKeeper
集群中只要有半数以上节点存活,就能正常对外提供服务,所以Zookeeper
集群一般运行奇数个服务端。由于学习用的Linux只有一台,只能搭建一个伪集群,不过理论上都是一样的。
1、伪集群
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
Java架构进阶面试及知识点文档笔记
这份文档共498页,其中包括Java集合,并发编程,JVM,Dubbo,Redis,Spring全家桶,MySQL,Kafka等面试解析及知识点整理
Java分布式高级面试问题解析文档
其中都是包括分布式的面试问题解析,内容有分布式消息队列,Redis缓存,分库分表,微服务架构,分布式高可用,读写分离等等!
互联网Java程序员面试必备问题解析及文档学习笔记
Java架构进阶视频解析合集
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
Java架构进阶面试及知识点文档笔记
这份文档共498页,其中包括Java集合,并发编程,JVM,Dubbo,Redis,Spring全家桶,MySQL,Kafka等面试解析及知识点整理
[外链图片转存中…(img-mEkfIJ4N-1711894378482)]
Java分布式高级面试问题解析文档
其中都是包括分布式的面试问题解析,内容有分布式消息队列,Redis缓存,分库分表,微服务架构,分布式高可用,读写分离等等!
[外链图片转存中…(img-Cu7mRNRb-1711894378482)]
互联网Java程序员面试必备问题解析及文档学习笔记
[外链图片转存中…(img-N4u3ADqB-1711894378483)]
Java架构进阶视频解析合集
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!