节点通信
回顾我们之前配置zookeeper时,分别对每一个实例指定了2个端口号,1个是2888,一个是3888。
server.1=node01:2888:3888
server.2=node02:2888:3888
server.3=node03:2888:3888
server.4=node04:2888:3888
2888: leader的暴露端口,用于接受follower的写请求和用于主从数据同步使用该端口进行。
3888: 选主投票时使用端口
我们以四台节点为例:
node01(192.168.221.66) follower
node02(192.168.221.68) follower
node03(192.168.221.70) follower
node04(192.168.221.72) leader
分别进行查看它们关于2888和3888的端口连接状况:
netstat -natp | egrep '(2888|3888)'
node01:
[root@dream01 ~]# netstat -natp | egrep '(2888|3888)'
tcp 0 0 ::ffff:192.168.221.66:3888 :::* LISTEN 1313/java
tcp 0 0 ::ffff:192.168.221.66:55053 ::ffff:192.168.221.72:2888 ESTABLISHED 1313/java
tcp 0 0 ::ffff:192.168.221.66:3888 ::ffff:192.168.221.70:59534 ESTABLISHED 1313/java
tcp 0 0 ::ffff:192.168.221.66:3888 ::ffff:192.168.221.68:33912 ESTABLISHED 1313/java
tcp 0 0 ::ffff:192.168.221.66:3888 ::ffff:192.168.221.72:57391 ESTABLISHED 1313/java
node02:
[root@dream02 ~]# netstat -natp | egrep '(2888|3888)'
tcp 0 0 ::ffff:192.168.221.68:3888 :::* LISTEN 1069/java
tcp 0 0 ::ffff:192.168.221.68:3888 ::ffff:192.168.221.72:58166 ESTABLISHED 1069/java
tcp 0 0 ::ffff:192.168.221.68:60803 ::ffff:192.168.221.72:2888 ESTABLISHED 1069/java
tcp 0 0 ::ffff:192.168.221.68:3888 ::ffff:192.168.221.70:40730 ESTABLISHED 1069/java
tcp 0 0 ::ffff:192.168.221.68:33912 ::ffff:192.168.221.66:3888 ESTABLISHED 1069/java
node03:
[root@dream03 ~]# netstat -natp | egrep '(2888|3888)'
tcp 0 0 ::ffff:192.168.221.70:3888 :::* LISTEN 1263/java
tcp 0 0 ::ffff:192.168.221.70:59534 ::ffff:192.168.221.66:3888 ESTABLISHED 1263/java
tcp 0 0 ::ffff:192.168.221.70:40730 ::ffff:192.168.221.68:3888 ESTABLISHED 1263/java
tcp 0 0 ::ffff:192.168.221.70:45808 ::ffff:192.168.221.72:2888 ESTABLISHED 1263/java
tcp 0 0 ::ffff:192.168.221.70:3888 ::ffff:192.168.221.72:36776 ESTABLISHED 1263/java
node04:
[root@dream04 ~]# netstat -natp | egrep '(2888|3888)'
tcp 0 0 ::ffff:192.168.221.72:3888 :::* LISTEN 1246/java
tcp 0 0 ::ffff:192.168.221.72:2888 :::* LISTEN 1246/java
tcp 0 0 ::ffff:192.168.221.72:2888 ::ffff:192.168.221.68:60803 ESTABLISHED 1246/java
tcp 0 0 ::ffff:192.168.221.72:2888 ::ffff:192.168.221.70:45808 ESTABLISHED 1246/java
tcp 0 0 ::ffff:192.168.221.72:58166 ::ffff:192.168.221.68:3888 ESTABLISHED 1246/java
tcp 0 0 ::ffff:192.168.221.72:36776 ::ffff:192.168.221.70:3888 ESTABLISHED 1246/java
tcp 0 0 ::ffff:192.168.221.72:2888 ::ffff:192.168.221.66:55053 ESTABLISHED 1246/java
tcp 0 0 ::ffff:192.168.221.72:57391 ::ffff:192.168.221.66:3888 ESTABLISHED 1246/java
观察上面几个图,
首先看关于2888端口,观察规律:
发现在node4暴露了2888端口,node1,node2,node3都分别与node04的2888端口建立了连接。
大致是这样的:
关于3888端口,观察规律:
1、发现每个节点都暴露了3888端口。
2、发现每个节点都只向比自己权重低的所有节点的3888端口建立了连接。
大致是这样的:
ZK选举机制
ZK选举发生的场景
zookeeper选举基本上发生在两种情况下:
1、第一次启动集群的时候
2、重启集群,或者当leader宕机后。
ZK选举规则
zookeeper里选举leader其实是谦让制的,那什么样的会被公认觉得就它应该是leader?
每个zkServer实例身上都有zxid以及myid。
当选举的时候,会先比较zxid,如果zxid相同,再比较myid。(大部分时候zxid都应该是一致的,如果频繁出问题,这锅肯定是运维的)
ZK选举过程:
以四台节点node01,node02,node03,node04举例:
1、当第一次启动服务的时候,按顺序启动1,2,3,4节点。当过半台数启动(也就是1,2,3节点启动)后,开始选举,事务id都是0,推选MYID最大的那个服务为leader。 也就是node03
2、node03发生了故障,01,02,04开始进行选举。
此时04机器因为最终一致性,还没来得及同步,03就挂掉了,所以比起其他机器,事务id落后1.。
最先发现主机故障的从节点会主动向之前建立了3888端口的节点进行通信,每次请求要携带自己的MYID和ZXID作为铭牌。
每次请求,都会返回请求方与响应方的铭牌,比较大的那个铭牌所属节点,放入请求方和响应方里分别进行累积。
当A节点累积的B节点数达到参与选举的总数时,A节点会判断自己是否是B节点,
如果是,就把自己的2888端口开放,自己就是leader;如果不是,就将B节点设置为自己的leader
以04首先发现了主机故障了为例,它开始向之前建立了3888端口的节点进行通信,
简要描述一下过程:
04向01发送通信,对比铭牌,发现01的铭牌比自己大,因此会返还给04自己的节点号,此时01记录01一票 ,04记录一下 01一票
04向02发送通信,对比铭牌,发现02的铭牌比自己大,因此会返还给04自己的节点号,此时02记录一下 02一票,此时04记录一下 02一票
此处有个细节:每个节点收到其他节点的铭牌请求时,会触发该节点也去向之前建立了3888端口的节点进行通信。
因此会触发02分别向01和04发送铭牌比较的请求:
02向04发送通信,对比铭牌,发现04的铭牌比自己小,因此会返还给02自己的节点号,此时04记录一下 02一票,02记录一下 02一票
02向01发送通信,对比铭牌,发现01的铭牌比自己小,因此会返还给02自己的节点号,此时01记录一下 02一票,此时02记录一下 02一票
。。。。。。
。。。。。。 以此类推,过程省略
。。。。。。
这种来回碰撞,一直触发向两端对比铭牌,到最后,每个节点一定都会有node02+3,此时将会把02置为leader。
虽然我们描述的好像很麻烦,但实际上它的运作是非常高速的,就是通过这种机制使得官方可以号称200毫秒即可完成选举工作。
ZK选举过程总结
1、通过3888端口造成zk服务实例两两通信。
2、只要任何人投票,都会触发那个准leader发起自己的投票
3、推选制:先比较zxid,如果zxid相同,再比较myid