<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          讀文筆記:Kafka 官方設(shè)計文檔

          共 6952字,需瀏覽 14分鐘

           ·

          2021-10-21 07:16


          原文:
          http://kafka.apache.org/documentation/#design


          數(shù)據(jù)持久化

          不用懼怕文件系統(tǒng)

          磁盤的讀寫速度,取決于如何讀寫。對于線性讀寫方式,操作系統(tǒng)做了充分的優(yōu)化:提前讀 - 預(yù)取若干數(shù)據(jù)塊,滯后寫 - 將小的邏輯寫操作合并成一個大的物理寫操作。
          研究表明:順序讀寫磁盤(sequential disk access)的速度有些時候比隨機(jī)訪問內(nèi)存還要快。
          現(xiàn)代操作系統(tǒng)激進(jìn)地盡可能將空閑內(nèi)存用作磁盤緩存。所有磁盤讀寫都經(jīng)過操作系統(tǒng)提供的統(tǒng)一緩存。這個特性沒法輕易關(guān)閉,除非直接 I/O (direct I/O),因此,如果程序在用戶進(jìn)程中進(jìn)行數(shù)據(jù)緩存,緩存的數(shù)據(jù)通常也是和操作系統(tǒng)頁緩存重復(fù)的,緩存兩遍,沒啥意義,也浪費(fèi)內(nèi)存。
          而且,Kafka 是構(gòu)建在 JVM 之上的,了解 Java 內(nèi)存使用方式的人應(yīng)該都知道:
          1. 對象的內(nèi)存開銷非常高,通常是實際數(shù)據(jù)大小的2倍(甚至更多)
          2. 隨著堆上數(shù)據(jù)量增大,Java 的 GC 表現(xiàn)也會更糟糕
          因此,使用文件系統(tǒng)并依賴于操作系統(tǒng)內(nèi)存頁緩存,優(yōu)于在程序中維護(hù)一塊內(nèi)存緩存或其它結(jié)構(gòu)。至少操作系統(tǒng)內(nèi)存頁緩存的可用內(nèi)存翻倍了。另外,如果使用緊湊的字節(jié)結(jié)構(gòu)來緩存數(shù)據(jù),相比使用對象,可用內(nèi)存可能還會翻倍。在 32GB 內(nèi)存的機(jī)器上這么搞,緩存可用到 20-30GB,還不會對 GC 造成了什么壞影響。并且,即使服務(wù)重啟,這塊緩存空間也是熱的(除非機(jī)器重啟),用戶進(jìn)程內(nèi)的內(nèi)存緩存在服務(wù)重啟后得重建(10GB的數(shù)據(jù)緩存可能需要10分鐘左右)。
          這樣也可以簡化代碼邏輯,因為緩存和文件系統(tǒng)之間的一致性由操作系統(tǒng)來保證了。
          這樣一分析,設(shè)計就簡單了:我們反其道而行之,所有數(shù)據(jù)都直接寫到文件系統(tǒng)上持久化日志文件中,不需要在程序中使用內(nèi)存緩存,也不必確保將數(shù)據(jù)刷到磁盤。這實際意味著數(shù)據(jù)轉(zhuǎn)移到了內(nèi)核的內(nèi)存頁緩存。

          常量時間就能搞定

          B 樹的 O(log N) 時間復(fù)雜度,對于磁盤操作來說,并不能等同于常量時間復(fù)雜度。
          Kafka 采用日志文件方式,確保讀寫操作的時間復(fù)雜度是 O(1)。
          Kafka 不會在消息一被消費(fèi)就立即刪除,而是保留一段時間,這樣對于消費(fèi)者來說也更靈活一些。

          效率

          對于 Kafka 這類系統(tǒng)而言,即使像前述那樣消除了糟糕的磁盤訪問模式,也會遇到兩個導(dǎo)致數(shù)據(jù)效率低的問題:過多的小 I/O 操作,以及過多的字節(jié)拷貝。
          小 I/O 問題在客戶端與服務(wù)端之間,以及服務(wù)端內(nèi)部的數(shù)據(jù)持久化操作中都會發(fā)生。對此,Kafka 協(xié)議建立在 “消息集” (即一批消息)的抽象之上,這樣網(wǎng)絡(luò)請求讀寫的是一批一批的消息,減少了網(wǎng)絡(luò)往返的時間開銷(注:消息處理的實時性會相對差一點)。服務(wù)端也是一次將一批消息寫到日志文件中,消費(fèi)者也按序一次獲取一批消息。這一簡單的優(yōu)化可以將吞吐能力提升幾個數(shù)量級。
          對于過多的字節(jié)拷貝問題,在消息量大的時候,影響比較明顯。Kafka 采用了一種標(biāo)準(zhǔn)化的二進(jìn)制消息格式,producer、broker、consumer 都使用這種格式,這樣數(shù)據(jù)塊在傳輸期間不需要變動。
          broker 維護(hù)的消息日志只是一個目錄下的一堆文件,文件內(nèi)容是按序?qū)懭氲南⒓?,消息集的?shù)據(jù)格式同于 producer、consumer 使用的。共用一種數(shù)據(jù)格式方便了一個重要的操作優(yōu)化:持久化日志塊的網(wǎng)絡(luò)傳輸。對于從內(nèi)存頁緩存(pagecache)到網(wǎng)絡(luò)套接字(socket)的數(shù)據(jù)傳輸操作,現(xiàn)代 UNIX 操作系統(tǒng)提供了一種高度優(yōu)化的代碼執(zhí)行路徑。Linux 中使用 sendfile 系統(tǒng)調(diào)用?可以利用這個優(yōu)化。
          要理解 sendfile 的收益,需要先理解從文件到套接字傳輸數(shù)據(jù)的常規(guī)代碼執(zhí)行路徑:
          1. 操作系統(tǒng)從磁盤將數(shù)據(jù)讀到內(nèi)核空間的內(nèi)存頁緩存(pagecache)
          2. 應(yīng)用程序從內(nèi)核空間將數(shù)據(jù)讀到用戶空間緩沖區(qū)
          3. 應(yīng)用程序?qū)?shù)據(jù)從用戶空間緩沖區(qū)讀到內(nèi)核空間的套接字緩沖區(qū)
          4. 操作系統(tǒng)將數(shù)據(jù)從套接字緩沖區(qū)讀到 NIC 緩沖區(qū),網(wǎng)卡從 NIC 緩沖區(qū)讀取數(shù)據(jù)通過網(wǎng)絡(luò)發(fā)出去
          這一代碼執(zhí)行路徑,涉及 4 次數(shù)據(jù)拷貝和 2 次系統(tǒng)調(diào)用,很顯然是低效的。使用 sendfile,可以避免內(nèi)核空間和用戶空間之間一些不必要的數(shù)據(jù)拷貝,操作系統(tǒng)可以直接將數(shù)據(jù)從內(nèi)存頁緩存發(fā)送到網(wǎng)絡(luò)。
          進(jìn)一步了解 sendfile 以及 Java 平臺如何支持零拷貝,可以閱讀這篇文章(https://developer.ibm.com/articles/j-zerocopy/)。

          生產(chǎn)者(The Producer)

          負(fù)載均衡

          消息應(yīng)該發(fā)到哪個分區(qū)(partition)由客戶端根據(jù)哈希算法(或者隨機(jī))決定,并且消息是直接由 producer 發(fā)到目標(biāo)分區(qū)的 leader broker,沒有任何中間路由層。
          所有 Kafka 節(jié)點都可以響應(yīng)元數(shù)據(jù)請求 - 告知客戶端(producer 或 consumer)哪些服務(wù)節(jié)點還存活以及某個 topic 的各個分區(qū) leader 分別是哪個節(jié)點(疑惑:如果某個分區(qū) leader 節(jié)點掛掉之后,客戶端如何獲知?何時可以獲知?)

          消息交付語義

          producer 和 consumer 之間的消息交付語義,分 3 種:
          1. 最多消費(fèi)一次 - 消息可能會丟失,但不會被重復(fù)消費(fèi)

          2. 最少消費(fèi)一次 - 消息不會丟,但可能被重復(fù)消費(fèi)

          3. 僅消費(fèi)一次 - 每個消息都會被消費(fèi)且僅消費(fèi)一次


          這個問題可以分成兩個階段的問題:producer 向 broker 發(fā)布一個消息時的持久性保證?以及?consumer 消費(fèi)一個消息時的語義保證?(the durability guarantees for publishing a message and the guarantees when consuming a message)。
          producer 向 Kafka 集群發(fā)消息時,會提供一個請求參數(shù)?acks
          1. acks=0:表示 producer 不需要等分區(qū) leader broker 返回任何響應(yīng),將消息存入套接字緩沖區(qū)(socket buffer)就當(dāng)做消息已經(jīng)發(fā)送成功。所以可靠性是沒有保證的。
          2. acks=1:表示 分區(qū) leader broker 將消息寫入自己的本地日志文件,就向 producer 響應(yīng)成功,不必等待分區(qū)副本 broker 同步好消息。
          3. acks=-1 或 acks=all:表示 分區(qū) leader broker 需要等待所有同步副本 broker 同步好消息并響應(yīng)成功,才向 producer 響應(yīng)成功
          第 2 種情況,如果分區(qū) leader broker 掛掉/不存活,則副本未來得及同步的消息會丟失。
          第 3 種情況,只要有同步副本正常同步消息,那么即使 leader 掛了也不會丟數(shù)據(jù)。
          如果 leader 被系統(tǒng)判定為不存活,則會從(同步)副本中選舉一個新的 leader,那么 Kafka 如何判定一個節(jié)點是否存活?存活判定依賴 2 個條件:
          1. 節(jié)點必須維持與 Zookeeper 的 session 連接(通過 Zookeeper 的心跳機(jī)制)
          2. 如果是一個從節(jié)點(follower),則必須不斷從 leader 節(jié)點同步消息數(shù)據(jù),且同步進(jìn)度沒有落后太多
          如果 producer 在發(fā)送消息的過程中發(fā)生網(wǎng)絡(luò)問題,它沒法判定分區(qū) leader 是否收到消息。0.11.0.0 版本之前,producer 只能重發(fā)消息,別無他法,因此只能提供“最少消費(fèi)一次的”交付語義。0.11.0.0 版本之后,Kafka producer 支持一個冪等交付功能選項,可以確保消息重發(fā)不會導(dǎo)致 Kafka 的消息日志中出現(xiàn)重復(fù)的條目:broker 為每個 producer 分配一個 ID,然后基于消息序號來去重。
          也是從 0.11.0.0 版本開始,Producer 支持以類事務(wù)的語義向多個 topic 分區(qū)發(fā)送消息:要么所有消息都發(fā)送成功,要么都不成功。這個能力主要用于實現(xiàn) Kafka topic 之間的僅處理一次語義。
          從 consumer 角度來看,同一個分區(qū)的所有副本,日志數(shù)據(jù)相同,消費(fèi)進(jìn)度也一樣。consumer 可以控制自己對分區(qū)日志數(shù)據(jù)的消費(fèi)位置。
          1. 如果 consumer 讀取消息后,先向 kafka 提交消費(fèi)位置,再處理消息;如果該 consumer 掛掉或重啟,會可能導(dǎo)致丟消息,從而只能滿足“最多處理一次”交付語義。
          2. 如果 consumer 讀取消息后,是先處理,再提交消費(fèi)位置;如果該 consumer 掛掉或重啟,則可能導(dǎo)致重復(fù)消費(fèi)消息,從而只能滿足“最少處理一次”交付語義。
          如何實現(xiàn)“僅處理一次”語義?借助 Producer 的事務(wù)能力。

          復(fù)制

          復(fù)制的粒度/單元是 topic 分區(qū)。Kafka 集群中,每個分區(qū)都有一個 leader broker 節(jié)點,0個或多個從節(jié)點(follower)。分區(qū)讀寫都是由 leader broker 處理。
          如同一個普通的 consumer,從節(jié)點從 leader broker 拉?。╬ull)消息,然后寫到自己的消息日志文件中。讓從節(jié)點以 pull 的方式獲取 leader 的消息數(shù)據(jù),好處在于批量讀寫。
          對于 follower 節(jié)點而言,“是否存活”的實際含義是“是否順利地從 leader 同步消息”,leader 節(jié)點會追蹤“同步中”節(jié)點集(ISRs)。如果一個 follower 掛掉了/卡住了/同步落后太多了,則將其從這個 ISRs 中移除。follow 是否卡住或者同步落后太多,依據(jù)?replica.lag.time.max.ms?配置參數(shù)判定。
          將某消息寫到某個分區(qū),如果該分區(qū)所有同步中副本都已經(jīng)將該消息寫到自己的消息日志文件中,則可以認(rèn)為該消息的寫操作已提交(committed),也就是真正的寫成功。
          只有寫提交的消息才會分發(fā)給 consumer。
          producer 可以選擇是否等待消息寫操作提交,在延遲(latency)和持久性(durability)之間權(quán)衡。
          Kafka 集群在某分區(qū)的 leader 節(jié)點掛掉之后,會快速進(jìn)行失敗轉(zhuǎn)移(a short fail-over period),選舉出新的分區(qū) leader 節(jié)點,可用性不會受到影響。但如果發(fā)生網(wǎng)絡(luò)分區(qū)(network partitions)問題,則無法保證可用性。CAP - C(Consistency):一致性,A(Availability):可用性,P(Partition Tolerance):分區(qū)容錯性 - 放棄了 分區(qū)容錯性。

          日志數(shù)據(jù)復(fù)制:仲裁成員集(Quorums)、同步中副本集(ISRs)和狀態(tài)機(jī)

          (備注:這一節(jié)我理解得還不太透徹。)

          一類常見的分布式系統(tǒng)是主從模式的,由主節(jié)點決定狀態(tài)變化的順序(the order of a series of values)。從節(jié)點通過日志復(fù)制(replicated log)方式同步狀態(tài)數(shù)據(jù)。對于提交決策(commit decision)和選主(leader election),通常是基于多數(shù)人投票的機(jī)制。假設(shè)副本個數(shù)(注:個人理解包含主節(jié)點)為 2f+1,那么只有當(dāng) f+1 個副本寫入成功,主節(jié)點才會將這個寫操作標(biāo)記為已提交(committed)。當(dāng)主節(jié)點掛掉之后,基于 f 個狀態(tài)最新的副本節(jié)點,可以選舉出新的主節(jié)點,且狀態(tài)不會有任何丟失。
          多數(shù)人投票方式,有一個優(yōu)點:延遲取決于速度快的節(jié)點,而不是慢的。缺點是:對于實際的生產(chǎn)系統(tǒng),抗風(fēng)險能力還不夠,而且不夠靈活,不能讓使用者做權(quán)衡。
          Kafka 選擇仲裁成員集(quorum set)的方式與此不同,而不是基于多數(shù)人投票,而是動態(tài)維護(hù)一組同步中副本(ISR),這些副本與主節(jié)點保持同步。只有這組副本中的成員才有資格當(dāng)選為主節(jié)點。ISR 集發(fā)生變化時會持久化到 Zookeeper 上。
          基于 ISR 模型,如果 topic 分區(qū)有 f+1 個副本,則可以容忍 f 個節(jié)點掛掉,也不會丟失任何已提交的消息。
          與 Kafka ISR 模型實際實現(xiàn)最相近的學(xué)術(shù)論文是微軟的 PacificA(http://research.microsoft.com/apps/pubs/default.aspx?id=66814)。

          可用性和持久性保證

          注意:producer 發(fā)送消息時設(shè)定?acks=all?并不是要求所有的副本都確認(rèn)寫入成功,而是在當(dāng)前同步中副本(ISR)都確認(rèn)寫入成功時,分區(qū) leader 就向 producer 響應(yīng)成功。例如:某個 topic 被設(shè)置為 2 個副本,然后其中一個副本節(jié)點掛掉,此時要求?acks=all?的寫操作也會成功。如果剩下的副本節(jié)點也掛了,那么就會丟消息啦。
          為了方便用戶在 可用性 和 持久性 之間權(quán)衡,Kafka 提供兩個 topic 級別的配置,用于 持久性 比 可用性 重要的情況:
          1. 禁用臟 leader 選舉
          2. 指定一個最小 ISR 集大?。?/span>min.insync.replicas?參數(shù)設(shè)置):只有當(dāng) ISR 集大小大于設(shè)定的最小值,分區(qū) [leader] 才會接受消息寫入。這個設(shè)置只有當(dāng) producer 使用?acks=all?時才會生效。(注:在我們生產(chǎn)環(huán)境中,分區(qū)副本數(shù)通常申請為 3(包含 leader),那么?min.insync.replicas?應(yīng)該設(shè)定為 2,但默認(rèn)是 1。使用 1,那么當(dāng)分區(qū)只有一個副本(即 leader),producer 也能寫入成功,但如果這個副本又掛了,就會丟數(shù)據(jù)。)

          副本管理

          一個 Kafka 集群上一般會有多個 topic,每個 topic 又有多個 partition,為了節(jié)點之間負(fù)載均衡,通常以循環(huán)(round-robin)方式在所有節(jié)點上分布 partition 和 分區(qū) leader 角色。
          另外,在分區(qū) leader 節(jié)點之后重新選出 leader 之前,存在一段不可用的時間窗口,為了縮短這個時間窗口,Kafka 會從所有 broker 中選擇一個作為“控制器(controller)”,這個控制器會檢測 broker 級別的問題(failures),在發(fā)現(xiàn)某個 broker 掛掉之后,負(fù)責(zé)為受影響的分區(qū)指定新的 leader,而不是每個分區(qū)自己負(fù)責(zé)重新選主,這樣的選主過程更輕量更快。如果控制器節(jié)點掛了,還存活的 broker 中的一個會成為新的控制器。

          消費(fèi)者消費(fèi)進(jìn)度跟蹤

          Kafka 為每個消費(fèi)組(consumer group)指定一個 broker 來存儲目標(biāo) topic 各個分區(qū)的消費(fèi)進(jìn)度(offsets),這個 broker 稱為?組協(xié)調(diào)器(group coordinator)。這個消費(fèi)組中的任一消費(fèi)者實例都應(yīng)該將消費(fèi)進(jìn)度提交到這個組協(xié)調(diào)器,或者從這個組協(xié)調(diào)器獲取啟動之前上次的消費(fèi)進(jìn)度。Kafka 基于消費(fèi)組的名稱為消費(fèi)組分配協(xié)調(diào)器。消費(fèi)者可以向任一 broker 發(fā)送 FindCoordinatorRequest 請求來查找自己的協(xié)調(diào)器,并從 FindCoordinatorResponse 響應(yīng)中獲取協(xié)調(diào)器的詳細(xì)信息。
          在組協(xié)調(diào)器接收到一個 OffsetCommitRequest 請求后,會將請求數(shù)據(jù)寫到一個特殊的經(jīng)壓實的(compacted)(http://kafka.apache.org/documentation/#compaction)?Kafka topic -?__consumer_offsets。在目標(biāo)分區(qū)的所有副本都確認(rèn)收到了,協(xié)調(diào)器才會向消費(fèi)者發(fā)送進(jìn)度提交成功的響應(yīng)。這個 topic 的消息日志數(shù)據(jù)會定期進(jìn)行壓實(compact),因為只需要為每個分區(qū)維護(hù)最新的消費(fèi)進(jìn)度。協(xié)調(diào)器也會在內(nèi)存中緩存消費(fèi)進(jìn)度,方便快速響應(yīng)消費(fèi)進(jìn)度查詢請求。
          注:如果消費(fèi)者/消費(fèi)組特別多(例如:我們廣告引擎服務(wù),讀取正排消息 topic,一個機(jī)器實例就是一個 consumer group,數(shù)量在幾百到幾千不等),那么組協(xié)調(diào)器的壓力會比較大,那么確保組協(xié)調(diào)器的角色均勻分配到集群的所有 broker,比較關(guān)鍵。另外,__consumer_offsets?這個 topic 的分區(qū)數(shù)量不能太少,最好和 broker 數(shù)量相同或者整數(shù)倍數(shù)量。


          如喜歡本文,請點擊右上角,把文章分享到朋友圈
          如有想了解學(xué)習(xí)的技術(shù)點,請留言給若飛安排分享

          ·END·

          作者:xiayf

          來源:http://blog.xiayf.cn/2019/10/13/reading-kafka-design/


          版權(quán)申明:內(nèi)容來源網(wǎng)絡(luò),版權(quán)歸原創(chuàng)者所有。除非無法確認(rèn),我們都會標(biāo)明作者及出處,如有侵權(quán)煩請告知,我們會立即刪除并表示歉意。謝謝!

          瀏覽 56
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩成人在线播放 | 娱乐网一区二区三区 | 大鸡巴操逼 | 中日精品 | 韩国精品一二区 |