Zookeeper之两阶段提交源码分析

zookeeper集群为了保证数据一致性,使用了两阶段提交。
在zookeeper集群的角色有:leader、follower、observer。
在这几个角色中处理读写请求是不同的:
读请求:从当前节点直接读取数据
写请求:在leader直接进行两阶段提交、在非leader则是把请求转交给leader处理
所以,分析两阶段提交就是分析集群模式下的请求处理。在单机模式在请求处理是经过RequestProcessor请求处理链处理。
单个zookeeprt请求处理主要有以下几步:
1、对当前请求生成日志txn
2、持久化日志txn
3、根据日志txn更新Database
两阶段提交(2PC)步骤:


image.png

lead节点请求处理链构建

image.png

其中标绿色的PrepRequestProcessor、SyncRequestProcessor、CommitProcessor都继承了ZooKeeperCriticalThread是一个线程。
org.apache.zookeeper.server.quorum.LeaderZooKeeperServer#setupRequestProcessors


image.png

org.apache.zookeeper.server.quorum.ProposalRequestProcessor#ProposalRequestProcessor


image.png

ProposalRequestProcessor中包含SyncRequestProcessor和AckRequestProcessor
LeaderRequestProcessor----->PrepRequestProcessor---->ProposalRequestProcessor(SyncRequestProcessor--->AckRequestProcessor)----->CommitProcessor--->Leader.ToBeAppliedRequestProcessor---->FinalRequestProcessor

1、LeaderRequestProcessor

org.apache.zookeeper.server.quorum.LeaderRequestProcessor#processRequest


image.png

①、检查是不是local session本地session,创建临时节点会升级session
org.apache.zookeeper.server.quorum.QuorumZooKeeperServer#checkUpgradeSession


image.png

image.png

image.png

②、交给下一个请求处理器处理


image.png

2、PrepRequestProcessor

作用与单机模式相同,给请求Request的Hdr和Txn赋值,然后交给下一个请求处理器处理

3、ProposalRequestProcessor(2PC提交协议)

如果是写请求(request.getHdr() != null),则会把当前请求封装为协议并发送给follower。发送之后交给SyncRequestProcessor持久化处理


image.png

org.apache.zookeeper.server.quorum.Leader#propose


image.png

image.png

image.png

org.apache.zookeeper.server.quorum.Leader#sendPacket
发送到所有其他Followe节点forwardingFollowers


image.png

org.apache.zookeeper.server.quorum.LearnerHandler#queuePacket
添加到LearnerHandler的queuedPackets队列中
image.png

org.apache.zookeeper.server.quorum.LearnerHandler#sendPackets
image.png
3.1、SyncRequestProcessor(2PC持久化)

把请求放入到queuedRequests阻塞队列


image.png

①、对请求进行持久化与单机相同
org.apache.zookeeper.server.SyncRequestProcessor#run


image.png

image.png

②、交给下一个AckRequestProcessor处理
3.2、AckRequestProcessor(两阶段提交leader端处理)

向lead发送自己的ack(2PC发送ACK)


image.png

org.apache.zookeeper.server.quorum.Leader#processAck


image.png

image.png

image.png

org.apache.zookeeper.server.quorum.Leader#tryToCommit


image.png

image.png

image.png

org.apache.zookeeper.server.quorum.Leader#commit
创建一个Leader.COMMIT数据包并发送所有Follower节点
image.png

org.apache.zookeeper.server.quorum.Leader#inform
创建一个INFORM通知包发送给所有观察者Observer节点
image.png

org.apache.zookeeper.server.quorum.CommitProcessor#commit
提交当前请求,放入到committedRequests,最终会更新database


image.png

4、CommitProcessor

CommitProcessor类参数:
queuedRequests:表示接收到的请求,没有进行两阶段的提交
queuedWriteRequests:表示接收到的写请求,没有进行两阶段的提交
committedRequests:表示可以提交的请求,在两阶段验证过半之后进行会在本地进行committe操作,便添加到这个队列
commitIsWaiting:表示存在可以提交的请求(committedRequests是否有值,有true)
pendingRequests:是一个map集合,表示每个客户端sessionId的请求
Leader类参数:
outstandingProposals:表示记录提议的请求的队列,符合过半机制之后会移除
toBeApplied:表示记录待生效的请求,在FinalRequestProcessor移除
①、processRequest
org.apache.zookeeper.server.quorum.CommitProcessor#processRequest


image.png

首先判断是否需要两阶段提交。如果需要则会添加到queuedWriteRequests队列
org.apache.zookeeper.server.quorum.CommitProcessor#needCommit
如果是更改操作则返回true


image.png

②、CommitProcessor#run
CommitProcessor是一个线程最主要的是运行run方法
org.apache.zookeeper.server.quorum.CommitProcessor#run
a、commitIsWaiting和requestsToProcess获取
image.png

首先获取commitIsWaiting是否有待提交的(committedRequests有值返回true),requestsToProcess待处理的请求大小
b、wait()等待
如果queuedRequests和committedRequests没有数据则会wait();等待
image.png

c、pendingRequests
image.png

这里表示:如果需要提交,则会直接放入到pendingRequests集合中。如果是个读操作,则会查看当前请求的sessionId是否存在pendingRequests集合,如果存在继续添加到pendingRequests集合。如果都不符合,说明是一个客户端的读请求,直接交给下一个sendToNextProcessor(request);处理
image.png

d、然后,再看一下这个while的退出条件。
①、从queuedRequests取出的是空
②、如果queuedRequests数据不为空,那么requestsToProcess是大于0的。这时只有maxReadBatchSize < 0或readsProcessed <= maxReadBatchSize才能退出。
maxReadBatchSize < 0表示默认是-1,如果配置了这个参数当连续读了readsProcessed时,也会退出。
③、pendingRequests和committedRequests不为空
e、commitIsWaiting有待提交的


image.png

从committedRequests取出请求,while循环处理写请求
image.png

从pendingRequests集合获取此客户端sessionId的等待集合sessionQueue(可能会有读写)
一个pendingRequests可能会存这样的数据,一个客户端发送这样一系列命令: sessionQueue ={读、读、写、写}
image.png

把第一个请求重新赋值给topPending
image.png

把当前请求放入到queuesToDrain,把此时请求从committedRequests移除,把提交的数量commitsProcessed加1,把commitsToProcess=maxCommitBatchSize提交处理写的减1,这里为了退出while循环while (commitIsWaiting && !stopped && commitsToProcess > 0) 。最后调用processWrite方法处理这个写请求交给下个处理器处理
image.png

f、queuesToDrain
这里是与commitsToProcess结合,commitIsWaiting表示还有待提交的,在处理commitsToProcess个写请求之后退出了,在queuesToDrain中再优先处理一部分读
image.png

5、ToBeAppliedRequestProcessor

org.apache.zookeeper.server.quorum.Leader.ToBeAppliedRequestProcessor#processRequest
删除toBeApplied


image.png

follower节点请求处理链构建

image.png

其中标绿色的FollowerRequestProcessor、CommitProcessor、SyncRequestProcessor都继承了ZooKeeperCriticalThread是一个线程。
org.apache.zookeeper.server.quorum.FollowerZooKeeperServer#setupRequestProcessors


image.png

开了两条链:
FollowerRequestProcessor(firstProcessor)---->CommitProcessor----->FinalRequestProcessor
SyncRequestProcessor---->SendAckRequestProcessor

1、FollowerRequestProcessor

org.apache.zookeeper.server.quorum.FollowerRequestProcessor#processRequest
请求添加到queuedRequests队列


image.png

image.png

FollowerRequestProcessor是一个线程,会从queuedRequests获取请求
org.apache.zookeeper.server.quorum.FollowerRequestProcessor#run


image.png

image.png

org.apache.zookeeper.server.quorum.Learner#request
创建请求转发给lead节点处理
image.png

createSession和closeSession也会转发给lead节点处理


image.png

2、SendAckRequestProcessor

org.apache.zookeeper.server.quorum.SendAckRequestProcessor#processRequest
在用SendAckRequestProcessor处理之前会先调用SyncRequestProcessor进行持久化处理,由于与单机或lead处理相同就不单独列出来了。
向领导者发送确认ack包


image.png

org.apache.zookeeper.server.quorum.Learner#writePacket


image.png

org.apache.zookeeper.server.quorum.Learner#writePacketNow
image.png
LearnerHandler转发请求

在经过FollowerRequestProcessor处理后,lead端会得到一个Request的请求
org.apache.zookeeper.server.quorum.LearnerHandler#run


image.png

org.apache.zookeeper.server.quorum.Leader#submitLearnerRequest


image.png

org.apache.zookeeper.server.quorum.LeaderZooKeeperServer#submitLearnerRequest
转发到leader的prepRequestProcessor
image.png

在连接Follower节点的客户端发送更改命令请求会转发到leader节点的prepRequestProcessor进行处理

两阶段提交Follower端处理

1、run
org.apache.zookeeper.server.quorum.QuorumPeer#run


image.png

2、followLeader
org.apache.zookeeper.server.quorum.Follower#followLeader
不断读取从lead端的数据包


image.png

①、Follower接收到PROPOSAL协议命令请求
org.apache.zookeeper.server.quorum.Follower#processPacket
image.png

image.png

org.apache.zookeeper.server.quorum.FollowerZooKeeperServer#logRequest


image.png

调用到SyncRequestProcessor处理器处理、SyncRequestProcessor处理完之后便交给SendAckRequestProcessor处理器处理发送ACK数据包
②、Follower接收到commit命令请求
image.png

org.apache.zookeeper.server.quorum.FollowerZooKeeperServer#commit
image.png

调用到commitProcessor处理器处理把请求添加到committedRequests队列,处理完之后会交给FinalRequestProcessor处理器处理,这样在连接Follower客户端的更改操作也会有数据返回

observer节点请求处理链构建

image.png

其中标绿色的ObserverRequestProcessor、CommitProcessor、SyncRequestProcessor都继承了ZooKeeperCriticalThread是一个线程。
org.apache.zookeeper.server.quorum.ObserverZooKeeperServer#setupRequestProcessors


image.png

也是开了两条链:
ObserverRequestProcessor(firstProcessor)---->CommitProcessor----->FinalRequestProcessor
SyncRequestProcessor---->null
observer节点不参与两阶段提交,所以同步SyncRequestProcessor之后没有ACK确认提交。这样既提高了读效率,又对写效率没有影响。请求处理链与leader、follower的功能相同不再累述。

总结:

zookeeper集群的两阶段提交,是在写操作的情况下发生的。2PC的整体实现逻辑是在RequestProcessor请求处理链处理的。只有在接受到的ACK超过一半才会进行提交,提交的实现逻辑是在CommitProcessor中实现的,CommitProcessor处理器中里面涉及多种集合、队列等参数(需要首先了解这些参数意义,然后再读CommitProcessor源码)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350