1.why zookeeper
现在各个服务大部分都是集群。多个节点一起工作。就是传说中的分布式。
多个节点工作肯定不会个一个节点工作一模一样。需要来进行数据的同步等。高并发,就需要上锁。这里是分布式锁。不是单台机器的锁。
zookeeper 就是为了解决这些问题的。
zookeeper 是分布式协调服务。
登录官网看咋说的
准备用谷歌在线翻译。发现用不了了。。呵呵
使用微信翻译
可以维护配置信息,比如kafka的配置信息?
命名?这个不懂。
分布式同步。就是多个节点数据同步。
提供服务。
zookeeper就是把我们业务需要做的公共的东西做了。咱只需要关系咱自己的业务。出现bug。一般是咱自己导致的。不是zookeeper。
2.zookeeper应用场景
学习kafka的时候,前提就是zookeeper.
2.1、分布式协调组件
协调分布式中系统的状态。
2.2、分布式锁
比如俩个主机A和B 提供一样的服务,同一个数据D,有人请求肯定在其中一个主机上,如果修改了主机A的数据D。 那么B上的D也应该被修改。这就是zookeeper做的事,使用分布式锁,保证了数据的一致性。强一致性 或 最终一致性。
2.3、无状态化的实现
现在流行无状态化,就是服务器只负责处理数据,而不保存数据,每个节点都一样。这样扩缩容就很简单。数据肯定 是去其他地方保存了。那么就是zookeeper.但是zookeeper不能当做数据库一样来保存数据。
3.安装zookeeper
就是到官方下载包。然后解压就完事了。一般安装在 /usr/local
zookeeper的目录
- bin 就是一些可以执行的命令。比如启动/停止zk. 客户端连接zk等。
- conf 配置文件的地方,每个软件一般都叫这个玩意。一些配置数据,启动数据就在这里
zk的配置文件一般是zoo.cfg
zoo_sample.cfg是示例
- logs
默认保存数据、日志的路径
4.使用zookeeper.单节点
4.1 启动前需要先编辑配置文件
zoo.cfg配置如下
tickTime=2000 # zk最小时间单位。2s dataDir=/var/lib/zookeeper # zk保存数据的路径,如果没有logdatadir,log也在这个路径 clientPort=2181 # 客户端连接zk的端口
4.2 启动zk
在 bin目录下
./zkServer.sh start
还可以指定配置文件,默认就是config下的zoo.cfg
PS:使用zk,先要安装好jdk. 毕竟是apache的。
4.3 查看zk状态
./zkServer.sh status
Mode: standalone
4.4 停止服务器
./zkServer.sh stop
5.使用zkCli 客户端 来连接zk服务端,来学习一些基本命令
5.1 使用zkCli连接命令
./zkCli.sh
5.2 输入help就会有命令提示帮助,告诉我们有哪些命令可以执行
[zk: localhost:2181(CONNECTED) 0] help ZooKeeper -server host:port -client-configuration properties-file cmd args addWatch [-m mode] path # optional mode is one of [PERSISTENT, PERSISTENT_RECURSIVE] - default is PERSISTENT_RECURSIVE addauth scheme auth close config [-c] [-w] [-s] connect host:port create [-s] [-e] [-c] [-t ttl] path [data] [acl] delete [-v version] path deleteall path [-b batch size] delquota [-n|-b|-N|-B] path get [-s] [-w] path getAcl [-s] path getAllChildrenNumber path getEphemerals path history listquota path ls [-s] [-w] [-R] path printwatches on|off quit reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*] redo cmdno removewatches path [-c|-d|-a] [-l] set [-s] [-v version] path data setAcl [-s] [-v version] [-R] path acl setquota -n|-b|-N|-B val path stat [-w] path sync path version whoami
5.3、版本、获取节点信息
[zk: localhost:2181(CONNECTED) 1] version ZooKeeper CLI version: 3.8.0-5a02a05eddb59aee6ac762f7ea82e92a68eb9c0f, built on 2022-02-25 08:49 UTC [zk: localhost:2181(CONNECTED) 2] whoami Auth scheme: User ip: 0:0:0:0:0:0:0:1 [zk: localhost:2181(CONNECTED) 4] get / [zk: localhost:2181(CONNECTED) 5]
- zk保存数据的格式
zk保存数据的格式 是 类似目录的。 树结构。
基于节点来保存的。叫znode.
根路径 /
根路径下test节点 /test
test 节点下还可以有 /test/test_sub
5.4、创建节点
zk有多种类型的节点.对应不同的命令
create [-s] [-e] [-c] [-t ttl] path [data] [acl]
5.4.1 持久节点
创建出来的节点。会话结束后还存在
[zk: localhost:2181(CONNECTED) 5] create /test1 ll Created /test1
5.4.2 持久序号节点
根据先后顺序。在节点之后带上一个数值,数值递增。 应用于分布式场景。 来区分不同的并发,时间先后。
[zk: localhost:2181(CONNECTED) 6] create -s /test2 ll2 Created /test20000000016 [zk: localhost:2181(CONNECTED) 7] create -s /test2 ll22 Created /test20000000017 [zk: localhost:2181(CONNECTED) 9] create /test1 ll2 Node already exists: /test1
名称后面自动加一个数值。
序号节点创建同名的不会报错。
创建同名的非序号节点,就会说已经存在
5.4.3 临时节点
会话结束,该节点就没了
[zk: localhost:2181(CONNECTED) 8] create -e /test3 ll3 Created /test3
5.4.4 临时序号节点
用于临时的分布式锁场景。
[zk: localhost:2181(CONNECTED) 10] create -s -e /test4 ll4 Created /test40000000019 [zk: localhost:2181(CONNECTED) 11] create -s -e /test4 ll42 Created /test40000000020
5.5 查看节点信息
5.5.1 首先查看有哪些节点
ls [-s] [-w] [-R] path
[zk: localhost:2181(CONNECTED) 17] ls / [admin, brokers, cluster, config, consumers, controller_epoch, feature, isr_change_notification, latest_producer_id_block, log_dir_event_notification, test1, test20000000016, test20000000017, test3, test40000000019, test40000000020, zookeeper]
默认只能查看当前路径下的节点。
- -R : 一般是递归的意思,可以查看子路径下的节点信息
[zk: localhost:2181(CONNECTED) 18] ls -R / / /admin /brokers /cluster /config /consumers /controller_epoch /feature /isr_change_notification /latest_producer_id_block /log_dir_event_notification /test1 /test20000000016 /test20000000017 /test3 /test40000000019 /test40000000020 /zookeeper /admin/delete_topics /brokers/ids /brokers/seqid /brokers/topics /brokers/topics/test /brokers/topics/test/partitions /brokers/topics/test/partitions/0 /brokers/topics/test/partitions/0/state /cluster/id /config/brokers /config/changes /config/clients /config/ips /config/topics /config/users /config/topics/test /consumers/console-consumer-20678 /consumers/console-consumer-20678/offsets /consumers/console-consumer-20678/offsets/test /consumers/console-consumer-20678/offsets/test/0 /zookeeper/config /zookeeper/quota
5.5.2 查看具体某个节点的信息
get [-s] [-w] path
- 查看当前节点保存的数据
[zk: localhost:2181(CONNECTED) 19] get /test1 ll
- 查看当前节点的元数据
[zk: localhost:2181(CONNECTED) 20] get -s /test1 ll # 数据 cZxid = 0xa1 # 创建事务ID ctime = Sun Jan 01 20:36:36 HKT 2023 # 创建时间 mZxid = 0xa1 # 修改事务ID mtime = Sun Jan 01 20:36:36 HKT 2023 # 修改时间 pZxid = 0xa1 # 添加/删除子节点的事务ID cversion = 0 # dataVersion = 0 # 节点数据版本,每修改一次,就递增1 aclVersion = 0 # 此节点的权限版本 ephemeralOwner = 0x0 # 如果当前节点是临时节点,是当前节点所有者的session id。如果节点不是临时节点,则该值为零 dataLength = 2 # 节点内数据的长度 numChildren = 0 # 节点的子节点个数
table键会有提示
[zk: localhost:2181(CONNECTED) 22] get -s /test2000000001 test20000000016 test20000000017 [zk: localhost:2181(CONNECTED) 22] get -s /test20000000016 ll2 cZxid = 0xa2 ctime = Sun Jan 01 20:45:13 HKT 2023 mZxid = 0xa2 mtime = Sun Jan 01 20:45:13 HKT 2023 pZxid = 0xa2 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 3 numChildren = 0 [zk: localhost:2181(CONNECTED) 23] get -s /test3 ll3 cZxid = 0xa4 ctime = Sun Jan 01 20:47:47 HKT 2023 mZxid = 0xa4 mtime = Sun Jan 01 20:47:47 HKT 2023 pZxid = 0xa4 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x10002ee9e300000 dataLength = 3 numChildren = 0 [zk: localhost:2181(CONNECTED) 24] get -s /test400000000 test40000000019 test40000000020 [zk: localhost:2181(CONNECTED) 24] get -s /test40000000019 ll4 cZxid = 0xa6 ctime = Sun Jan 01 20:50:04 HKT 2023 mZxid = 0xa6 mtime = Sun Jan 01 20:50:04 HKT 2023 pZxid = 0xa6 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x10002ee9e300000 dataLength = 3 numChildren = 0
5.6 权限设置
5.6.1 注册当前会话的账号和密码
addauth scheme auth
- 节点的权限
c: create,创建
w: write,更新
d: delete,删除
a: admin,管理者,进行acl权限设置
r: read,读
[zk: localhost:2181(CONNECTED) 27] addauth digest lzl:123 [zk: localhost:2181(CONNECTED) 28]
lzl 是用户 123 是密码
[zk: localhost:2181(CONNECTED) 28] [zk: localhost:2181(CONNECTED) 28] create /test5 ll5 auth:lzl:123:cdw Created /test5 [zk: localhost:2181(CONNECTED) 29] get /test5 Insufficient permission : /test5 [zk: localhost:2181(CONNECTED) 30] getAcl -s /test5 Insufficient permission : /test5 [zk: localhost:2181(CONNECTED) 31] whoami Auth scheme: User digest: lzl ip: 0:0:0:0:0:0:0:1 [zk: localhost:2181(CONNECTED) 35] create /test6 ll6 auth:lzl:123:cdwra Created /test6 [zk: localhost:2181(CONNECTED) 36] get /test6 ll6 [zk: localhost:2181(CONNECTED) 38] getAcl /test6 'digest,'lzl:ctz2pGX7T978GzU15rvOzb8mJeM= : cdrwa
5.7 删除节点
delete [-v version] path
- 删除持久节点
[zk: localhost:2181(CONNECTED) 40] delete /test1 [zk: localhost:2181(CONNECTED) 41] ls ls [-s] [-w] [-R] path [zk: localhost:2181(CONNECTED) 42] ls / [admin, brokers, cluster, config, consumers, controller_epoch, feature, isr_change_notification, latest_producer_id_block, log_dir_event_notification, test20000000016, test20000000017, test3, test40000000019, test40000000020, test5, test6, zookeeper]
- 删除持久序号节点
[zk: localhost:2181(CONNECTED) 43] delete /test20000000016 [zk: localhost:2181(CONNECTED) 45] ls / [admin, brokers, cluster, config, consumers, controller_epoch, feature, isr_change_notification, latest_producer_id_block, log_dir_event_notification, test20000000017, test3, test40000000019, test40000000020, test5, test6, zookeeper]
- 更新节点数据
[zk: localhost:2181(CONNECTED) 46] get /test3 ll3 [zk: localhost:2181(CONNECTED) 47] get -s /test3 ll3 cZxid = 0xa4 ctime = Sun Jan 01 20:47:47 HKT 2023 mZxid = 0xa4 mtime = Sun Jan 01 20:47:47 HKT 2023 pZxid = 0xa4 cversion = 0 dataVersion = 0 # 数据版本0 aclVersion = 0 ephemeralOwner = 0x10002ee9e300000 dataLength = 3 numChildren = 0 [zk: localhost:2181(CONNECTED) 48] set -e /test3 ll333 org.apache.commons.cli.UnrecognizedOptionException: Unrecognized option: -e [zk: localhost:2181(CONNECTED) 49] set /test3 ll333 [zk: localhost:2181(CONNECTED) 50] [zk: localhost:2181(CONNECTED) 50] get /test3 ll333 [zk: localhost:2181(CONNECTED) 51] get -s /test3 ll333 cZxid = 0xa4 ctime = Sun Jan 01 20:47:47 HKT 2023 mZxid = 0xae mtime = Sun Jan 01 21:22:07 HKT 2023 pZxid = 0xa4 cversion = 0 dataVersion = 1 # 数据版本加1 aclVersion = 0 ephemeralOwner = 0x10002ee9e300000 dataLength = 5 numChildren = 0
zk: localhost:2181(CONNECTED) 58] create -e /test3 ll3 Created /test3 [zk: localhost:2181(CONNECTED) 59] set /test3 ll3+1 [zk: localhost:2181(CONNECTED) 60] get /test3 ll3+1 [zk: localhost:2181(CONNECTED) 61] ls / [admin, brokers, cluster, config, consumers, controller_epoch, feature, isr_change_notification, latest_producer_id_block, log_dir_event_notification, test20000000017, test3, test40000000019, test40000000020, test5, test6, zookeeper] [zk: localhost:2181(CONNECTED) 62] get -s /test3 ll3+1 cZxid = 0xb3 ctime = Sun Jan 01 21:24:35 HKT 2023 mZxid = 0xb4 mtime = Sun Jan 01 21:24:48 HKT 2023 pZxid = 0xb3 cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x10002ee9e300000 dataLength = 5 numChildren = 0 [zk: localhost:2181(CONNECTED) 63] delete -v 1 /test3 [zk: localhost:2181(CONNECTED) 64] ls / [admin, brokers, cluster, config, consumers, controller_epoch, feature, isr_change_notification, latest_producer_id_block, log_dir_event_notification, test20000000017, test40000000019, test40000000020, test5, test6, zookeeper]
6 Gurator 客户端
是 java写的一个api 库,可以更方便的在代码中调用。而不是 使用zkCli
就zkCli能操作的。Gurator都封装好方法了。调用就完事了。
7 zk 的分布式锁
7.1 zk中锁的种类
- 读锁: 读锁是共享的,获取读锁的前提是没有写锁
- 写锁:写锁是排他的,获取写锁的前提是没有任何锁
7.2 读锁的实现
- 创建一个临时序号节点,节点的数据是read. 表示是读锁。
- 获取zk中序号比自己小的所有节点。如果最小的节点是读锁。则上锁成功。因为如果最小的锁是读锁。后面不可能会有写锁。读锁共享。所以可以直接上锁。如果最小的节点是写锁。上锁失败,监听最小的节点。阻塞,如果最小节点释放了。然后重新执行第二步。
但是这样有一个坏处,并发的时候。多个客户端都监听 最小的节点,最小的节点释放之后会触发所有的客户端 重新获取锁。
也叫惊群效应。
解决方案:并发的操作,不去监听最小的,而是监听自己的上一个。这样就会按照请求的顺序依次获取锁。像个链表一样。
7.3 写锁的实现
- 创建一个临时序号节点,节点的数据是write.
- 获取zk中所有的子节点。
- 判断自己是不是最小的节点,如果是,上锁成功,如果不是,上锁失败,监听上一个节点。直到自己是最小的节点。
8 zk的watch机制
就是需要监听某一个节点的事件。类似于监听器。如果更新或删除了执行某些动作。
一个客户端修改了数据,另一个客户端会收到数据被修改的时间通知。
如果想监听是单次生效的。使用 get -w /test-watch
监听当前目录 ls -w /
监听当前目录及子目录 ls -w -R /
9 zk集群
9.1 伪集群,在一台服务器上创建四个目录。目录下分别有myid文件,保存id
└─# pwd 2 ⚙ /usr/local/zookeeper/zkdata (base) ┌──(root💀kali)-[/usr/local/zookeeper/zkdata] └─# ll 2 ⚙ total 16 drwxr-xr-x 2 root root 4096 Jan 1 21:55 zk1 drwxr-xr-x 2 root root 4096 Jan 1 21:55 zk2 drwxr-xr-x 2 root root 4096 Jan 1 21:55 zk3 drwxr-xr-x 2 root root 4096 Jan 1 21:55 zk4 (base) ┌──(root💀kali)-[/usr/local/zookeeper/zkdata/zk1] └─# ll 2 ⚙ total 4 -rw-r--r-- 1 root root 2 Jan 1 21:55 myid (base) ┌──(root💀kali)-[/usr/local/zookeeper/zkdata/zk1] └─# cat myid 2 ⚙ 1
9.2 创建4个配置文件zoo*.cfg
每个配置文件的监听端口不一样,zoo1.cfg如下
tickTime=2000 dataDir=/usr/local/zookeeper/zkdata/zk1 # zk1-4 clientPort=2181 # 客户端连接端口 2181-4 # 2001-4 是 集群间 数据同步端口, 3001-4是集群间选举用的。 oberver是代表当前节点的角色 server.1=192.168.31.27:2001:3001 server.2=192.168.31.27:2002:3002 server.3=192.168.31.27:2003:3003 server.4=192.168.31.27:2004:3004:observer
(base) ┌──(root💀kali)-[/usr/local/zookeeper/conf] └─# ll 2 ⚙ total 36 -rw-r--r-- 1 lzl staff 535 Feb 25 2022 configuration.xsl -rw-r--r-- 1 lzl staff 4559 Feb 25 2022 logback.xml -rw-r--r-- 1 root root 201 Jan 1 22:02 zoo1.cfg -rw-r--r-- 1 root root 201 Jan 1 22:05 zoo2.cfg -rw-r--r-- 1 root root 201 Jan 1 22:06 zoo3.cfg -rw-r--r-- 1 root root 201 Jan 1 22:06 zoo4.cfg -rw-r--r-- 1 root root 57 Oct 23 16:02 zoo.cfg -rw-r--r-- 1 lzl staff 1187 Oct 23 16:00 zoo_sample.cfg
9.3 启动zk,分别使用不同的配置文件
└─# ./zkServer.sh start ../conf/zoo1.cfg 2 ⚙ /usr/bin/java ZooKeeper JMX enabled by default Using config: ../conf/zoo1.cfg Starting zookeeper ... STARTED (base) ┌──(root💀kali)-[/usr/local/zookeeper/bin] └─# ./zkServer.sh start ../conf/zoo2.cfg 2 ⚙ /usr/bin/java ZooKeeper JMX enabled by default Using config: ../conf/zoo2.cfg Starting zookeeper ... STARTED (base) ┌──(root💀kali)-[/usr/local/zookeeper/bin] └─# ./zkServer.sh start ../conf/zoo3.cfg 2 ⚙ /usr/bin/java ZooKeeper JMX enabled by default Using config: ../conf/zoo3.cfg Starting zookeeper ... STARTED (base) ┌──(root💀kali)-[/usr/local/zookeeper/bin] └─# ./zkServer.sh start ../conf/zoo4.cfg 2 ⚙ /usr/bin/java ZooKeeper JMX enabled by default Using config: ../conf/zoo4.cfg Starting zookeeper ... STARTED (base) ┌──(root💀kali)-[/usr/local/zookeeper/bin]
发现启动失败,看出启动日志。报错信息如下
(base) ┌──(root💀kali)-[/usr/local/zookeeper/logs] └─# cat zookeeper-lzl-server-kali.out 1 ⚙ Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true 2023-01-01 22:34:17,833 [myid:] - INFO [main:o.a.z.s.q.QuorumPeerConfig@177] - Reading configuration from: /usr/local/zookeeper/conf/zoo1.cfg 2023-01-01 22:34:17,859 [myid:] - INFO [main:o.a.z.s.q.QuorumPeerConfig@440] - clientPortAddress is 0.0.0.0:2181 2023-01-01 22:34:17,860 [myid:] - INFO [main:o.a.z.s.q.QuorumPeerConfig@444] - secureClientPort is not set 2023-01-01 22:34:17,860 [myid:] - INFO [main:o.a.z.s.q.QuorumPeerConfig@460] - observerMasterPort is not set 2023-01-01 22:34:17,860 [myid:] - INFO [main:o.a.z.s.q.QuorumPeerConfig@477] - metricsProvider.className is org.apache.zookeeper.metrics.impl.DefaultMetricsProvider 2023-01-01 22:34:17,884 [myid:1] - ERROR [main:o.a.z.s.q.QuorumPeerMain@99] - Invalid config, exiting abnormally org.apache.zookeeper.server.quorum.QuorumPeerConfig$ConfigException: Error processing /usr/local/zookeeper/conf/zoo1.cfg at org.apache.zookeeper.server.quorum.QuorumPeerConfig.parse(QuorumPeerConfig.java:198) at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:125) at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:91) Caused by: java.lang.IllegalArgumentException: initLimit is not set at org.apache.zookeeper.server.quorum.QuorumPeerConfig.checkValidity(QuorumPeerConfig.java:786) at org.apache.zookeeper.server.quorum.QuorumPeerConfig.setupQuorumPeerConfig(QuorumPeerConfig.java:663) at org.apache.zookeeper.server.quorum.QuorumPeerConfig.parseProperties(QuorumPeerConfig.java:487) at org.apache.zookeeper.server.quorum.QuorumPeerConfig.parse(QuorumPeerConfig.java:194) ... 2 common frames omitted Invalid config, exiting abnormally 2023-01-01 22:34:17,885 [myid:1] - INFO [main:o.a.z.a.ZKAuditProvider@42] - ZooKeeper audit is disabled. 2023-01-01 22:34:17,889 [myid:1] - ERROR [main:o.a.z.u.ServiceUtils@42] - Exiting JVM with code 2
initLimit is not set
集群必须设置这个参数。。。
zoo*.cfg新增如下俩个参数
└─# cat zoo1.cfg 1 ⚙ tickTime=2000 dataDir=/usr/local/zookeeper/zkdata/zk1 clientPort=2181 initLimit=10 syncLimit=5 # server.1=192.168.31.27:2001:3001 server.2=192.168.31.27:2002:3002 server.3=192.168.31.27:2003:3003 server.4=192.168.31.27:2004:3004:observer
重新启动后查看 每个节点的状态。全部启动之后才能查看得到。
启动是否成功,根据是否有对应的配置文件进程
(base) ┌──(root💀kali)-[/usr/local/zookeeper/bin] └─# ./zkServer.sh status /usr/local/zookeeper/conf/zoo4.cfg 1 ⚙ /usr/bin/java ZooKeeper JMX enabled by default Using config: /usr/local/zookeeper/conf/zoo4.cfg Client port found: 2184. Client address: localhost. Client SSL: false. Mode: observer (base) ┌──(root💀kali)-[/usr/local/zookeeper/bin] └─# ./zkServer.sh status /usr/local/zookeeper/conf/zoo3.cfg 1 ⚙ /usr/bin/java ZooKeeper JMX enabled by default Using config: /usr/local/zookeeper/conf/zoo3.cfg Client port found: 2183. Client address: localhost. Client SSL: false. Mode: follower (base) ┌──(root💀kali)-[/usr/local/zookeeper/bin] └─# ./zkServer.sh status /usr/local/zookeeper/conf/zoo2.cfg 1 ⚙ /usr/bin/java ZooKeeper JMX enabled by default Using config: /usr/local/zookeeper/conf/zoo2.cfg Client port found: 2182. Client address: localhost. Client SSL: false. Mode: leader (base) ┌──(root💀kali)-[/usr/local/zookeeper/bin] └─# ./zkServer.sh status /usr/local/zookeeper/conf/zoo1.cfg 1 ⚙ /usr/bin/java ZooKeeper JMX enabled by default Using config: /usr/local/zookeeper/conf/zoo1.cfg Client port found: 2181. Client address: localhost. Client SSL: false. Mode: follower (base) ┌──(root💀kali)-[/usr/local/zookeeper/bin]
9.4 连接集群
/zkCli.sh -server 192.168.31.27:2181,192.168.31.27:2182,192.168.31.27:2183,192.168.31.27:2184
四个节点的参数都填上。
10 ZAB 协议
zookeeper atomic broadcast 协议
微信翻译哈哈,zookeeper起名本来就是动物园管理的意思,因为好多产品都拿动物起名。其实就是zookeeper 原子广播协议
zookeeper是一主多从的。根据zab协议来 解决zookeeper的崩溃恢复和主从数据同步的问题。
10.1 集群节点的四个状态
- following: 从节点所处的状态
- leading: 主节点所处的状态
- looking: 选举状态
10.2 集群上线时选举
先启动zoo1.cfg. 称为节点1.
再启动zoo2.cfg. 称为节点2.
节点1 给节点2 发送自己的选票。 选票格式:(myid,zXid) (当前节点的myid.集群中唯一,zxid是事务ID)(1, 0)
节点2 给节点1 发送自己的选票。(2, 0)
目前节点1有俩张选票,自己的(1,0) 和 节点2发送给节点1的(2,0)。根据zXid,myid 的优先级 谁大投谁。 投 (2,0)。节点2 有1票
为啥事务ID优先级比myid高:因为事务ID越大,说明请求越多。所以就让这个来当老大。
目前节点2也有俩张选票子节点(2,0) 和节点1 发送给节点2的(1,0)。投(2,0)。 节点2有1票。
目前还没有选出来主节点,当票数大于集群内所有节点个数的一半才行。
观察节点不参与选举,当前集群有三个节点参与选举。 不是已经启动的节点。是配置文件里所有的非观察节点。
还要继续投票。第二轮投票。把自己选票中最大的zxid,myid 发送给对方。
节点1 给节点2 (2,0),节点 2 有俩张选票(2,0)(第一轮自己的)(2,0)(第二轮节点1发给节点2的)
节点2 给节点1 (2, 0),节点 1 有俩张选票(2,0)(第一轮节点2发给节点1的,自己选出来的)(2,0)(第二轮节点2发给节点1的)
目前节点2 有2票。当选主节点
此时,再启动节点3. 查看集群已经有主节点,就不抢了。自己就当从节点。
10.3 崩溃恢复时的选举
节点2 是主节点。 当主节点 负责读写数据、从节点和观察节点只负责读数据。
当主节点挂掉之后。需要在从节点再选择一个主节点
主 从节点之间 会有故障检测。 就是2001,2002,2003,2004端口。
主节点周期性的发送ping命令给从节点。主节点挂掉之后,从节点一段时间没收到主节点的ping命令,就认为主节点挂了。
开始新一轮的选举。从节点的状态从following 切换为looking状态。选举就是集群上线时选举一样。
PS:没有leader就不对外提供服务。
10.4 主从节点的数据同步
客户端可能连接集群里的任意一台。
当读数据的时候。主从都一样。
但当写数据的时候。从会发送给主,让主来写数据。
主会先保存数据到 数据文件中,自己会给自己返回一个ack , 然后主会发送数据给所有的从节点。
从节点也是先保存数据到数据文件中,然后给主节点回复 ack, 当超过集群一半的节点 回复 ack给 主节点,也就是超过一般的节点已经写到自己的本地文件。
主节点就会发送commit给从节点。
从节点会保存数据到内存中。客户端读取数据是读取 内存里的数据。 不是数据文件中的。
然后客户端连接的主或从节点才会收到 写成功。
- why 半数以上?
kafka集群也有这个机制。
为了提高吞吐量。 要是等所有的节点都 写成功才算成功。那么就会很慢。
PS: 半数以上计算的时候 包括主节点
11 CAP理论
2000年提出的理论。分布式集群有了理论指导。
-
Consistency 一致性
就是写成功返回给客户端的前提是所有的节点的数据已经保持一致了,这种叫强一致性。 zk不是这样子的,zk是最终一致性。半数以上之后。后面会同步未同步的节点。最后数据肯定会一致。
但是有些系统不能使用最终一致性。比如银行,金融。 有一台数据没同步。查询的时候,发现钱没了。其实过几秒或几十秒会同步过来,但是还是对用户影响挺大的。 -
Availability 可用性
服务可用就是当前集群能够提供正常的功能,能在正常的时间内返回,而不是时延太大。
这个和 一致性就会有冲突。不可能同时满足。 -
Partition tolerance 分区容错性
就是分布式系统,在多个节点的情况下,一个节点挂了。还能用。 这是分布式的目的。这个是必要条件。
所有CAP只能有其中俩个共存。
CP 或 AP
zk属于AP
CA就是单机了。不算分布式了。个人理解。
12 BASE理论
就是在特殊情况下,系统的功能不一定要全部能用。比如双十一的时候。那天晚上退款功能,评论功能会禁止取消。为了给下单功能让路。
- Basically Available 基本可用
核心功能能用。 服务降级。 - Soft State 软状态
系统存在中间状态。该中间状态不影响系统整体可用。 运行不同的节点之间数据不一致。延时一致。 - Eventual Consistency
就是在运行一定时间后,所有节点的数据保证会一致。 也叫弱一致性。
13 关于分布式推荐书籍
-《分布式高可用算法》
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统