zookeeper的连接建立+session管理+2pc提交原理分析-又揭开zk神秘面纱

拳法码农

1 zk的leader和follower之间的连接

zk的leader和follower交互.png
(1)leader选举成功后,状态改变,执行lead方法,lead()方法其实就是启动了个LearnCnxAcceptor线程,为了接受其他follower/observer的请求。用的是传统的bio,

a LearnerCnxAcceptor线程start启动
b LearnerCnxAcceptor线程调用accept,等待其他follower连接
c 其他follower连接建立,则生成这个follower专用的LearnerHandler线程,并start
d LearnerHandler线程代表了对follower的请求收发,通过while循环阻塞读取follower的请求,请求包括注册,proposal的ack,ping等等。
e 读取请求后,通过jute的反序列化和定制的协议规则,解析请求数据并处理。

(2)follower身份确立后,状态改变,执行Follwer的followLeader方法,做这几个事:

a findLeader,找到leader是哪个机器
b connectToLeader,对leader发起连接,此时leader如果连接建立会生成LearnerHandler
c 向leader发起注册,主要是follower信息。
d while循环,阻塞式的读取和处理leader发来的请求,proposal commit等操作。

leader和follower主要通过jute来进行序列化/反序列化,数据用单词开始结束 ,如type xxxxx type,代表type的内容。

2 客户端和服务端的连接建立

图片.png

(1)客户端启动,其实就是new Zookeeper实例,构造方法中会初始化一个ClientCnxn组件,用于和server端连接通信。clientCnx 启动,run方法里while循环,再循环中不断发出ping和数据包

 a 如果没连接,ClientCnxnSocketNIO的registerAndConnect创建连接,
 b 连接建立后,判断上次发起ping的时间间隔,通过sendping和服务端发起心跳
 c 基于底层封装的clientCnxnSocket,通过nio的读写事件遍历selectkey对读写事件进行处理。

(2)服务端就是quorumpeer启动的时候启动起来的NIOServerCnxnFactory,使用的通用的nio,通过configure计算使用的线程数,实际上是2*cpu核数的worker线程和一个固定的accept建立连接线程。由于客户端不一定特别多,所以一个accept也够用。接下来accept的run接受accept事件,然后注册read事件,使用select处理具体读写事件。

3 session管理

zk的session机制 (1).png
(1)session的创建流程:

a zookeeper客户端创建后,会定时ping到server,请求入自己的队列,然后由nio组件消费队列通过上面说的连接发到server端,
b server端通过启动的NIOServerCnxnFactory网络组件接受请求,拆包转化。
c 如果第一次请求,没有session,会进入processConnectRequest,使用sessionTraker组件创建唯一sessionid。
d 然后把这次session存到sessionid为key的map中,并进行touch分桶,管理seesion声明周期。后面每次请求都会touch一下分到新的过期桶。
e 这次session请求submit到处理链中。
f 处理链一层层走下去,走完写回结果到客户端。创建session的话处理链做的不多,处理链出要给后面proposal和commit二次提交用。

(2)sessionid:通过机器标识(配置的myid)+时间戳位移,得到唯一id。分桶策略:ExpirationTime是指该会话最近一次可能超时的时间点,ExpirationTime = CurrentTime + SessionTimeout,sessionTracker的run会定时检查会话,其时间间隔为ExpirationInterval(默认配置的ticktime)。
(3)session的状态轮转
session的主要作用是判断临时节点是否还保留。

a Connected/Reconnected->Suspended
表明心跳超时, Session 可能将会失效(临时节点可能会消失);
b Suspended->Lost
表明已经连接上Server和服务器确认了Session超时,或者客服端长期无法连接到服务端,此事Session已经完全无法再使用(临时节点消失了);
c Suspended->Reconnected
重新连接上Server,并且Session能够继续使用(临时节点还存在);
d Suspended->Lost->Reconnected
重新连接上Server,但是Session已经重新创建(临时节点需要重新创建)

(4)session的touch激活
078A8F06-DA8C-4D81-A1B8-98015951AFCE.png
client发出激活请求的时机:

 a 客户端向服务端发送请求,包括读写请求,就会触发会话激活。
 b 客户端发现在sessionTimeout/3时间内尚未与服务端进行任何通信,就会主动发起ping请求,服务端收到该请求后,就会触发会话激活。

(5)session的检测和清理
sessionTracker的run方法定期检测,

a 设置session状态位关闭,清除过期的桶
b 提交一个closeSession的本地请求到处理链,和正常请求一样进行一个2pc的删除,删除内存临时节点。session保存了自己创建的所有临时节点,直接删非常方便。删了以后还会触发watcher。
c 移除会话,把session对象移除sessionTracker.removeSession(sessionId);
d 从NIOServerCnxFactory里,按照标准的nio关闭流程关闭session对应的连接。

4 2pc提交流程分析


从client客户端的create开始,发送到zkserver的leader机器。leader有自己的proposal处理链,对所有follower发送proposal,同时自己用一个proposal的ack集合收集返回的ack,过半后触发commit

a 客户端create,请求进入客户端的发送队列outgoingqueue,有clientcnx的sendthread线程发送请求出去,到达zkserver服务器的NIOServerCnxnFactory组件,提交给处理链。如果请求到了follower,follower完成转发。
b 处理链到达ProposalRequestProcessor,开启2.1,进入proposal方法,放入outstandingProposals队列,遍历所有leaner组件(这里learner可以看作follower),发送proposal到每个follower
c follower接收到proposal请求后,对数据包进行处理,先提交到Synprocessor线程进行刷内存数据库和刷盘,这里看到数据是写入内存的,刷盘是数据包队列为空或累计了1000条事务数据后再刷盘。然后通过SendAckRequestProcessor返回ack,这些processor名字很容易理解。
d leader收到proposal的ack后,边收集边统计,如果ack过半,则trycommit,自己先commit,然后异步发送commit到所有follower。leader自己commit完就能对外使用了,不用等commit的ack,leader自己commit是通过FinalRequestProcessor,写入内存数据库zkDatabase,反馈给客户端结果。
e follower收到commit,自己更新数据,逻辑和leader的commit一样。

还有些细节:
(1)leader接收写请求的时候,第一个processor,PreRequestProcessor里生成的事务id,zxid怎么生成?其实就是个原子long递增hzxid.incrementAndGet();
(2)全量数据快照的存储:SyncRequestProcessor里,不断读事务请求存内存的时候,会判断是否需要落整体快照(根据配置大小来判断),需要的话就开启写盘线程,通过序列化把内存树写入磁盘。
(3) qurumPeerMain启动的时候,有个DatadirCleanupManager,启动PurgeTask清理文件,因为快照都是全量的,前面的快照可以删,通过这个线程可以清理。
(4)leader和follower的处理链:

阅读 183

颈椎撕裂者。

15 声望
2 粉丝
0 条评论
你知道吗?

颈椎撕裂者。

15 声望
2 粉丝
宣传栏