1.Kafka的topic和分區內部是如何存儲的,有什麼特色?spring
2.與傳統的消息系統相比,Kafka的消費模型有什麼優勢?性能優化
3.Kafka如何實現分佈式的數據存儲與數據讀取?服務器
1.kafka名詞解釋微信
在一套kafka架構中有多個Producer,多個Broker,多個Consumer,每一個Producer能夠對應多個Topic,每一個Consumer只能對應一個ConsumerGroup。網絡
整個Kafka架構對應一個ZK集羣,經過ZK管理集羣配置,選舉Leader,以及在consumer group發生變化時進行rebalance。多線程
名稱架構
解釋併發
Brokerapp
消息中間件處理節點,一個Kafka節點就是一個broker,一個或者多個Broker能夠組成一個Kafka集羣分佈式
Topic
主題,Kafka根據topic對消息進行歸類,發佈到Kafka集羣的每條消息都須要指定一個topic
Producer
消息生產者,向Broker發送消息的客戶端
Consumer
消息消費者,從Broker讀取消息的客戶端
ConsumerGroup
每一個Consumer屬於一個特定的Consumer Group,一條消息能夠發送到多個不一樣的Consumer Group,可是一個Consumer Group中只能有一個Consumer可以消費該消息
Partition
物理上的概念,一個topic能夠分爲多個partition,每一個partition內部是有序的
2.Topic和Partition
在Kafka中的每一條消息都有一個topic。通常來講在咱們應用中產生不一樣類型的數據,均可以設置不一樣的主題。一個主題通常會有多個消息的訂閱者,當生產者發佈消息到某個主題時,訂閱了這個主題的消費者均可以接收到生產者寫入的新消息。
kafka爲每一個主題維護了分佈式的分區(partition)日誌文件,每一個partition在kafka存儲層面是append log。任何發佈到此partition的消息都會被追加到log文件的尾部,在分區中的每條消息都會按照時間順序分配到一個單調遞增的順序編號,也就是咱們的offset,offset是一個long型的數字,咱們經過這個offset能夠肯定一條在該partition下的惟一消息。在partition下面是保證了有序性,可是在topic下面沒有保證有序性。
在上圖中在咱們的生產者會決定發送到哪一個Partition。
1.若是沒有Key值則進行輪詢發送。
2.若是有Key值,對Key值進行Hash,而後對分區數量取餘,保證了同一個Key值的會被路由到同一個分區,若是想隊列的強順序一致性,可讓全部的消息都設置爲同一個Key。
3.消費模型
消息由生產者發送到kafka集羣后,會被消費者消費。通常來講咱們的消費模型有兩種:推送模型(psuh)和拉取模型(pull)
基於推送模型的消息系統,由消息代理記錄消費狀態。消息代理將消息推送到消費者後,標記這條消息爲已經被消費,可是這種方式沒法很好地保證消費的處理語義。好比當咱們把已經把消息發送給消費者以後,因爲消費進程掛掉或者因爲網絡緣由沒有收到這條消息,若是咱們在消費代理將其標記爲已消費,這個消息就永久丟失了。若是咱們利用生產者收到消息後回覆這種方法,消息代理須要記錄消費狀態,這種不可取。若是採用push,消息消費的速率就徹底由消費代理控制,一旦消費者發生阻塞,就會出現問題。
Kafka採起拉取模型(poll),由本身控制消費速度,以及消費的進度,消費者能夠按照任意的偏移量進行消費。好比消費者能夠消費已經消費過的消息進行從新處理,或者消費最近的消息等等。
4.網絡模型
4.1 KafkaClient --單線程Selector
單線程模式適用於併發連接數小,邏輯簡單,數據量小。
在kafka中,consumer和producer都是使用的上面的單線程模式。這種模式不適合kafka的服務端,在服務端中請求處理過程比較複雜,會形成線程阻塞,一旦出現後續請求就會沒法處理,會形成大量請求超時,引發雪崩。而在服務器中應該充分利用多線程來處理執行邏輯。
4.2 Kafka--server -- 多線程Selector
在kafka服務端採用的是多線程的Selector模型,Acceptor運行在一個單獨的線程中,對於讀取操做的線程池中的線程都會在selector註冊read事件,負責服務端讀取請求的邏輯。成功讀取後,將請求放入message queue共享隊列中。而後在寫線程池中,取出這個請求,對其進行邏輯處理,即便某個請求線程阻塞了,還有後續的縣城從消息隊列中獲取請求並進行處理,在寫線程中處理完邏輯處理,因爲註冊了OP_WIRTE事件,因此還須要對其發送響應。
5.高可靠分佈式存儲模型
在Kafka中保證高可靠模型的依靠的是副本機制,有了副本機制以後,就算機器宕機也不會發生數據丟失。
5.1高性能的日誌存儲
kafka一個topic下面的全部消息都是以partition的方式分佈式的存儲在多個節點上。同時在kafka的機器上,每一個Partition其實都會對應一個日誌目錄,在目錄下面會對應多個日誌分段(LogSegment)。LogSegment文件由兩部分組成,分別爲「.index」文件和「.log」文件,分別表示爲segment索引文件和數據文件。這兩個文件的命令規則爲:partition全局的第一個segment從0開始,後續每一個segment文件名爲上一個segment文件最後一條消息的offset值,數值大小爲64位,20位數字字符長度,沒有數字用0填充,以下,假設有1000條消息,每一個LogSegment大小爲100,下面展示了900-1000的索引和Log:
因爲kafka消息數據太大,若是所有創建索引,即佔了空間又增長了耗時,因此kafka選擇了稀疏索引的方式,這樣的話索引能夠直接進入內存,加快偏查詢速度。
簡單介紹一下如何讀取數據,若是咱們要讀取第911條數據首先第一步,找到他是屬於哪一段的,根據二分法查找到他屬於的文件,找到0000900.index和00000900.log以後,而後去index中去查找 (911-900) =11這個索引或者小於11最近的索引,在這裏經過二分法咱們找到了索引是[10,1367]而後咱們經過這條索引的物理位置1367,開始日後找,直到找到911條數據。
上面講的是若是要找某個offset的流程,可是咱們大多數時候並不須要查找某個offset,只須要按照順序讀便可,而在順序讀中,操做系統會對內存和磁盤之間添加page cahe,也就是咱們日常見到的預讀操做,因此咱們的順序讀操做時速度很快。可是kafka有個問題,若是分區過多,那麼日誌分段也會不少,寫的時候因爲是批量寫,其實就會變成隨機寫了,隨機I/O這個時候對性能影響很大。因此通常來講Kafka不能有太多的partition。針對這一點,RocketMQ把全部的日誌都寫在一個文件裏面,就能變成順序寫,經過必定優化,讀也能接近於順序讀。
能夠思考一下:1.爲何須要分區,也就是說主題只有一個分區,難道不行嗎?2.日誌爲何須要分段
5.2副本機制
Kafka的副本機制是多個服務端節點對其餘節點的主題分區的日誌進行復制。當集羣中的某個節點出現故障,訪問故障節點的請求會被轉移到其餘正常節點(這一過程一般叫Reblance),kafka每一個主題的每一個分區都有一個主副本以及0個或者多個副本,副本保持和主副本的數據同步,當主副本出故障時就會被替代。
在Kafka中並非全部的副本都能被拿來替代主副本,因此在kafka的leader節點中維護着一個ISR(In sync Replicas)集合,翻譯過來也叫正在同步中集合,在這個集合中的須要知足兩個條件:
另外還有個AR(Assigned Replicas)用來標識副本的全集,OSR用來表示因爲落後被剔除的副本集合,因此公式以下:ISR = leader + 沒有落後太多的副本; AR = OSR+ ISR;
這裏先要說下兩個名詞:HW(高水位)是consumer可以看到的此partition的位置,LEO是每一個partition的log最後一條Message的位置。HW能保證leader所在的broker失效,該消息仍然能夠重新選舉的leader中獲取,不會形成消息丟失。
當producer向leader發送數據時,能夠經過request.required.acks參數來設置數據可靠性的級別:
注:關注做者微信公衆號,瞭解更多分佈式架構、微服務、netty、MySQL、spring、JVM、性能優化、等知識點。
公衆號:《 Java大蝸牛 》