一、KAFKA介紹
Kafka是一種分布式的忍燥,基于發(fā)布/訂閱的消系統(tǒng)拧晕。kafka對(duì)消息保存時(shí)根據(jù)Topic進(jìn)行歸類,發(fā)送消息者成為Producer,消息接受者成為Consumer,此外kafka集群有多個(gè)kafka實(shí)例組成梅垄,每個(gè)實(shí)例(server)稱為broker厂捞。無(wú)論是kafka集群,還是producer和consumer都依賴于zookeeper來(lái)保證系統(tǒng)可用性集群保存一些meta信息。
二靡馁、名詞解釋
Broker:Kafka集群包含一個(gè)或多個(gè)服務(wù)器欲鹏,這種服務(wù)器被稱為broker
Topic:每條發(fā)布到Kafka集群的消息都有一個(gè)類別,這個(gè)類別被稱為Topic臭墨。(物理上不同Topic的消息分開存儲(chǔ)赔嚎,邏輯上一個(gè)Topic的消息雖然保存于一個(gè)或多個(gè)broker上但用戶只需指定消息的Topic即可生產(chǎn)或消費(fèi)數(shù)據(jù)而不必關(guān)心數(shù)據(jù)存于何處)
Partition:Topic物理上的分組,一個(gè)topic可以分為多個(gè)partition胧弛,每個(gè)partition是一個(gè)有序的隊(duì)列尤误。partition中的每條消息都會(huì)被分配一個(gè)有序的id(offset)。
Producer:負(fù)責(zé)發(fā)布消息到Kafka broker
Consumer:消息消費(fèi)者叶圃,向Kafka broker讀取消息的客戶端袄膏。
Consumer Group:每個(gè)Consumer屬于一個(gè)特定的Consumer Group(可為每個(gè)Consumer指定group name,若不指定group name則屬于默認(rèn)的group)掺冠。
三、Kafka架構(gòu)
Kafka的整體架構(gòu)非常簡(jiǎn)單码党,是顯式分布式架構(gòu)德崭,producer、broker(kafka)和consumer都可以有多個(gè)揖盘。Producer眉厨,consumer實(shí)現(xiàn)Kafka注冊(cè)的接口,數(shù)據(jù)從producer發(fā)送到broker兽狭,broker承擔(dān)一個(gè)中間緩存和分發(fā)的作用憾股。broker分發(fā)注冊(cè)到系統(tǒng)中的consumer。broker的作用類似于緩存箕慧,即活躍的數(shù)據(jù)和離線處理系統(tǒng)之間的緩存服球。客戶端和服務(wù)器端的通信颠焦,是基于簡(jiǎn)單斩熊,高性能,且與編程語(yǔ)言無(wú)關(guān)的TCP協(xié)議伐庭。
四粉渠、Kafka的存儲(chǔ)
Topic在邏輯上可以被認(rèn)為是一個(gè)queue,每條消費(fèi)都必須指定它的Topic圾另,可以簡(jiǎn)單理解為必須指明把這條消息放進(jìn)哪個(gè)queue里霸株。為了使得Kafka的吞吐率可以線性提高,物理上把Topic分成一個(gè)或多個(gè)Partition集乔,每個(gè)Partition在物理上對(duì)應(yīng)一個(gè)文件夾去件,該文件夾下存儲(chǔ)這個(gè)Partition的所有消息和索引文件。若創(chuàng)建topic1和topic2兩個(gè)topic,且分別有13個(gè)和19個(gè)分區(qū)箫攀,則整個(gè)集群上會(huì)相應(yīng)會(huì)生成共32個(gè)文件夾肠牲。Producer集群通過(guò)zookeeper(實(shí)際中寫的是broker list)獲取所寫topic對(duì)應(yīng)的partition列表,然后順序發(fā)送消息(支持自己實(shí)現(xiàn)分發(fā)策略)靴跛,broker集群負(fù)責(zé)消息的存儲(chǔ)和傳遞缀雳,支持Master Slaver模型,可分布式擴(kuò)展梢睛;Consumer集群從zookeeper上獲取topic所在的partition列表肥印,然后消費(fèi),一個(gè)partition只能被一個(gè)consumer消費(fèi)绝葡。
五深碱、Partition的數(shù)據(jù)文件
如果客戶端不指定Patition,也沒(méi)有指定Key的話藏畅,使用自增長(zhǎng)的數(shù)字取余數(shù)的方式實(shí)現(xiàn)指定的Partition敷硅。這樣Kafka將平均的向Partition中生產(chǎn)數(shù)據(jù)。kafka只能保證分區(qū)內(nèi)的有序性愉阎。
Partition中的每條Message由offset來(lái)表示它在這個(gè)partition中的偏移量绞蹦,這個(gè)offset不是該Message在partition數(shù)據(jù)文件中的實(shí)際存儲(chǔ)位置,而是邏輯上一個(gè)值榜旦,它唯一確定了partition中的一條Message幽七。因此,可以認(rèn)為offset是partition中Message的id溅呢。partition中的每條Message包含了以下三個(gè)屬性:
Offset澡屡、MessageSize、data
其中offset為long型咐旧,MessageSize為int32驶鹉,表示data有多大,data為message的具體內(nèi)容休偶。它的格式和Kafka通訊協(xié)議中介紹的MessageSet格式是一致梁厉。
它的主要方法如下:
append: 把給定的ByteBufferMessageSet中的Message寫入到這個(gè)數(shù)據(jù)文件中。
searchFor: 從指定的startingPosition開始搜索找到第一個(gè)Message其offset是大于或者等于指定的offset踏兜,并返回其在文件中的位置Position词顾。它的實(shí)現(xiàn)方式是從startingPosition開始讀取12個(gè)字節(jié),分別是當(dāng)前MessageSet的offset和size碱妆。如果當(dāng)前offset小于指定的offset肉盹,那么將position向后移動(dòng)LogOverHead+MessageSize(其中LogOverHead為offset+messagesize,為12個(gè)字節(jié))疹尾。
read:準(zhǔn)確名字應(yīng)該是slice上忍,它截取其中一部分返回一個(gè)新的FileMessageSet骤肛。它不保證截取的位置數(shù)據(jù)的完整性。
sizeInBytes: 表示這個(gè)FileMessageSet占有了多少字節(jié)的空間窍蓝。
truncateTo: 把這個(gè)文件截?cái)嘁傅撸@個(gè)方法不保證截?cái)辔恢玫腗essage的完整性。
readInto: 從指定的相對(duì)位置開始把文件的內(nèi)容讀取到對(duì)應(yīng)的ByteBuffer中吓笙。
partition是分段的淑玫,每個(gè)段叫LogSegment,包括了一個(gè)數(shù)據(jù)文件和一個(gè)索引文件面睛,下圖是某個(gè)partition目錄下的文件:
index文件中并沒(méi)有為數(shù)據(jù)文件中的每條Message建立索引絮蒿,而是采用了稀疏存儲(chǔ)的方式,每隔一定字節(jié)的數(shù)據(jù)建立一條索引叁鉴。這樣避免了索引文件占用過(guò)多的空間土涝,從而可以將索引文件保留在內(nèi)存中。但缺點(diǎn)是沒(méi)有建立索引的Message也不能一次定位到其在數(shù)據(jù)文件的位置幌墓,從而需要做一次順序掃描但壮,但是這次順序掃描的范圍就很小了。
Partition設(shè)置:
分區(qū)多吞吐量更高
一個(gè)話題topic的各個(gè)分區(qū)partiton之間是并行的克锣。在producer和broker方面茵肃,寫不同的分區(qū)是完全并行的。因此一些昂貴的操作比如壓縮袭祟,可以獲得更多的資源,因?yàn)橛卸鄠€(gè)進(jìn)程捞附。在consumer方面巾乳,一個(gè)分區(qū)的數(shù)據(jù)可以由一個(gè)consumer線程在拉去數(shù)據(jù)。分區(qū)多鸟召,并行的consumer(同一個(gè)消費(fèi)組)也可以多胆绊。因此通常,分區(qū)越多吞吐量越高欧募。
基于吞吐量可以獲得一個(gè)粗略的計(jì)算公式压状。先測(cè)量得到在只有一個(gè)分區(qū)的情況下,Producer的吞吐量(P)和Consumer的吞吐量(C)跟继。那如果總的目標(biāo)吞吐量是T的話种冬,max(T/P,T/C)就是需要的最小分區(qū)數(shù)
分區(qū)多需要的打開的文件句柄也多
每個(gè)分區(qū)都映射到broker上的一個(gè)目錄,每個(gè)log片段都會(huì)有兩個(gè)文件(一個(gè)是索引文件舔糖,另一個(gè)是實(shí)際的數(shù)據(jù)文件)娱两。分區(qū)越多所需要的文件句柄也就越多,可以通過(guò)配置操作系統(tǒng)的參數(shù)增加打開文件句柄數(shù)金吗。
分區(qū)多增加了不可用風(fēng)險(xiǎn)
kafka支持主備復(fù)制十兢,具備更高的可用性和持久性趣竣。一個(gè)分區(qū)(partition)可以有多個(gè)副本,這些副本保存在不同的broker上旱物。每個(gè)分區(qū)的副本中都會(huì)有一個(gè)作為L(zhǎng)eader遥缕。當(dāng)一個(gè)broker失敗時(shí),Leader在這臺(tái)broker上的分區(qū)都會(huì)變得不可用宵呛,kafka會(huì)自動(dòng)移除Leader单匣,再其他副本中選一個(gè)作為新的Leader。Producer和Consumer都只會(huì)與Leader相連烤蜕。
一般情況下封孙,當(dāng)一個(gè)broker被正常關(guān)機(jī)時(shí),controller主動(dòng)地將Leader從正在關(guān)機(jī)的broker上移除讽营。移動(dòng)一個(gè)Leader只需要幾毫秒虎忌。然當(dāng)broker出現(xiàn)異常導(dǎo)致關(guān)機(jī)時(shí),不可用會(huì)與分區(qū)數(shù)成正比橱鹏。假設(shè)一個(gè)boker上有2000個(gè)分區(qū)膜蠢,每個(gè)分區(qū)有2個(gè)副本,那這樣一個(gè)boker大約有1000個(gè)Leader莉兰,當(dāng)boker異常宕機(jī)挑围,會(huì)同時(shí)有1000個(gè)分區(qū)變得不可用。假設(shè)恢復(fù)一個(gè)分區(qū)需要5ms糖荒,1000個(gè)分區(qū)就要5s杉辙。
分區(qū)越多,在broker異常宕機(jī)的情況捶朵,恢復(fù)所需時(shí)間會(huì)越長(zhǎng)蜘矢,不可用風(fēng)險(xiǎn)會(huì)增加。
六综看、Consumers分組
發(fā)布-訂閱模式中消息被廣播到所有的consumer中品腹。Consumers可以加入一個(gè)consumer 組,共同競(jìng)爭(zhēng)一個(gè)topic红碑,topic中的消息將被分發(fā)到組中的一個(gè)成員中舞吭。同一組中的consumer可以在不同的程序中析珊,也可以在不同的機(jī)器上。如果所有的consumer都在一個(gè)組中唾琼,這就成為了傳統(tǒng)的隊(duì)列模式,在各consumer中實(shí)現(xiàn)負(fù)載均衡锡溯。如果所有的consumer都不在不同的組中赶舆,這就成為了發(fā)布-訂閱模式哑姚,所有的消息都被分發(fā)到所有的consumer中芜茵。更常見(jiàn)的是,每個(gè)topic都有若干數(shù)量的consumer組九串,每個(gè)組都是一個(gè)邏輯上的“訂閱者”绞佩,為了容錯(cuò)和更好的穩(wěn)定性,每個(gè)組由若干consumer組成猪钮。這其實(shí)就是一個(gè)發(fā)布-訂閱模式品山,只不過(guò)訂閱者是個(gè)組而不是單個(gè)consumer烤低。
在kafka中,一個(gè)partition中的消息只會(huì)被group中的一個(gè)consumer消費(fèi);每個(gè)group中consumer消息消費(fèi)互相獨(dú)立;我們可以認(rèn)為一個(gè)group是一個(gè)"訂閱"者,一個(gè)Topic中的每個(gè)partions,只會(huì)被一個(gè)"訂閱者"中的一個(gè)consumer消費(fèi),不過(guò)一個(gè)consumer可以消費(fèi)多個(gè)partitions中的消息.kafka只能保證一個(gè)partition中的消息被某個(gè)consumer消費(fèi)時(shí),消息是順序的.事實(shí)上,從Topic角度來(lái)說(shuō),消息仍不是有序的。
kafka的設(shè)計(jì)原理決定,對(duì)于一個(gè)topic,同一個(gè)group中不能有多于partitions個(gè)數(shù)的consumer同時(shí)消費(fèi),否則將意味著某些consumer將無(wú)法得到消息涯呻。