消費(fèi)者發(fā)送加入組請(qǐng)求和同步組請(qǐng)求給服務(wù)端,服務(wù)端將請(qǐng)求的處理交給消費(fèi)組的協(xié)調(diào)者(GroupCoordinator)琐旁′汤客戶端發(fā)送生產(chǎn)請(qǐng)求和拉取請(qǐng)求給服務(wù)端,服務(wù)端將請(qǐng)求的處理交給副本管理器(RelicaManager)灰殴。與日志存儲(chǔ)相關(guān)的業(yè)務(wù)組件是副本管理器敬特,負(fù)責(zé)日志的底層類是日志管理器,副本管理器通過日志管理器間接的操作底層日志牺陶。
服務(wù)端創(chuàng)建日志伟阔,讀取日志,管理日志都只能通過日志管理器完成掰伸,日志管理器對(duì)象會(huì)傳遞給副本管理器皱炉,而副本管理器管理所有的分區(qū),分區(qū)管理所有的副本狮鸭,每個(gè)副本對(duì)應(yīng)一個(gè)日志合搅。分區(qū)在創(chuàng)建副本時(shí)才會(huì)創(chuàng)建日志。每個(gè)分區(qū)都需要持有副本管理器的引用歧蕉,才能通過日志管理器創(chuàng)建日志灾部。
副本管理器
追加消息時(shí),生產(chǎn)者客戶端會(huì)發(fā)送每個(gè)分區(qū)以及對(duì)應(yīng)的消息集廊谓;拉取消息時(shí)梳猪,客戶端會(huì)發(fā)送每個(gè)分區(qū)以及對(duì)應(yīng)的拉取信息。服務(wù)端返回給客戶端的響應(yīng)結(jié)果是按照分區(qū)分別返回蒸痹,生產(chǎn)請(qǐng)求的響應(yīng)結(jié)果包含追加消息集到分區(qū)后的起始偏移量春弥。拉取請(qǐng)求的響應(yīng)結(jié)果包含每個(gè)分區(qū)的最高水位(HW),每個(gè)分區(qū)的消息集叠荠。
追加和讀取本地日志
服務(wù)端接收客戶端發(fā)送的生產(chǎn)請(qǐng)求和拉取請(qǐng)求都會(huì)包含多個(gè)分區(qū)匿沛。追加消息集和讀取消息集,首先都需要獲取到分區(qū)榛鼎,然后再獲取分區(qū)的主副本逃呼。追加消息集時(shí),在分區(qū)中獲取主副本者娱,并寫入本地日志文件抡笼;讀取消息集時(shí),在副本管理器中獲取分區(qū)主副本黄鳍,并讀取本地日志文件推姻。追加消息集和讀取消息集都會(huì)獲取到副本的日志對(duì)象(Log),然后分別調(diào)用日志的追加方法(append())和讀取方法(read())框沟。
生產(chǎn)者用同步和異步的模式發(fā)送生產(chǎn)請(qǐng)求給服務(wù)端:同步模式下藏古,生產(chǎn)者發(fā)送一條消息后增炭,必須阻塞等到收到響應(yīng)結(jié)果后,才會(huì)接著發(fā)送下一條消息拧晕;異步模式下隙姿,生產(chǎn)者發(fā)送一條消息后,不用等待收到上一條消息的響應(yīng)結(jié)果厂捞,就可以接著發(fā)送下一條消息输玷。生產(chǎn)者發(fā)送生產(chǎn)請(qǐng)求通過設(shè)置:request.required.acks的值來控制是否需要等待服務(wù)端的應(yīng)答,應(yīng)答值表示:生產(chǎn)者要求主副本收到指定數(shù)量(備份副本)的應(yīng)答靡馁,才會(huì)認(rèn)為生產(chǎn)請(qǐng)求完成饲嗽。
應(yīng)答值為0:表示生產(chǎn)者不會(huì)等待服務(wù)端的任何應(yīng)答,將消息發(fā)送到網(wǎng)絡(luò)通道后就認(rèn)為生產(chǎn)請(qǐng)求完成了奈嘿∶蚕海客戶端發(fā)送生產(chǎn)請(qǐng)求后,對(duì)應(yīng)的響應(yīng)結(jié)果需要返回每條消息的在服務(wù)端偏移量裙犹,應(yīng)答值等于0時(shí)尽狠,每條消息的偏移量都是-1。
生產(chǎn)者設(shè)置的應(yīng)答值等于0叶圃,可能會(huì)丟失數(shù)據(jù):
1袄膏,生產(chǎn)者將消息發(fā)送到網(wǎng)絡(luò)通道后就認(rèn)為生產(chǎn)請(qǐng)求完成。此時(shí)向網(wǎng)絡(luò)通道發(fā)送3條數(shù)據(jù):消息1,2,3.
2掺冠,消息1和消息2通過網(wǎng)絡(luò)通道發(fā)送出去了沉馆,但消息3沒有被網(wǎng)絡(luò)通道發(fā)送出去。消息3就不算完成德崭。
3斥黑,主副本只收到了消息1,但不保證備份副本復(fù)制了消息1眉厨。
4锌奴,主副本掛掉了,它接收的消息1還沒有備份憾股,消息1就丟失了鹿蜀。
5,備份副本變?yōu)橹鞲北竞蠓颍蛻舳苏J(rèn)為成功的消息1和消息2實(shí)際上都丟失了茴恰。
應(yīng)答值為1:表示生產(chǎn)者會(huì)等待主副本收到一個(gè)應(yīng)答后,認(rèn)為生產(chǎn)請(qǐng)求完成斩熊。這個(gè)應(yīng)答其實(shí)是主副本自己的應(yīng)答:主副本收到客戶端發(fā)送的消息集往枣,并存儲(chǔ)到本地日志后,生產(chǎn)請(qǐng)求就算完成,服務(wù)端可以返回響應(yīng)結(jié)果給客戶端(其實(shí)就是主副本寫入日志成功)婉商。這種情況也沒有收到備份副本發(fā)送的應(yīng)答,仍然有可能丟失消息:主副本寫入本地日志后渣叛,服務(wù)端發(fā)送響應(yīng)給客戶端丈秩,生產(chǎn)者認(rèn)為請(qǐng)求完成,但這時(shí)主副本掛掉淳衙,備份副本還沒有來得及同步主副本寫入日志的信息蘑秽。
應(yīng)答值為-1:表示生產(chǎn)者發(fā)送生產(chǎn)請(qǐng)求后,所有處于同步中的備份副本(ISR)都向主副本發(fā)送了應(yīng)答后箫攀,生產(chǎn)請(qǐng)求才算完成肠牲。如果這些ISR中的副本只要有一個(gè)沒有向主副本發(fā)送應(yīng)答,主副本就會(huì)阻塞等待靴跛,生產(chǎn)請(qǐng)求就不能完成缀雳。這種情況下只要ISR中有一個(gè)副本存活,消息就不會(huì)丟失梢睛。
針對(duì)應(yīng)答值為-1的情況肥印,服務(wù)端為了等待備份副本發(fā)送應(yīng)答,可以采用阻塞的方式绝葡,但這種方式對(duì)服務(wù)端的性能影響較大深碱,所以針對(duì)這種情況kafka采用了延遲的操作。
創(chuàng)建延遲的生產(chǎn)和延遲的拉取
創(chuàng)建延遲的生產(chǎn)必須同時(shí)滿足三個(gè)條件:
1藏畅,request.required.acks=-1:生產(chǎn)者等待所有ISR中的備份副本都向主副本發(fā)送應(yīng)答敷硅。
2,生產(chǎn)者發(fā)送的消息集有數(shù)據(jù)愉阎。
3绞蹦,至少要有一個(gè)分區(qū)寫入到主副本本地日志文件成功(一個(gè)請(qǐng)求中可能會(huì)包含多個(gè)分區(qū)的消息集,如果沒有一個(gè)成功寫入日志文件榜旦,創(chuàng)建延遲的生產(chǎn)就沒有意義)坦辟。
如果以上情況,有任意一條不滿足章办,服務(wù)端就會(huì)立即返回響應(yīng)結(jié)果給客戶端锉走。如果服務(wù)端沒有創(chuàng)建延遲的生產(chǎn),并不意味著備份副本不會(huì)向主副本拉取數(shù)據(jù)藕届,只是生產(chǎn)者客戶端不關(guān)心而已挪蹭。
創(chuàng)建延遲的拉取必須同時(shí)滿足四個(gè)條件:
1,拉取請(qǐng)求設(shè)置等待時(shí)間大于0休偶。
2梁厉,拉取請(qǐng)求必須設(shè)置拉取的分區(qū)。
3,本次拉取還沒有收集到足夠的數(shù)據(jù)词顾。
4八秃,拉取分區(qū)時(shí)不能發(fā)送錯(cuò)誤。
延遲的生產(chǎn)和延遲的拉取的超時(shí)時(shí)間都是客戶端設(shè)置肉盹;延遲的生產(chǎn)和延遲的拉取的鍵都是分區(qū)昔驱。
服務(wù)端的延遲對(duì)象完成都有兩種方式:超時(shí)或者外部事件。延遲的生產(chǎn)的外部事件:ISR的所有備份副本都向主副本發(fā)送了應(yīng)答上忍;服務(wù)端處理備份副本的拉取請(qǐng)求骤肛,向主副本的本地日志讀取消息集后(最高水位有關(guān)(HW))。延遲的拉取的外部事件:讀取到足夠數(shù)量的消息集窍蓝;服務(wù)端處理生產(chǎn)請(qǐng)求腋颠,追加消息集到主副本的本地日志后。
服務(wù)端處理的拉取請(qǐng)求可以來自消費(fèi)者和備份副本吓笙,備份副本拉取主副本的消息淑玫,會(huì)嘗試完成延遲的生產(chǎn),而消費(fèi)者拉取主副本消息則不會(huì)去嘗試完成延遲的生產(chǎn)面睛。
生產(chǎn)者追加消息創(chuàng)建延遲的生產(chǎn)混移,它的限制條件是:所有ISR備份副本發(fā)送應(yīng)答給主副本。當(dāng)備份副本拉取消息時(shí)侮穿,表示備份副本會(huì)發(fā)送應(yīng)答給主副本歌径,就會(huì)嘗試完成延遲的生產(chǎn)。備份副本拉取消息創(chuàng)建延遲的拉取亲茅,它的限制條件是:拉取到足夠的消息集回铛。當(dāng)生產(chǎn)者追加到消息集到主副本后,表示有新的信息克锣,就會(huì)嘗試去完成延遲的拉取茵肃。
分區(qū)與副本
日志管理器是副本管理器的全局變量,副本管理器管理消息代理節(jié)點(diǎn)上的分區(qū)袭祟,副本管理器將日志管理器傳遞給所管理的分區(qū)验残,分區(qū)通過日志管理器為每個(gè)副本創(chuàng)建對(duì)應(yīng)的日志文件。日志管理器對(duì)日志進(jìn)行管理巾乳,副本管理器對(duì)副本進(jìn)行管理您没。日志管理器(Logmanager)通過每個(gè)日志對(duì)象(log)管理日志的所有分段(LogSegment),副本管理器(ReplicaManager)通過每個(gè)分區(qū)(Partition)管理每個(gè)分區(qū)的所有副本(Replica)胆绊。
副本機(jī)制:同一個(gè)分區(qū)的副本會(huì)存在于多個(gè)消息代理節(jié)點(diǎn)上氨鹏,并被對(duì)應(yīng)節(jié)點(diǎn)的副本管理器所管理。節(jié)點(diǎn)上的每個(gè)分區(qū)都有多個(gè)副本压状,但只有本地副本才有對(duì)應(yīng)的日志文件仆抵。(多個(gè)消息代理節(jié)點(diǎn)管理了同一個(gè)分區(qū),分區(qū)的不同副本,只有分區(qū)副本編號(hào)與代理節(jié)點(diǎn)編號(hào)相同的镣丑,被稱為本地副本舔糖,才會(huì)有對(duì)應(yīng)的日志文件,剩下的副本只是在當(dāng)前節(jié)點(diǎn)上保留信息:其實(shí)是分區(qū)和副本的對(duì)應(yīng)關(guān)系)
備份副本會(huì)向主副本拉取消息保持?jǐn)?shù)據(jù)的同步莺匠,主副本所在節(jié)點(diǎn)服務(wù)端處理備份副本的拉取請(qǐng)求金吗,也會(huì)更新對(duì)應(yīng)的備份副本信息(注意是主副本)。以上圖中節(jié)點(diǎn)2和節(jié)點(diǎn)3上的備份副本向節(jié)點(diǎn)1上的主副本發(fā)送了拉取請(qǐng)求慨蛙,節(jié)點(diǎn)1在返回主副本的數(shù)據(jù)給備份副本之前,分別是更新副本2和副本3的信息纪挎。這樣如果需要獲取分區(qū)的所有副本信息時(shí)期贫,就不需要和備份副本所在的節(jié)點(diǎn)進(jìn)行網(wǎng)絡(luò)通信;還有即使主副本所在的節(jié)點(diǎn)掛掉异袄,其他副本所在的節(jié)點(diǎn)也保存著分區(qū)和副本的關(guān)系通砍。
分區(qū)
每個(gè)分區(qū)都是只有一個(gè)主副本和多個(gè)備份副本,不同節(jié)點(diǎn)上的分區(qū)對(duì)象烤蜕,針對(duì)同一個(gè)分區(qū)封孙,這個(gè)分區(qū)對(duì)象的主副本對(duì)象是同一個(gè)。還維護(hù)了所有副本集合(AR)和同步的副本(ISR)讽营。
例如:分區(qū)P1有三個(gè)副本編號(hào)【1,2,3】虎忌,分別存儲(chǔ)在對(duì)應(yīng)編號(hào)的節(jié)點(diǎn)上,每個(gè)節(jié)點(diǎn)的副本管理器都會(huì)管理分區(qū)P1橱鹏。不同節(jié)點(diǎn)上的每個(gè)分區(qū)對(duì)象除了本地節(jié)點(diǎn)編號(hào)不一樣膜蠢,其他成員變量都一樣:AR等于1,2,3,主副本等于2莉兰。
副本
分區(qū)創(chuàng)建副本分成本地副本和遠(yuǎn)程副本挑围,節(jié)點(diǎn)編號(hào)和副本編號(hào)相同的副本叫做本地副本,編號(hào)不同的叫遠(yuǎn)程副本糖荒。
本地副本和遠(yuǎn)程副本的區(qū)別:本地副本有日志(log)杉辙,遠(yuǎn)程副本沒有日志。
數(shù)據(jù)目錄下有三個(gè)檢查點(diǎn)文件:恢復(fù)點(diǎn)捶朵,清理點(diǎn)蜘矢,最高水位,最高水位檢查點(diǎn)文件表示備份副本的數(shù)據(jù)同步位置综看。
每個(gè)副本對(duì)象都定義了兩個(gè)元數(shù)據(jù):最高水位元數(shù)據(jù)(HW)和偏移量元數(shù)據(jù)(LEO)硼端,創(chuàng)建副本對(duì)象時(shí)從最高水位檢查點(diǎn)文件中讀取分區(qū)的HW作為初始的最高水位。獲取副本偏移量元數(shù)據(jù):本地副本通過讀取日志文件的偏移量元數(shù)據(jù)寓搬。
以下兩種情況:
1珍昨,消息集追加到主副本的本地日志,更新日志的下一個(gè)偏移量元數(shù)據(jù)(nextOffsetMetadata)。
2镣典,備份副本同步主副本的數(shù)據(jù)兔毙,將拉取結(jié)果寫到本地日志,更新日志的下一個(gè)偏移量元數(shù)據(jù)(nextOffsetMetadata)兄春。
注意都只是更新的日志的下一個(gè)偏移量元數(shù)據(jù)(nextOffsetMetadata)澎剥,沒有操作副本的偏移量元數(shù)據(jù)。這是因?yàn)獒槍?duì)本地副本赶舆,當(dāng)需要獲取副本偏移量元數(shù)據(jù)哑姚,可以直接獲取日志的偏移量元數(shù)據(jù)。
具體步驟:
1芜茵,生產(chǎn)者客戶端將消息集追加到分區(qū)的主副本叙量。
2,消息集刷寫到主副本的本地日志九串,會(huì)更新日志的偏移量元數(shù)據(jù)绞佩。
3,其他消息代理節(jié)點(diǎn)上的備份副本向主副本所在的消息代理節(jié)點(diǎn)同步數(shù)據(jù)猪钮。
4品山,主副本所在的副本管理器讀取本地日志,更新對(duì)應(yīng)拉取的備份副本信息烤低。
5肘交,主副本所在的服務(wù)端將拉取結(jié)果返回給發(fā)起拉取請(qǐng)求的備份副本。
6扑馁,備份副本接收到服務(wù)端飯會(huì)的拉取結(jié)果酸些,將消息集追加到本地日志,更新日志的偏移量元數(shù)據(jù)檐蚜。
總結(jié):
某一個(gè)分區(qū)的所有副本魄懂,都在與之編號(hào)相同的消息代理節(jié)點(diǎn)有對(duì)應(yīng)的本地日志文件,同時(shí)消息代理節(jié)點(diǎn)上的副本管理會(huì)管理這個(gè)分區(qū)的所有副本的信息闯第。主副本和備份副本信息的更新情況不同:主副本:消息集追加到主副本市栗,刷寫到日志文件后更新日志的偏移量元數(shù)據(jù),主副本在處理備份副本拉取請(qǐng)求時(shí)所在節(jié)點(diǎn)的副本管理器讀取本地日志咳短,更新對(duì)應(yīng)拉取的備份副本在主副本節(jié)點(diǎn)上的信息填帽。備份副本:發(fā)起拉取請(qǐng)求,接收來之主副本節(jié)點(diǎn)的拉取請(qǐng)求結(jié)果咙好,并追加到本地日志中篡腌,并更新日志的元數(shù)據(jù)偏移量。
注意:這里只有主副本所在的副本管理器讀取本地日志更新其主副本的分區(qū)的備份副本信息勾效。在備份副本所在的節(jié)點(diǎn)的副本管理器嘹悼,備份副本接收到服務(wù)端返回的拉取結(jié)果叛甫,將消息集追加到本地日志,更新日志的偏移量元數(shù)據(jù)后杨伙,并沒有讀取備份副本的本地日志更新對(duì)應(yīng)的副本信息其监。
我的理解這是因?yàn)椋簜浞莞北颈旧淼哪康氖翘峁┫⒌膫浞荩瑐浞莞北舅诘墓?jié)點(diǎn)保留分區(qū)與副本的對(duì)應(yīng)關(guān)系限匣。在主副本所在的節(jié)點(diǎn)掛掉后抖苦,選擇出來的備份副本可以繼續(xù)提供讀寫服務(wù),并且數(shù)據(jù)不丟失米死。并且保留的分區(qū)和副本的對(duì)應(yīng)關(guān)系可以知道還有哪些備份副本锌历,并且當(dāng)剩下的備份副本發(fā)送拉取請(qǐng)求后,當(dāng)下的副本(已經(jīng)變成主副本)所有在節(jié)點(diǎn)的副本管理器就會(huì)讀取本地日志峦筒,更新對(duì)應(yīng)拉取的備份副本信息究西。這樣原本備份副本節(jié)點(diǎn)上面的副本信息就會(huì)更新到最近的副本信息。
備份副本同步數(shù)據(jù)
備份副本向主副本所在的消息代理節(jié)點(diǎn)發(fā)送拉取請(qǐng)求勘天,會(huì)指定備份副本編號(hào)怔揩。服務(wù)端處理備份副本請(qǐng)求捉邢,會(huì)讀取主副本的本地日志文件脯丝,然后更新備份副本的偏移量元數(shù)據(jù);擴(kuò)展分區(qū)的ISR集合伏伐;是否增加主副本最高水位宠进;如果主副本的最高水位增加還會(huì)嘗試完成延遲的生產(chǎn)請(qǐng)求和延遲的拉取請(qǐng)求。
擴(kuò)展ISR的集合必須滿足三個(gè)條件:
1藐翎,備份副本之前不在ISR集合中材蹬。2,備份副本必須在分區(qū)的AR中吝镣。3堤器,備份副本的偏移量必須大于或者等于主副本的最高水位,因?yàn)檫@樣才能表示備份副本已經(jīng)跟上了主副本的腳步末贾。
主副本的最高水位是ISR集合中偏移量最小的值闸溃。
為什么說主副本的最高水位增加了就可以嘗試完成延遲的生產(chǎn)請(qǐng)求和延遲的拉取請(qǐng)求?
1拱撵,消費(fèi)者最多只能消費(fèi)到主副本的最高水位辉川。如果消費(fèi)者已經(jīng)消費(fèi)到最高水位,但是主副本的最高水位一直沒有增加拴测,服務(wù)端就不會(huì)返回拉取結(jié)果給消費(fèi)者乓旗。而一旦主副本的最高水位增加了,就有可能滿足“拉取到足夠的消息”的限制條件集索,服務(wù)端就可以返回拉取結(jié)果給消費(fèi)者屿愚。
2汇跨,主副本等待ISR集合中的所有備份副本都向它發(fā)生應(yīng)答,在這之前渺鹦,服務(wù)端不會(huì)返回生產(chǎn)請(qǐng)求給生產(chǎn)者扰法,主副本的最高水位會(huì)選擇ISR集合中所有備份副本的最小偏移量值。服務(wù)端處理備份副本的拉取請(qǐng)求毅厚,會(huì)更新備份副本的偏移量塞颁,那么就有可能增加主副本的最高水位,一旦主副本的最高水位增加了吸耿,那么ISR集合中的所有備份副本一定都發(fā)送了應(yīng)答祠锣,服務(wù)器就可以返回生產(chǎn)請(qǐng)求應(yīng)答給生產(chǎn)者了。
偏移量咽安,最高水位伴网,復(fù)制點(diǎn)
備份副本向主副本同步數(shù)據(jù)過程中,備份副本自己會(huì)更新本地的日志偏移量妆棒,主副本所在的服務(wù)端也會(huì)更新對(duì)應(yīng)備份副本的偏移量和最高水位澡腾。
具體如下:
1,生產(chǎn)者向主副本發(fā)送消息集糕珊,主副本的偏移量增加动分,初始時(shí)所有的副本的最高水位都是0.
2,備份副本拉取到數(shù)據(jù)红选,更新本地的偏移量澜公。拉取響應(yīng)帶有主副本的最高水位,但是主副本的最高水位還是0喇肋,因此備份副本的最高水位也是0坟乾。
3,備份副本再次拉取數(shù)據(jù)蝶防,會(huì)更新主副本的最高水位甚侣。主副本返回給備份副本的拉取響應(yīng)中包含最新的最高水位。
4间学,備份副本拉取到數(shù)據(jù)殷费,更新本地的偏移量,也會(huì)更新備份副本的最高水位菱鸥。
通過以上的操作宗兼,主副本記錄的備份副本的偏移量與備份副本自己記錄的偏移量是一致的。
更新副本偏移量:
1氮采,追加消息到主副本的本地日志 殷绍、備份副本拉取消息寫到自己的本地日志,都會(huì)更新日志的偏移量鹊漠。
2主到,主副本所在的服務(wù)端處理備份副本的拉取請(qǐng)求茶行,也會(huì)更新分區(qū)中備份副本對(duì)應(yīng)的偏移量。
更新副本最高水位:
1登钥,主副本的最高水位取決于ISR中所有副本的最小偏移量畔师。最小值沒有變化,最高水位也不會(huì)變化牧牢。
2看锉,備份副本的最高水位取決于主副本的最高水位和它自己的偏移量,它會(huì)選擇這兩者的最小值塔鳍。
日志管理器會(huì)定時(shí)將所有分區(qū)的副本偏移量伯铣,刷寫到恢復(fù)點(diǎn)檢查點(diǎn)文件;副本管理器會(huì)定時(shí)將分區(qū)副本的最高水位轮纫,刷寫到復(fù)制點(diǎn)文件(最高水位檢查點(diǎn)文件)腔寡。
同一個(gè)分區(qū)在不同的消息代理節(jié)點(diǎn)上,它們的本地副本都有偏移量和最高水位掌唾。主副本所在的節(jié)點(diǎn)會(huì)記錄所有副本的偏移量和主副本的最高水位放前。備份副本所在的節(jié)點(diǎn)只會(huì)記錄自己的偏移量和最高水位,不會(huì)記錄其他副本的偏移量和最高水位糯彬。
消費(fèi)者客戶端最多只會(huì)讀取到主副本的最高水位凭语。但因?yàn)橹鞲北究赡軙?huì)出現(xiàn)故障,所以備份副本也需要記錄最高水位情连。當(dāng)主副本出現(xiàn)故障時(shí)叽粹,備份副本成為主副本览效,它的最高水位和之前主副本的最高水位保持一致却舀,消費(fèi)者客戶端就不會(huì)丟失數(shù)據(jù)。
參考資料:
Kafka技術(shù)內(nèi)幕:圖文詳解Kafka源碼設(shè)計(jì)與實(shí)現(xiàn)