主頁 > 資料庫 > kafka設計理念決議

kafka設計理念決議

2023-04-24 09:07:02 資料庫

一.引言

kafka是廣泛使用的流處理組件,我們知道怎么使用它,也知道它的實作原理,但是更重要的部分是它的設計理念,即kafka設計者當時是如何考量各種方案的,了解這些,對提升我們的設計能力非常有幫助,

二.動機

我們將 Kafka 設計為一個統一平臺,來處理大型公司可能擁有的所有實時資料流, 為此,我們必須考慮相當廣泛的用例集,

  • 它必須具有高吞吐量,才能支持大容量事件流,例如實時日志聚合,
  • 它需要優雅地處理大量積壓資料,以便能夠支持離線系統的周期性資料負載,
  • 系統必須保證low-latency delivery,才能處理更傳統的訊息傳遞用例,
  • 我們希望支持磁區、分布式、實時處理,基于舊的事件流創建新的事件流, 這激發了我們的磁區和consumer模型,
  • 最后,在將流送入其他資料系統進行服務的情況下,系統必須能夠在出現機器故障時保證容錯性,

支持這些用途使我們做出具有許多獨特元素的設計,與傳統的訊息傳遞系統相比,它更類似于資料庫日志, 我們將在以下部分概述設計的一些元素,

三.持久化

不要害怕檔案系統!
Kafka 嚴重依賴檔案系統來存盤和快取訊息, 人們普遍認為“磁盤很慢”,這讓人們懷疑持久化結構能否提供有競爭力的性能, 事實上,磁盤比人們預期的要慢得多,也快得多,這取決于它們的使用方式, 一個設計得當的磁盤結構通常可以和網路一樣快,

關于磁盤性能的關鍵事實是,在過去十年中,硬碟驅動器的吞吐量與磁盤尋道的延遲有所不同, 在6個 7200rpm SATA RAID-5 陣列的 JBOD 配置上,順序寫入的性能約為 600MB/秒,而隨機寫入的性能僅為約 100k/秒——相差超過 6000 倍, 這些順序讀取和寫入是所有使用模式中最可預測的,并且由作業系統進行了大量優化, 現代作業系統提供read-ahead和write-behind技術,以大的資料塊預取資料,并將較小的邏輯寫入分組為大的物理寫入, 可以在這篇 ACM 佇列文章ACM Queue article;找到對這個問題的進一步討論;他們實際上發現順序磁盤訪問在某些情況下比隨機記憶體訪問更快!

tips:read-ahead

Linux的檔案預讀readahead,指Linux系統內核將指定檔案的某 區域預讀進頁快取起來,便于接下來對該區域進行讀取時,不會因缺頁(page fault)而阻塞,因為從記憶體讀取比從磁盤讀取要快很多,預讀可以有效的減少磁盤的尋道次數和應用程式的I/O等待時間,是改進磁盤讀I/O性能的重要 優化手段之一,

為了彌補這種性能差異,現代作業系統越來越積極地使用記憶體進行磁盤快取, 現代作業系統會傾向于將所有空閑記憶體用作磁盤快取,而性能損失很小, 所有的磁盤讀寫都會經過這個統一的快取, 如果不使用直接 I/O,則無法輕易關閉此功能,因此即使行程快取了資料,該資料也可能會在作業系統page cache中復制,將其存盤兩次,

此外,當構建在 JVM 之上,任何花時間研究 Java 記憶體使用的人都知道兩件事:

  1. 物件的記憶體開銷非常高,通常會使存盤的資料空間翻倍(或更糟),
  2. 隨著堆內資料的增加,Java GC變得越來越繁瑣和緩慢,

tips: Java物件構成

Java 的物件包括物件頭,類指標,陣列長度(可選),資料,比如只有一個int型別欄位的物件需要96bit的記憶體,

由于這些因素,使用檔案系統和依賴page cache優于在記憶體中維護快取或其他結構——至少,可以用作快取的記憶體翻倍,并且可能通過存盤緊湊的位元組結構而不是單個物件再次翻倍, 這樣做將導致 32GB 機器上的快取高達 28-30GB, 并且沒有GC問題, 此外,即使服務重新啟動,此快取仍將保持預熱狀態,而行程內快取將需要在記憶體中重建(對于 10GB 的快取可能需要 10 分鐘),否則它將需要從一個完全未加載的快取開始(這可能意味著糟糕的初始性能), 這也大大簡化了代碼,因為維護快取和檔案系統之間一致性的所有邏輯現在都在作業系統中,這往往比one-off 行程內嘗試更有效、更正確, 如果您的磁盤使用有利于順序讀取,那么read-ahead實際上是在每次磁盤讀取時使用有用的資料預先填充此快取,

這表明了一種非常簡單的設計:與其在記憶體中保留盡可能多的內容,并在我們用完空間時,恐慌地將其全部重繪到檔案系統,不如將其反轉, 所有資料都立即寫入檔案系統上的持久日志中,而不必重繪到磁盤, 實際上這只是意味著它被轉移到內核的page cache中,

這種以page cache為中心的設計風格在此處有關 Varnish 設計的文章article 中進行了描述(以及適度的自大),

 

常數時間性能
訊息系統中使用的持久資料結構通常是以每個consumer佇列為單位,帶有關聯的 BTree 或其他通用隨機訪問資料結構,以維護有關訊息的元資料, BTree 是可用的最通用的資料結構,可以在訊息系統中支持各種事務性和非事務性語意, 不過,它們確實帶來了相當高的成本:Btree 操作是 O(log N), 通常 O(log N) 被認為本質上等同于常數時間,但對于磁盤操作而言并非如此, 磁盤尋道的速度是10 毫秒一次,并且每個磁盤一次只能進行一次尋道,因此并行性受到限制, 因此,即使很少的磁盤尋道也會導致非常高的開銷, 由于存盤系統混合了非常快的快取操作和非常慢的物理磁盤操作,固定快取資料增加時,樹結構的性能通常是超線性的——即資料加倍會使性能變得慢兩倍不止,

直觀地說,持久佇列可以建立在簡單的讀取和追加到檔案的基礎上,就像日志記錄解決方案的一樣, 這種結構的優點是所有操作都是 O(1) 并且讀取不會阻塞彼此的寫入, 這具有明顯的性能優勢,因為性能與資料大小完全分離——一臺服務器現在可以充分利用許多廉價、低轉速的 1+TB SATA 驅動器, 盡管它們的尋道性能較差,但這些驅動器對于大容量讀寫具有可接受的性能,而且價格是前者的 1/3,容量是前者的 3 倍,

在沒有任何性能損失的情況下訪問幾乎無限的磁盤空間意味著我們可以提供一些訊息系統中不常見的功能, 例如,在 Kafka 中,我們可以將訊息保留相對較長的時間(比如一周),而不是嘗試在訊息被消費后立即洗掉, 正如我們將要描述的,這為consumer帶來了很大的靈活性,

四.效率

我們在效率方面付出了巨大的努力, 我們的主要用例之一是處理網路活動資料,該資料量非常大:每次頁面瀏覽都可能產生數十次寫入, 此外,我們假設發布的每條訊息都被至少一個consumer(通常是很多)消費,因此我們努力使消費盡可能高效,

我們還發現,根據構建和運行大量類似系統的經驗,效率是有效多租戶操作的關鍵, 如果下游基礎設施服務很容易因為應用程式使用量的小波動而成為瓶頸,那么這種小的變化通常會產生問題, 必須非常快,才能確保應用程式不會因為基礎架構出現問題, 當嘗試在集中式集群上運行支持數十或數百個應用程式的集中式服務時,這一點尤為重要,因為使用模式的變化幾乎每天都會發生,

 

我們在上一節中討論了磁盤效率, 一旦消除了不良的磁盤訪問模式,在這種型別的系統中有兩個常見的低效率原因:太多的小 I/O 操作和過多的位元組復制,

小I/O問題既發生在客戶端和服務器之間,也發生在服務器自身的持久化操作中,

為避免這種情況,我們的協議是圍繞“訊息集”抽象構建的,該抽象將訊息自然地分組在一起, 這允許網路請求將訊息組合在一起并分攤網路往返的開銷,而不是一次發送一條訊息, 服務器依次將訊息塊一次性附加到其日志中,而consumer一次獲取大的線性塊,

這個簡單的優化使性能得到巨大的提升, 批處理導致更大的網路資料包、更大的順序磁盤操作、連續的記憶體塊等,所有這些都允許 Kafka 將突發的隨機訊息寫入流轉換為流向consumer的線性寫入,

 

另一個低效率是位元組復制, 在低訊息速率下這不是問題,但在負載下影響很大, 為了避免這種情況,我們采用了一種由producer、broker和consumer共享的標準化二進制訊息格式(因此資料塊可以在它們之間傳輸而無需修改),

broker維護的訊息日志本身只是一個檔案目錄,每個檔案由一系列訊息集填充,這些訊息集以producer和consumer使用的相同格式寫入磁盤, 維護這種通用格式可以優化最重要的操作:持久日志塊的網路傳輸, 現代 unix 作業系統提供高度優化的代碼路徑,用于將資料從頁面快取傳輸到套接字; 在 Linux 中,這是通過 sendfile 系統呼叫完成的,

要了解 sendfile 的影響,了解從檔案到套接字傳輸資料的通用資料路徑:

  1. 作業系統從磁盤讀取資料到內核空間的page cache
  2. 應用程式從內核空間讀取資料到用戶空間緩沖區
  3. 應用程式將資料寫回內核空間的套接字緩沖區
  4. 作業系統將資料從套接字緩沖區復制到 NIC 緩沖區,并在此處通過網路發送

這顯然是低效的,有四個副本和兩個系統呼叫, 使用 sendfile,通過允許作業系統將資料從頁面快取直接發送到網路,可以避免這種重新復制, 所以在這個優化路徑中,只需要最終拷貝到網卡緩沖區,

我們期望一個共同的用例是一個topic的多個consumer, 使用上面的零拷貝優化,資料只被復制到頁面快取中一次并在每次消費時重復使用,而不是存盤在記憶體中并在每次讀取時復制到用戶空間, 這允許以接近網路連接限制的速率使用訊息,

page cache 和 sendfile 的這種組合意味著在 Kafka 集群上,consumer大部分時間不會落后,你將看不到磁盤上的任何讀取活動,因為它們將完全從快取中提供資料,

TLS/SSL 庫在用戶空間運行(Kafka 目前不支持內核中的 SSL_sendfile), 由于此限制,啟用 SSL 時不使用 sendfile, 啟用S??SL配置,參考security.protocol和security.inter.broker.protocol

有關 Java 中的 sendfile 和zero copy支持的更多背景資訊,請參閱本文article,

tips: send file 圖解

 

端到端批量壓縮
在某些情況下,瓶頸實際上不是 CPU 或磁盤,而是網路帶寬, 對于需要通過廣域網在資料中心之間發送訊息的資料管道來說尤其如此, 當然,用戶總是可以在不需要 Kafka 的任何支持的情況下一次壓縮一條訊息,但這可能會導致非常差的壓縮率,因為大部分冗余是由于相同型別的訊息之間的重復(例如欄位名稱在JSON 或 Web 日志中的用戶agent或常見字串值), 高效壓縮需要將多條訊息一起壓縮,而不是單獨壓縮每條訊息,

Kafka 通過高效的批處理格式支持這一點, 一批訊息可以聚集在一起壓縮并以這種形式發送到服務器, 這批訊息將以壓縮形式寫入,并在日志中保持壓縮狀態,只會被consumer解壓,

Kafka 支持 GZIP、Snappy、LZ4 和 ZStandard 壓縮協議, 有關壓縮的更多詳細資訊,請參見此處here.,

五. producer

負載均衡
producer直接將資料發送到作為磁區leader的broker,而無需任何中間路由層, 為了幫助producer做到這一點,所有 Kafka 節點都可以在任何給定時間回答有關哪些服務器處于活動狀態以及topic 磁區的leader所在的元資料請求,以允許producer適當地定向其請求,

客戶端控制將訊息發布到哪個磁區, 這可以隨機完成,實作一種隨機負載平衡,也可以通過某種語意磁區函式來完成, 我們通過允許用戶指定一個鍵來進行磁區并使用它來散列到一個磁區,來自定義語意磁區的介面(如果需要,還有一個選項可以覆寫磁區函式), 例如,如果選擇的鍵是用戶 ID,則給定用戶的所有資料都將發送到同一磁區, 這反過來將允許consumer對他們的消費做出本地假設, 這種磁區方式明確設計為允許在consumer中進行本地敏感處理程式,

 tips: 如果我們想保證訂單消費的順序性,可以將同一用戶的訂單發送給同一磁區,一個磁區只會同時被一個consumer消費,并且在consumer中進行單執行緒執行,

異步發送
批處理是效率的重要驅動因素之一,為了啟用批處理,Kafka producer將嘗試在記憶體中累積資料并在單個請求中發送更大的批次, 批處理可以配置為累積不超過固定數量的訊息,并且等待時間不超過某個固定的延遲限制(比如 64k 或 10 毫秒), 這允許積累更多的位元組來發送,并且在服務器上執行少量且更大的 I/O 操作, 這種緩沖是可配置的,并提供了一種機制來權衡少量的額外延遲以獲得更好的吞吐量,

有關producer的配置 configuration 和 api  api 的詳細資訊可以在檔案的其他地方找到,

tips

批處理的設計思想在很多其他分布式組件中出現過,比如es中的批量插入,

六.consumer

Kafka consumer通過向引導它想要消費的磁區的broker發出“獲取”請求來作業, consumer在每個請求中指定其在日志中的offset,并從該位置開始接識訓一大塊日志, 因此,consumer對該位置有很大的控制權,并且可以在需要時回退它以重新使用資料,

 

推 vs. 拉
我們最初考慮的一個問題是,consumer是應該從broker那里提取資料,還是broker應該將資料push給consumer, 在這方面,Kafka 遵循大多數訊息系統共享的更傳統的設計,其中資料從producer push到broker,并由consumer從broker pull, 一些以日志為中心的系統,例如 Scribe 和 Apache Flume,遵循一種非常不同的基于push的路徑,其中資料被push到下游, 兩種方法各有利弊, 然而,基于push的系統難以處理各種consumer,因為broker控制資料傳輸的速率, 目標通常是讓consumer能夠以最大可能的速度消費; 不幸的是,在push系統中,這意味著當consumer的消費率低于生產率時,consumer往往會不知所措(本質上是DDoS攻擊), 基于pull的系統具有更好的特性,即consumer落后后,在可能的時候趕上來, 這可以通過某種退避協議來緩解,consumer可以通過該協議表明它不堪重負,但是讓傳輸速率充分利用(但不要過度利用)比看起來更棘手, 以前以這種方式構建系統的嘗試使我們采用了更傳統的拉模型,

基于pull的系統的另一個優點是它有助于將發送給consumer的資料積極地分批處理, 基于push的系統必須選擇立即發送請求或積累更多資料然后在不知道下游consumer是否能夠立即處理它的情況下發送它, 如果針對低延遲進行了調整,這將導致一次只發送一條訊息,但無論如何傳輸最終都會被緩沖,這是一種浪費, 基于pull的設計解決了這個問題,因為consumer總是在其在日志中的當前位置(或達到某個可配置的最大大小)之后拉取所有可用訊息, 因此,可以在不引入不必要延遲的情況下獲得最佳批處理,

pull系統的不足之處在于,如果broker沒有資料,consumer可能會進行空轉等待資料到達, 為避免這種情況,我們在pull請求中設定了引數,允許consumer請求在“長輪詢”中阻塞,等待資料到達(并可選擇等待給定位元組數可用以確保較大的傳輸大小),

您可以想象其他可能的設計,這些設計只會端到端地pull, producer將在本地寫入本地日志,而broker將從中提取資料,而consumer則從中提取資料, 通常會提出一種類似型別的“存盤轉發”producer, 這很有趣,但我們覺得不太適合我們擁有數千個producer的目標用例, 我們大規模運行持久性資料系統的經驗讓我們感到,在系統中涉及許多應用程式的數千個磁盤實際上不會使事情變得更可靠,而且操作起來會是一場噩夢, 在實踐中,我們發現我們可以大規模運行具有強大 SLA 的管道,而無需producer持久性,

 

consumer位置

令人驚訝的是,跟蹤已消費的內容是訊息傳遞系統的關鍵性能點之一,
大多數訊息傳遞系統都保留有關在broker上使用了哪些訊息的元資料, 也就是說,當訊息被分發給consumer時,broker要么立即在本地記錄該事實,要么等待consumer的確認, 這是一個相當直觀的選擇,而且對于單機服務器來說確實不清楚這個狀態還能怎么設計, 由于許多訊息傳遞系統中用于存盤的資料結構擴展性很差,這也是一個務實的選擇——因為broker知道消費了什么,它可以立即洗掉它,從而保持資料量較小,

可能不明顯的是,讓broker和consumer就已消費的內容達成一致并不是一個微不足道的問題, 如果broker在每次通過網路分發訊息時立即將訊息記錄為已消費,那么如果consumer未能處理該訊息(比如因為它崩潰或請求超時或其他原因),該訊息將丟失, 為了解決這個問題,很多訊息系統都增加了確認功能,即訊息在發送時只標記為已發送而不是被消費; broker等待來自consumer的特定確認以將訊息記錄為已消費, 這種策略解決了丟失訊息的問題,但又產生了新的問題, 首先,如果consumer處理訊息但在發送確認之前失敗,那么訊息將被消費兩次, 第二個問題是關于性能的,現在broker必須記錄關于每條訊息的多個狀態(首先鎖定它以免它被第二次發出,然后將其標記為永久消費以便它可以被洗掉), 必須處理棘手的問題,例如如何處理已發送但從未確認的訊息,

Kafka 以不同的方式處理這個問題, 我們的topic分為一組完全有序的磁區,每個磁區在任何給定時間都由每個訂閱consumer組中的一個consumer消費, 這意味著consumer在每個磁區中的位置只是一個整數,即下一條要消費的訊息的偏移量, 這使得關于已消耗內容的狀態非常小,每個磁區只有一個數字, 可以定期檢查此狀態, 這使得訊息確認非常廉價,

這個決定有一個附帶的好處, consumer可以故意回退到舊的偏移量并重新消費資料, 這違反了佇列的共同約定,但事實證明這是許多consumer的基本特征, 比如consumer代碼有bug,在消費了一些訊息后被發現,修復bug后consumer可以重新消費這些訊息,

 

離線資料加載
可擴展的持久性允許consumer,只定期消費的可能性,例如批量資料加載,周期性地將資料批量加載到離線系統,如 Hadoop 或關系資料倉庫,
在 Hadoop 的情況下,我們通過將負載拆分到各個map任務來并行化資料加載,每個映射任務對應一個節點/topic/磁區組合,從而允許加載中的完全并行, Hadoop 提供了任務管理,失敗的任務可以重新啟動而沒有重復資料的危險——它們只是從原來的位置重新啟動,

 

static membership
靜態成員旨在提高流應用程式、consumer組和其他構建等在rebalance協議之上的應用程式的可用性, rebalance協議依賴group coordinator將物體 ID 分配給組成員, 這些生成的 ID 是短暫的,并且會在成員重新啟動和重新加入時更改, 對于基于consumer的應用程式,這種“dynamic membership”可能會導致在代碼部署、配置更新和定期重啟等管理操作期間將大部分任務重新分配給不同的實體, 對于大型狀態應用程式,打亂的任務需要很長時間才能恢復到之前的狀態,并導致應用程式部分或完全不可用, 受此觀察的啟發,Kafka 的group management protocol允許組成員提供持久物體 ID, 基于這些 id,組成員資格保持不變,因此不會觸發rebalance,
如果你想使用static membership,

  1. 將broker集群和客戶端應用程式升級到 2.3 或更高版本,并確保升級后的broker使用 2.3 或更高版本的 inter.broker.protocol.version,
  2. 將配置 ConsumerConfig#GROUP_INSTANCE_ID_CONFIG 設定為一組下每個consumer實體的唯一值,
  3. 對于 Kafka Streams 應用程式,為每個 KafkaStreams 實體設定唯一的 ConsumerConfig#GROUP_INSTANCE_ID_CONFIG 就足夠了,與實體使用的執行緒數無關,

如果您的broker版本低于 2.3,但您選擇在客戶端設定 ConsumerConfig#GROUP_INSTANCE_ID_CONFIG,應用程式將檢測broker版本,然后拋出 UnsupportedException, 如果您不小心為不同的實體配置了重復的 ID,broker端的防護機制將通過觸發 org.apache.kafka.common.errors.FencedInstanceIdException 通知您的重復客戶端立即關閉, 有關詳細資訊,請參閱 KIP-345

七.訊息語意

現在我們對producer和consumer的作業方式有了一些了解,讓我們討論一下 Kafka 在producer和consumer之間提供的語意保證, 顯然,可以提供多種可能的訊息傳遞保證:

At most once——訊息可能會丟失,但永遠不會重新傳遞,
At least once——訊息永遠不會丟失,但可以重新傳遞,
Exactly once——這才是人們真正想要的,每條訊息只傳遞一次,
值得注意的是,這分為兩個問題:發布訊息的持久性保證和消費訊息時的保證,


許多系統聲稱提供“Exactly once”交付語意,但閱讀細則很重要,這些宣告中的大多數都是誤導性的(即它們不會轉化為consumer或producer可能失敗的情況,有多個consumer行程的情況,或寫入磁盤的資料可能丟失的情況),

Kafka 的語意是直截了當的, 發布訊息時,我們有訊息被“提交”到日志的概念, 一旦發布的訊息被提交,只要復制該訊息寫入的磁區的一個broker保持“存活”狀態,它就不會丟失, 提交訊息的定義、活動磁區以及我們嘗試處理的故障型別的描述將在下一節中更詳細地描述, 現在讓我們假設一個完美的、無損的broker,并嘗試理解對producer和consumer的保證, 如果producer嘗試發布訊息并遇到網路錯誤,則無法確定此錯誤是發生在訊息提交之前還是之后, 這類似于使用自動生成的鍵插入資料庫表的語意,

在 0.11.0.0 之前,如果producer未能收到指示訊息已提交的回應,它別無選擇,只能重新發送訊息, 這提供了At least once傳遞語意,因為如果原始請求實際上已經成功,則訊息可能會在重新發送期間再次寫入日志, 從 0.11.0.0 開始,Kafka producer還支持冪等傳遞選項,保證重新發送不會導致日志中出現重復條目??, 為此,broker為每個producer分配一個 ID,并使用producer隨每條訊息發送的序列號對訊息進行重復資料洗掉, 同樣從 0.11.0.0 開始,producer支持使用類似事務的語意將訊息發送到多個topic磁區的能力:即 要么所有訊息都已成功寫入,要么都沒有, 主要用例是 Kafka topic之間的一次性處理(如下所述),

并非所有用例都需要如此強大的保證, 對于對延遲敏感的用途,我們允許producer指定其所需的持久性級別, 如果producer指定它想要等待提交的訊息,這可能需要 10 毫秒的數量級, 然而,producer也可以指定它想要完全異步地執行發送,或者它只想等到leader(但不一定是follower)收到訊息,

 

現在讓我們從consumer的角度來描述語意, 所有副本都有完全相同的日志和相同的偏移量, consumer控制其在此日志中的位置, 如果consumer從未崩潰,它可以將這個位置存盤在記憶體中,但如果consumer失敗并且我們希望這個topic磁區被另一個行程接管,新行程將需要選擇一個合適的位置來開始處理, 假設consumer讀取了一些訊息——它有幾個選項來處理訊息和更新它的位置,

  • 它可以讀取訊息,然后保存它在日志中的位置,最后處理訊息, 在這種情況下,consumer行程有可能在保存其位置之后但在保存其訊息處理的輸出之前崩潰, 在這種情況下,接管處理的行程將從保存的位置開始,即使該位置之前的一些訊息還沒有被處理, 這對應于“At most once”語意,因為在consumer失敗訊息的情況下可能不會被處理,
  • 它可以讀取訊息、處理訊息并最終保存其位置, 在這種情況下,consumer行程有可能在處理訊息之后但在保存其位置之前崩潰, 在這種情況下,當新行程接管它接收到的前幾條訊息時,它已經被處理過了, 這對應于consumer失敗情況下的“At least once”語意, 在許多情況下,訊息有一個主鍵,因此更新是冪等的(兩次接收相同的訊息只是用它自己的另一個副本覆寫記錄),

那么 exactly once 語意(即你真正想要的東西)呢? 當從 Kafka topic消費并生產到另一個topic時(如在 Kafka Streams 應用程式中),我們可以利用上面提到的 0.11.0.0 中的新事務producer功能, consumer的位置作為訊息存盤在topic中,因此我們可以在與接收處理資料的輸出topic相同的事務中將偏移量寫入 Kafka, 如果交易被中止,consumer的位置將恢復到它的舊值,并且輸出topic的產生的資料將對其他consumer不可見,這取決于他們的“隔離級別”, 在默認的“read_uncommitted”隔離級別中,所有訊息對consumer都是可見的,即使它們是中止事務的一部分,但在“read_committed”中,consumer將只回傳來自已提交事務的訊息(以及任何不屬于這部分的訊息),

tips:kafka事務示例

寫入外部系統時,限制在于需要將consumer的位置與實際存盤為輸出的內容相協調, 實作這一點的經典方法是在consumer位置的存盤和consumer輸出的存盤之間引入兩階段提交, 但這可以通過讓consumer將其偏移量存盤在與其輸出相同的位置來更簡單和更普遍地處理, 這更好,因為consumer可能想要寫入的許多輸出系統不支持兩階段提交, 作為這方面的一個例子,考慮一個 Kafka Connect 連接器,它在 HDFS 中填充資料以及它讀取的資料的偏移量,這樣可以保證資料和偏移量都被更新,或者兩者都不更新, 我們對許多其他資料系統遵循類似的模式,這些系統需要這些更強的語意并且訊息沒有主鍵以允許重復資料洗掉,

因此Kafka有效地支持Kafka Streams中的exactly-once delivery,在Kafka topics之間傳輸和處理資料時,一般可以使用事務性producer/consumer來提供exactly-once交付, 其他目標系統的 Exactly-once 交付通常需要與此類系統合作,但 Kafka 提供了偏移量,這使得實作這一點變得可行(另請參閱 Kafka Connect), 否則,Kafka 默認保證At least once交付,并允許用戶通過在處理一批訊息之前禁用對producer的重試并在consumer中提交偏移量來實作At most once交付,

八.復制

Kafka 在可配置數量的服務器上為每個topic的磁區復制日志(您可以逐個topic地設定此復制因子), 這允許在集群中的服務器發生故障時自動故障轉移到這些副本,因此訊息在出現故障時仍然可用,

其他訊息系統提供了一些與復制相關的功能,但是,在我們(完全有偏見的)看來,這似乎是一個附加的東西,沒有被大量使用,并且有很大的缺點:副本不活躍,吞吐量受到嚴重影響,它需要繁瑣的手動配置等, Kafka 旨在默認與復制一起使用——事實上,我們將未復制的topic實作為復制因子為 1 的復制topic,

復制單元是topic磁區, 在非故障情況下,Kafka 中的每個磁區都有一個leader和0個或多個follower, 包括leader在內的副本總數構成復制因子, 所有寫入都到磁區的leader,從磁區的leader或follower讀, 通常,磁區比broker多得多,leader平均分布在broker之間, follower上的日志與leader的日志相同——都具有相同的偏移量和相同順序的訊息(當然,在任何給定時間,leader可能在其日志末尾有一些尚未復制的訊息).

follower像普通的 Kafka consumer一樣消費來自leader的訊息,并將它們應用到自己的日志中, 讓follower從leader那里pull有一個很好的特性,那就是允許follower自然地將他們正在應用到他們日志的時候進行批處理,

與大多數分布式系統一樣,自動處理故障需要精確定義節點“存活”的含義, 在 Kafka 中,一個稱為“controller”的特殊節點負責管理集群中broker的注冊, Broker 活躍度有兩個條件:

  1. broker必須與controller保持活躍的會話,以便接收定期的元資料更新,
  2. 作為follower的broker必須復制leader的資料,而不是落后“太遠”,

“活動會話”的含義取決于集群配置, 對于 KRaft 集群,通過向controller發送定期心跳來維持活動會話, 如果controller在 broker.session.timeout.ms 配置的超時到期之前未能收到心跳,則該節點被視為離線,

對于使用 Zookeeper 的集群,活性是通過broker在其 Zookeeper 會話初始化時創建的臨時節點的存在間接確定的, 如果broker在 zookeeper.session.timeout.ms 到期之前未能向 Zookeeper 發送心跳后丟失其會話,則該節點將被洗掉, 然后,controller會通過 Zookeeper 監視通知節點洗掉,并將broker標記為離線,

我們將滿足這兩個條件的節點稱為“同步”,以避免“活著”或“失敗”的含糊不清, leader跟蹤一組“同步”副本,稱為 ISR, 如果這些條件中的任何一個未能滿足,則broker將從 ISR 中移除, 例如,如果一個follower宕機,那么controller將通過丟失會話來通知失敗,并將從 ISR 中洗掉broker, 另一方面,如果 follower 落后于 leader 太遠但仍有活動會話,則 leader 也可以將其從 ISR 中移除, 滯后副本的確定是通過 replica.lag.time.max.ms 配置來控制的, 無法在此配置設定的最長時間內趕上leader日志末尾的副本將從 ISR 中洗掉,

在分布式系統術語中,我們只嘗試處理“失敗/恢復”故障模型,其中節點突然停止作業然后恢復(可能不知道它們已經宕機), Kafka 不處理所謂的“拜占庭式”故障,在這種情況下,節點會產生任意或惡意的回應(可能是由于錯誤或犯規),

我們現在可以更精確地定義,當該磁區的 ISR 中的所有副本都將一條訊息應用到它們的日志時,該訊息被視為已提交, 只有提交的訊息才會發送給leader, 這意味著leader不必擔心如果leader失敗可能會看到一條可能丟失的訊息, 另一方面,producer可以選擇是否等待訊息被提交,這取決于他們對延遲和持久性之間權衡的偏好, 此首選項由producer使用的 acks 設定控制, 請注意,topic具有同步副本“最小數量”的設定,當producer請求確認訊息已寫入完整的同步副本集時,將檢查該設定, 如果producer請求不那么嚴格的確認,訊息可以被提交和消費,即使同步副本的數量低于最小值(例如,它可以與leader一樣低),

Kafka 提供的保證是提交的訊息不會丟失,只要至少有一個同步副本始終處于存活狀態,

在短暫的故障轉移期后,Kafka 將在出現節點故障時保持可用,但在出現網路磁區時可能無法保持可用,

 

復制日志:Quorums、ISR 和狀態機
Kafka 磁區的核心是復制的日志, 復制日志是分布式資料系統中最基本的原語之一,實作它的方法有很多種, 復制的日志可以被其他系統用作以狀態機樣式實作其他分布式系統的原語,
復制的日志模擬了對一系列值的順序達成共識的程序(通常將日志條目編號為 0、1、2、...), 有很多方法可以實作這一點,但最簡單和最快的是使用一個leader來選擇提供給它的值的排序, 只要leader還活著,所有的follower只需要復制leader選擇的值和順序,

當然,如果leader沒有失敗,我們就不需要follower了! 當leader宕機時,我們需要從follower中選擇一個新的leader, 但是follower本身可能會落后或崩潰,所以我們必須確保我們選擇了一個最新的follower, 日志復制演算法必須提供的基本保證是,如果我們告訴客戶端一條訊息已提交,而leader失敗了,我們選出的新leader也必須擁有該訊息, 這會產生一個權衡:如果leader等待更多的follower在獲取資訊之前提交一條資訊,那么將會有更多的選舉leader,

如果您選擇所需的確認數量和必須比較的日志數量以選擇leader,從而保證有重疊,那么這稱為Quorum,

這種權衡的一種常見方法是對提交決定和leader選舉都使用多數表決, 這不是 Kafka 所做的,但無論如何讓我們探索它以了解權衡, 假設我們有 2f+1 個副本, 如果 f+1 個副本必須在leader宣告提交之前收到一條訊息,并且如果我們通過從至少 f+1 個副本中選擇具有最完整日志的follower來選舉新的leader,那么,不超過 f失敗時,leader保證擁有所有已提交的訊息, 這是因為在任何 f+1 個副本中,必須至少有一個副本包含所有已提交的訊息, 該副本的日志將是最完整的,因此將被選為新的leader, 每個演算法都必須處理許多剩余的細節(例如精確定義什么使日志更完整,確保leader失敗期間日志的一致性或更改副本集中的服務器集)但我們現在將忽略這些,

這種多數表決方法有一個非常好的特性:延遲僅取決于最快的服務器, 也就是說,如果復制因子為 3,則延遲由較低的follower而不是較高的follower決定,

這個家族中有豐富多樣的演算法,包括 ZooKeeper 的 Zab、Raft 和 Viewstamped Replication, 據我們所知,與 Kafka 的實際實作最相似的學術出版物是來自 Microsoft 的 PacificA,

多數表決的不利之處在于,您無需多次失敗就會失去可選舉的leader, 容忍一次故障需要三份資料,容忍兩次故障需要五份資料, 根據我們的經驗,只有足夠的冗余來容忍單個故障對于實際系統來說是不夠的,但是每次寫入五次,磁盤空間要求是 5 倍,吞吐量是 1/5,對于大容量資料問題來說并不是很實用, 這可能就是為什么Quorum演算法更常出現在 ZooKeeper 等共享集群配置中,但不太常見于主資料存盤的原因, 例如,在 HDFS 中,namenode 的高可用性功能是建立在基于多數投票的日志上的,但這種更昂貴的方法并不用于資料本身,

Kafka 采用略微不同的方法來選擇其Quorum集, Kafka 不是多數表決,而是動態維護一組同步到leader的同步副本(ISR), 只有這個集合中的成員才有資格被選為leader, 在所有同步副本都收到寫入之前,不會將對 Kafka 磁區的寫入視為已提交, 只要 ISR 集發生變化,它就會保留在集群元資料中, 正因為如此,ISR 中的任何副本都有資格被選為leader, 這是 Kafka 的使用模型的一個重要因素,其中有許多磁區并且確保leader平衡很重要, 使用此 ISR 模型和 f+1 個副本,Kafka topic可以容忍 f 次故障而不會丟失已提交的訊息,

對于我們希望處理的大多數用例,我們認為這種權衡是合理的, 實際上,為了容忍 f 次失敗,多數投票和 ISR 方法都將在提交訊息之前等待相同數量的副本確認(例如,為了在一次失敗中幸存下來,多數群體需要三個副本和一個確認,而 ISR 方法需要兩個副本和一個確認), 在沒有最慢服務器的情況下提交的能力是多數表決方法的一個優勢, 但是,我們認為通過允許客戶端選擇是否阻塞訊息提交來改進它,并且由于所需的復制因子較低而帶來的額外吞吐量和磁盤空間是值得的,

另一個重要的設計區別是 Kafka 不要求崩潰的節點恢復時所有資料都完好無損, 在這個空間中,復制演算法依賴于“穩定存盤”的存在并不少見,這種存盤在任何故障恢復場景中都不會丟失,而不會出現潛在的一致性違規, 這個假設有兩個主要問題, 首先,磁盤錯誤是我們在持久資料系統的實際操作中觀察到的最常見問題,它們通常不會讓資料保持完整, 其次,即使這不是問題,我們也不希望在每次寫入時都使用 fsync 來保證一致性,因為這會使性能降低兩到三個數量級, 我們允許副本重新加入 ISR 的協議,確保在重新加入之前,它必須再次完全重新同步,即使它在崩潰中丟失了未重繪的資料,

tips: es使用的是Quorum演算法,

unclean leader選舉:如果他們都宕機了怎么辦?
請注意,Kafka 對資料丟失的保證基于至少一個副本保持同步, 如果復制磁區的所有節點都宕機,則此保證不再有效,
然而,當所有副本都宕機時,實際系統需要做一些合理的事情, 如果您不幸遇到這種情況,請務必考慮會發生什么, 有兩種行為可以實作:

  1. 等待 ISR 中的一個副本恢復并選擇這個副本作為leader(希望它仍然擁有所有資料),
  2. 選擇第一個的副本(不一定在 ISR 中)作為leader,

這是可用性和一致性之間的簡單權衡, 如果我們在 ISR 中等待副本,那么只要這些副本關閉,我們就將保持不可用狀態, 如果此類副本被破壞或資料丟失,那么我們將永久關閉, 另一方面,如果一個不同步的副本恢復并且我們允許它成為leader,那么它的日志就會成為真實的來源,即使它不能保證有每條提交的訊息, 默認情況下,從 0.11.0.0 版本開始,Kafka 選擇第一種策略并傾向于等待一致的副本, 可以使用配置屬性 unclean.leader.election.enable 更改此行為,以支持正常運行時間優于一致性的用例,

這種困境并不是 Kafka 特有的, 它存在于任何基于群體的方案中, 例如,在多數表決方案中,如果大多數服務器遭受永久性故障,那么您必須選擇丟失 100% 的資料,或者通過將現有服務器上保留的內容作為新的真實來源來違反一致性,

 

可用性和耐用性保證
寫入 Kafka 時,producer可以選擇是等待訊息被 0,1 還是所有 (-1) 個副本確認, 請注意,“所有副本的確認”并不能保證所有分配的副本都已收到訊息, 默認情況下,當 acks=all 時,一旦所有當前同步副本都收到訊息,就會發生確認, 例如,如果一個topic只配置了兩個副本并且一個失敗了(即只剩下一個同步副本),那么指定 acks=all 的寫入將成功, 但是,如果其余副本也發生故障,這些寫入可能會丟失, 雖然這確保了磁區的最大可用性,但對于一些更喜歡持久性而不是可用性的用戶來說,這種行為可能是不受歡迎的, 因此,我們提供了兩個topic級配置,可用于使訊息持久性優于可用性:

  1. 禁用unclean的leader選舉——如果所有副本都不可用,那么磁區將保持不可用狀態,直到最近的leader再次可用, 這實際上更傾向于不可用而不是訊息丟失的風險, 請參閱上一節關于 Unclean Leader Election 的說明,
  2. 指定最小 ISR 大小 - 如果 ISR 的大小超過某個最小值,磁區將僅接受寫入,以防止丟失僅寫入單個副本的訊息,該副本隨后變得不可用, 此設定僅在producer使用 acks=all 并保證訊息將被至少這么多同步副本確認時才生效, 此設定提供了一致性和可用性之間的權衡, 最小 ISR 大小的較高設定可保證更好的一致性,因為可以保證將訊息寫入更多副本,從而降低丟失訊息的可能性, 但是,它會降低可用性,因為如果同步副本的數量低于最小閾值,磁區將不可用于寫入,

 

副本管理
上面關于復制日志的討論實際上只涵蓋了一個日志,即 一個topic磁區, 但是,Kafka 集群將管理成百上千個這樣的磁區, 我們嘗試以回圈方式平衡集群內的磁區,以避免將高容量topic磁區聚集在少量節點上, 同樣,我們嘗試平衡leader,以便每個節點包含一定比率的leader,
優化leader選舉程序也很重要,因為這是不可用的關鍵視窗, leader選舉的簡單實作最侄訓在節點失敗時為該節點托管的所有磁區運行每個磁區的選舉, 正如上面關于復制的部分所討論的,Kafka 集群有一個特殊的角色,稱為“controller”,負責管理broker的注冊, 如果 controller 檢測到 broker 發生故障,它會負責選舉 ISR 的剩余成員之一作為新的 leader, 結果是我們能夠將許多所需的leader層變更通知在一起批處理,這使得大量磁區的選舉程序成本更低、速度更快, 如果controller本身出現故障,則將選舉另一個controller,

九.日志壓縮

日志壓縮確保 Kafka 始終至少保留單個topic磁區資料日志中每個訊息鍵的最后一個已知值, 它解決了如下用例和場景,例如在應用程式崩潰或系統故障后恢復狀態,或在操作維護期間應用程式重啟后重新加載快取, 讓我們更詳細地研究這些用例,然后描述壓縮的作業原理,
到目前為止,我們只描述了更簡單的資料保留方法,即在固定時間段后或當日志達到某個預定大小時丟棄舊日志資料, 這適用于時間事件資料,例如每條記錄獨立的日志記錄, 然而,一類重要的資料流是對給予鍵可變資料的變更日志(例如,對資料庫表的更改),

讓我們討論這種流的具體示例, 假設我們有一個包含用戶電子郵件地址的topic; 每次用戶更新他們的電子郵件地址時,我們都會使用他們的用戶 ID 作為主鍵向該topic發送一條訊息, 現在假設我們在一段時間內為 ID 為 123 的用戶發送以下訊息,每條訊息對應于電子郵件地址的更改(省略其他 ID 的訊息):

123 => [email protected]
        .
        .
        .
123 => [email protected]
        .
        .
        .
123 => [email protected]

日志壓縮為我們提供了更細粒度的保留機制,因此我們保證至少保留每個主鍵的最后更新(例如 [email protected]), 通過這樣做,我們保證日志包含每個鍵的最終值的完整快照,而不僅僅是最近更改的鍵, 這意味著下游leader可以在這個topic之外恢復他們自己的狀態,而我們不必保留所有更改的完整日志,
讓我們先看看一些有用的用例,然后再看看如何使用它,

  1. 資料庫更改訂閱, 通常需要在多個資料系統中擁有一個資料集,并且這些系統中的一個通常是某種資料庫(RDBMS 或者可能是其他的鍵值存盤), 例如,您可能有一個資料庫、一個快取、一個搜索集群和一個 Hadoop 集群, 對資料庫的每次更改都需要反映在快取、搜索集群中,并最終反映在 Hadoop 中, 在只處理實時更新的情況下,您只需要最近的日志, 但是,如果您希望能夠重新加載快取或恢復失敗的搜索節點,您可能需要一個完整的資料集,
  2. Event sourcing, 這是一種應用程式設計風格,它將查詢處理與應用程式設計放在一起,并使用更改日志作為應用程式的主要存盤,
  3. 日志記錄以實作高可用性, 一個執行本地計算的行程可以通過注銷它對其本地狀態所做的更改來實作容錯,這樣另一個行程可以重新加載這些更改并在它失敗時繼續執行, 一個具體的例子是在流查詢系統中處理計數、聚合和其他類似“分組依據”的處理, Samza 是一個實時流處理框架,正是為此目的而使用此功能,

tips:mysql中的redo log,es中的transaction log,redis中的aof持久化都是日志記錄以實作高可用性的例子,這里的日志壓縮類比redis中的aof日志重寫,

在這些情況中的每一種情況下,都需要主要處理實時變化,但偶爾,當機器崩潰或資料需要重新加載或重新處理時,需要進行完全加載, 日志壓縮可以同時支持這兩個用例, 這篇博文this blog post中更詳細地描述了這種日志的使用方式,
總體思路很簡單, 如果我們有無限的日志保留,并且我們記錄了上述情況下的每個更改,那么我們將捕獲系統從第一次開始時開始的每次狀態, 使用這個完整的日志,我們可以通過重放日志中的前 N ??條記錄來恢復到任何時間點, 這個假設的完整日志對于多次更新單個記錄的系統不是很實用,因為即使對于穩定的資料集,日志也會無限增長, 丟棄舊更新的簡單日志保留機制將限制空間,但日志不再是恢復當前狀態的一種方式——現在從日志開頭恢復不再重新創建當前狀態,因為舊更新可能根本無法捕獲.

日志壓縮是一種提供更細粒度的保留每條記錄的機制,而不是粗粒度的基于時間的保留, 我們的想法是有選擇地洗掉我們使用相同主鍵進行更新的記錄, 這樣可以保證日志至少具有每個鍵的最后狀態,

可以為每個topic設定此保留策略,因此單個集群可以有一些topic,其中保留是通過大小或時間強制執行的,而其他topic的保留是通過壓縮強制執行的,

此功能的靈感來自 LinkedIn 最古老、最成功的基礎設施之一——稱為 Databus 的資料庫變更日志快取服務, 與大多數日志結構存盤系統不同,Kafka 是為訂閱而構建的,并組織資料以實作快速線性讀寫, 與 Databus 不同,Kafka 充當真實來源存盤,因此即使在上游資料源無法重放的情況下,它也很有用,

 

日志壓碩訓礎
這是一張high-level圖片,顯示了 Kafka 日志的邏輯結構以及每條訊息的偏移量,

日志的頭部與傳統的 Kafka 日志相同, 它具有密集的、順序的偏移量并保留所有訊息, 日志壓縮添加了一個用于處理日志尾部的選項, 上圖顯示了帶有壓縮尾部 的 log, 請注意,日志尾部的訊息保留了首次寫入時分配的原始偏移量——永遠不會改變, 另請注意,即使具有該偏移量的訊息已被壓縮,所有偏移量仍會保留在日志中的有效位置; 在這種情況下,該位置與日志中出現的下一個最高偏移量無法區分, 例如,在上圖中,偏移量 36、37 和 38 都是等效位置,從這些偏移量中的任何一個開始的讀取都將回傳以 38 開頭的訊息集,

壓縮也允許洗掉, 帶有鍵和空負載的訊息將被視為從日志中洗掉, 這樣的記錄有時被稱為tombstone, 此洗掉標記將導致洗掉任何具有該鍵的先前訊息(就像任何具有該鍵的新訊息一樣),但洗掉標記是特殊的,因為它們將在一段時間后從日志中清除以釋放空間. 不再保留洗掉的時間點在上圖中標記為“洗掉保留點”,

壓縮是通過定期重新復制日志段在后臺完成的, 清理不會阻止讀取,并且可以限制使用不超過可配置的 I/O 吞吐量,以避免影響producer和leader, 壓縮日志段的實際程序如下所示:

 

日志壓縮提供什么保證?
日志壓縮保證以下內容:

  1. 任何停留在日志頭部的leader都會看到寫入的每條訊息; 這些訊息將具有順序偏移量, topic的 min.compaction.lag.ms 可用于保證訊息寫入后必須經過的最短時間長度才能被壓縮, IE, 它提供了每條訊息將在(未壓縮的)頭部保留多長時間的下限, topic的 max.compaction.lag.ms 可用于保證訊息寫入時間和訊息符合壓縮條件的時間之間的最大延遲,
  2. 始終保持訊息的順序, 壓縮永遠不會重新排序訊息,只是洗掉一些,
  3. 訊息的偏移量永遠不會改變, 它是日志中某個位置的永久識別符號,
  4. 任何從日志開頭開始的leader都將至少看到所有記錄的最終狀態,按照它們的寫入順序, 此外,如果leader在小于topic的 delete.retention.ms 設定(默認為 24?? 小時)的時間段內到達日志頭部,則會看到所有已洗掉記錄的洗掉標記, 換句話說:由于洗掉標記的洗掉與讀取同時發生,因此如果leader滯后超過 delete.retention.ms,則它有可能錯過洗掉標記,

日志壓縮詳細資訊
日志壓縮由日志清理器處理,這是一個重新復制日志段檔案的后臺執行緒池,洗掉其鍵出現在日志頭部的記錄, 每個壓縮器執行緒的作業方式如下:

  1. 它選擇日志頭與日志尾的比率最高的日志
  2. 它為日志頭部的每個鍵創建最后一個偏移量的簡潔摘要
  3. 它從頭到尾復制日志,洗掉日志中稍后出現的鍵, 新的、干凈的段會立即交換到日志中,因此所需的額外磁盤空間只是一個額外的日志段(不是日志的完整副本),
  4. 日志頭的摘要本質上只是一個空間緊湊的哈希表, 每個條目正好使用 24 個位元組, 結果,使用 8GB 的??清理器緩沖區,一次清理器迭代可以清理大約 366GB 的日志頭(假設有 1k 條訊息),

 

配置日志清理器
默認情況下啟用日志清理器, 這將啟動cleaner執行緒池, 要針對特定??topic啟用日志清理,請添加特定于日志的屬性
log.cleanup.policy=compact
log.cleanup.policy 屬性是在broker的 server.properties 檔案中定義的broker配置設定; 它會影響集群中所有沒有配置覆寫的topic,如此處所述, 日志清理器可以配置為保留最少量的未壓縮的日志“頭”, 這是通過設定壓縮時間延遲來實作的,
log.cleaner.min.compaction.lag.ms
這可用于防止比最小訊息年齡更新的訊息被壓縮, 如果未設定,則所有日志段都符合壓縮條件,但最后一段除外,即 當前正在寫入的那個, 活動段不會被壓縮,即使它的所有訊息都早于最小壓縮時間延遲, 可以配置日志清理器以確保最大延遲,在該延遲之后,日志的未壓縮“頭”有資格進行日志壓縮,
log.cleaner.max.compaction.lag.ms
這可用于防止低生產率的日志在無限制的持續時間內不符合壓縮條件, 如果未設定,則不壓縮不超過 min.cleanable.dirty.ratio 的日志, 請注意,這個壓縮截止日期并不是硬性保證,因為它仍然取決于日志清理器執行緒的可用性和實際壓縮時間, 您將需要監控 uncleanable-partitions-count、max-clean-time-secs 和 max-compaction-delay-secs 指標,
此處here.描述了更cleanner的配置,

十.配額

Kafka 集群能夠對請求強制執行配額,以控制客戶端使用的broker資源, Kafka broker可以為共享配額的每組客戶端強制執行兩種型別的客戶端配額:

  1. 網路帶寬配額定義位元組速率閾值(自 0.9 起)
  2. 請求率配額將 CPU 利用率閾值定義為網路和 I/O 執行緒的百分比(自 0.11 起)

tips,這里的配額可以類比為常見的限流器

為什么需要配額?
producer和leader有可能生產/消費非常大量的資料或以非常高的速率生成請求,從而壟斷broker資源,導致網路飽和,并且通常 DOS 其他客戶端和broker本身, 擁有配額可以防止這些問題,并且在大型多租戶集群中尤為重要,在這種情況下,一小部分行為不端的客戶端可能會降低行為良好的用戶體驗, 事實上,當將 Kafka 作為服務運行時,這甚至可以根據商定的合同強制執行 API 限制,

 

client group
Kafka 客戶端的身份是用戶主體,代表安全集群中經過身份驗證的用戶, 在支持未經身份驗證的客戶端的集群中,用戶主體是broker使用可配置的 PrincipalBuilder 選擇的一組未經身份驗證的用戶, Client-id 是客戶端的邏輯分組,具有由客戶端應用程式選擇的有意義的名稱, 元組 (user, client-id) 定義了共享用戶主體和客戶端 id 的安全邏輯客戶端組,
配額可以應用于(用戶、客戶端 ID)、用戶或客戶端 ID 組, 對于給定的連接,將應用與該連接匹配的最具體的配額, 配額組的所有連接共享為該組配置的配額, 例如,如果 (user="test-user", client-id="test-client") 的生產配額為 10MB/秒,這將在用戶“test-user”的所有producer實體與客戶端之間共享- ID“測驗客戶端”,

 

配額配置
可以為(用戶、客戶端 ID)、用戶和客戶端 ID 組定義配額配置, 可以在任何需要更高(或更低)配額的配額級別覆寫默認配額, 該機制類似于每個topic的日志配置覆寫, 用戶和 (user, client-id) 配額覆寫寫入 /config/users 下的 ZooKeeper,client-id 配額覆寫寫入 /config/clients 下, 這些覆寫被所有broker讀取并立即生效, 這讓我們無需滾動重啟整個集群即可更改配額, 有關詳細資訊,請參見此處 here , 每個組的默認配額也可以使用相同的機制動態更新,

配額配置的優先順序是:

  1. /config/users/<user>/clients/<client-id>
  2. /config/users/<user>/clients/<default>
  3. /config/users/<user>
  4. /config/users/<default>/clients/<client-id>
  5. /config/users/<default>/clients/<default>
  6. /config/users/<default>
  7. /config/clients/<client-id>
  8. /config/clients/<default>

 

網路帶寬配額
網路帶寬配額定義為共享配額的每組客戶端的位元組速率閾值, 默認情況下,每個唯一的客戶端組都會收到由集群配置的以位元組/秒為單位的固定配額, 此配額是基于每個broker定義的, 在客戶端受到限制之前,每組客戶端最多可以發布/獲取每個broker的 X 位元組/秒,

 

請求速率配額
請求率配額定義為客戶端可以在配額視窗內使用每個broker的請求處理程式 I/O 執行緒和網路執行緒的時間百分比, n% 的配額表示一個執行緒的 n%,因此配額超出總容量 ((num.io.threads + num.network.threads) * 100)%, 在被限制之前,每組客戶端可以在配額視窗中的所有 I/O 和網路執行緒中使用總百分比最高為 n% 的百分比, 由于為 I/O 和網路執行緒分配的執行緒數通常基于broker主機上可用的內核數,因此請求率配額代表每組共享配額的客戶端可能使用的 CPU 的總百分比,

 

執行細節
默認情況下,每個唯一的客戶端組都會收到集群配置的固定配額, 此配額是基于每個broker定義的, 在受到限制之前,每個客戶端都可以使用每個broker的這個配額, 我們決定為每個broker定義這些配額比為每個客戶端設定固定的集群帶寬要好得多,因為這需要一種機制來在所有broker之間共享客戶端配額使用情況, 這可能比配額實施本身更難做到!

broker在檢測到配額違規時如何反應? 在我們的解決方案中,broker首先計算將違規客戶置于其配額之下所需的延遲量,并立即回傳包含延遲的回應, 在獲取請求的情況下,回應將不包含任何資料, 然后,broker將到客戶端的通道靜音,不再處理來自客戶端的請求,直到延遲結束, 在收到具有非零延遲持續時間的回應后,Kafka 客戶端還將避免在延遲期間向broker發送進一步的請求, 因此,來自受限客戶端的請求會被雙方有效阻止, 即使使用不尊重broker延遲回應的舊客戶端實作,broker通過靜音其套接字通道施加的背壓仍然可以處理行為不端的客戶端的節流, 那些向受限通道發送進一步請求的客戶端只有在延遲結束后才會收到回應,

在多個小視窗(例如 30 個視窗,每個 1 秒)上測量位元組速率和執行緒利用率,以便快速檢測和糾正配額違規, 通常,具有較大的測量視窗(例如 10 個視窗,每個視窗 30 秒)會導致大量流量突發,隨后出現長時間延遲,這在用戶體驗方面并不是很好,

 

十一.參考

https://kafka.apache.org/documentation/

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/551006.html

標籤:其他

上一篇:oracle邏輯備份exp匯出指定表名時需要加括號嗎?

下一篇:返回列表

標籤雲
其他(157968) Python(38094) JavaScript(25389) Java(17988) C(15215) 區塊鏈(8259) C#(7972) AI(7469) 爪哇(7425) MySQL(7140) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4558) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2430) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1959) Web開發(1951) HtmlCss(1923) python-3.x(1918) 弹簧靴(1913) C++(1910) xml(1889) PostgreSQL(1873) .NETCore(1854) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • GPU虛擬機創建時間深度優化

    **?桔妹導讀:**GPU虛擬機實體創建速度慢是公有云面臨的普遍問題,由于通常情況下創建虛擬機屬于低頻操作而未引起業界的重視,實際生產中還是存在對GPU實體創建時間有苛刻要求的業務場景。本文將介紹滴滴云在解決該問題時的思路、方法、并展示最終的優化成果。 從公有云服務商那里購買過虛擬主機的資深用戶,一 ......

    uj5u.com 2020-09-10 06:09:13 more
  • 可編程網卡芯片在滴滴云網路的應用實踐

    **?桔妹導讀:**隨著云規模不斷擴大以及業務層面對延遲、帶寬的要求越來越高,采用DPDK 加速網路報文處理的方式在橫向縱向擴展都出現了局限性。可編程芯片成為業界熱點。本文主要講述了可編程網卡芯片在滴滴云網路中的應用實踐,遇到的問題、帶來的收益以及開源社區貢獻。 #1. 資料中心面臨的問題 隨著滴滴 ......

    uj5u.com 2020-09-10 06:10:21 more
  • 滴滴資料通道服務演進之路

    **?桔妹導讀:**滴滴資料通道引擎承載著全公司的資料同步,為下游實時和離線場景提供了必不可少的源資料。隨著任務量的不斷增加,資料通道的整體架構也隨之發生改變。本文介紹了滴滴資料通道的發展歷程,遇到的問題以及今后的規劃。 #1. 背景 資料,對于任何一家互聯網公司來說都是非常重要的資產,公司的大資料 ......

    uj5u.com 2020-09-10 06:11:05 more
  • 滴滴AI Labs斬獲國際機器翻譯大賽中譯英方向世界第三

    **桔妹導讀:**深耕人工智能領域,致力于探索AI讓出行更美好的滴滴AI Labs再次斬獲國際大獎,這次獲獎的專案是什么呢?一起來看看詳細報道吧! 近日,由國際計算語言學協會ACL(The Association for Computational Linguistics)舉辦的世界最具影響力的機器 ......

    uj5u.com 2020-09-10 06:11:29 more
  • MPP (Massively Parallel Processing)大規模并行處理

    1、什么是mpp? MPP (Massively Parallel Processing),即大規模并行處理,在資料庫非共享集群中,每個節點都有獨立的磁盤存盤系統和記憶體系統,業務資料根據資料庫模型和應用特點劃分到各個節點上,每臺資料節點通過專用網路或者商業通用網路互相連接,彼此協同計算,作為整體提供 ......

    uj5u.com 2020-09-10 06:11:41 more
  • 滴滴資料倉庫指標體系建設實踐

    **桔妹導讀:**指標體系是什么?如何使用OSM模型和AARRR模型搭建指標體系?如何統一流程、規范化、工具化管理指標體系?本文會對建設的方法論結合滴滴資料指標體系建設實踐進行解答分析。 #1. 什么是指標體系 ##1.1 指標體系定義 指標體系是將零散單點的具有相互聯系的指標,系統化的組織起來,通 ......

    uj5u.com 2020-09-10 06:12:52 more
  • 單表千萬行資料庫 LIKE 搜索優化手記

    我們經常在資料庫中使用 LIKE 運算子來完成對資料的模糊搜索,LIKE 運算子用于在 WHERE 子句中搜索列中的指定模式。 如果需要查找客戶表中所有姓氏是“張”的資料,可以使用下面的 SQL 陳述句: SELECT * FROM Customer WHERE Name LIKE '張%' 如果需要 ......

    uj5u.com 2020-09-10 06:13:25 more
  • 滴滴Ceph分布式存盤系統優化之鎖優化

    **桔妹導讀:**Ceph是國際知名的開源分布式存盤系統,在工業界和學術界都有著重要的影響。Ceph的架構和演算法設計發表在國際系統領域頂級會議OSDI、SOSP、SC等上。Ceph社區得到Red Hat、SUSE、Intel等大公司的大力支持。Ceph是國際云計算領域應用最廣泛的開源分布式存盤系統, ......

    uj5u.com 2020-09-10 06:14:51 more
  • es~通過ElasticsearchTemplate進行聚合~嵌套聚合

    之前寫過《es~通過ElasticsearchTemplate進行聚合操作》的文章,這一次主要寫一個嵌套的聚合,例如先對sex集合,再對desc聚合,最后再對age求和,共三層嵌套。 Aggregations的部分特性類似于SQL語言中的group by,avg,sum等函式,Aggregation ......

    uj5u.com 2020-09-10 06:14:59 more
  • 爬蟲日志監控 -- Elastc Stack(ELK)部署

    傻瓜式部署,只需替換IP與用戶 導讀: 現ELK四大組件分別為:Elasticsearch(核心)、logstash(處理)、filebeat(采集)、kibana(可視化) 下載均在https://www.elastic.co/cn/downloads/下tar包,各組件版本最好一致,配合fdm會 ......

    uj5u.com 2020-09-10 06:15:05 more
最新发布
  • kafka設計理念決議

    一.引言 kafka是廣泛使用的流處理組件,我們知道怎么使用它,也知道它的實作原理。但是更重要的部分是它的設計理念,即kafka設計者當時是如何考量各種方案的,了解這些,對提升我們的設計能力非常有幫助。 二.動機 我們將 Kafka 設計為一個統一平臺,來處理大型公司可能擁有的所有實時資料流。 為此 ......

    uj5u.com 2023-04-24 09:07:02 more
  • oracle邏輯備份exp匯出指定表名時需要加括號嗎?

    Oracle 的exp、imp、expdp、impdp命令用于資料庫邏輯備份與恢復; exp命令用于把資料從遠程資料庫server匯出至本地,生成dmp檔案。 筆者在實操中遇到: $exp user/pass file=exp.dmp tables = (TABLE1,TABLE3,TABLE3) ......

    uj5u.com 2023-04-24 09:01:52 more
  • 讀《mysql是怎樣運行的》有感

    最近讀了一本書《mysql是怎樣運行的》,讀完后在大體上對mysql的運行有一定的了解。在以前,我對mysql有以下的為什么: InnoDB中的表空間、段、區和頁是什么? redo log為什么就能實作事務的持久性? 到底什么是意向鎖?意向鎖有什么用? mysql中的外連接、內連接到底是什么? 事務 ......

    uj5u.com 2023-04-24 09:01:49 more
  • 《Redis設計與實作》讀書筆記

    《Redis設計與實作》讀書筆記 簡單動態字串 SDS的定義 結構: buf陣列:用于保存字串 len屬性:記錄SDS中保存字串的長度 free屬性:記錄buf中未使用位元組數量 遵循C字串以空字串結尾的慣例,保存空字串的位元組不計入長度 SDS與C字串的區別 常數復雜度獲取字串長度 因 ......

    uj5u.com 2023-04-24 09:01:39 more
  • 袋鼠云春季生長大會圓滿落幕,帶來數實融合下的新產品、新方案、新

    4月20日,以“數實融合,韌性生長”為主題的袋鼠云春季生長大會圓滿落幕。 在春季生長大會中,袋鼠云帶來了數實融合趨勢下的最新行業沉淀、最佳實踐經驗和行業前瞻性的產品發布。從大資料基礎軟體“數堆疊”、到低代碼數字孿生世界“易知微”,再到可觀測運維專家“云掣”,為廣大用戶帶來了一場場精彩內容,共話數字未來 ......

    uj5u.com 2023-04-24 09:00:57 more
  • 無懼百萬級并發,GaussDB(for Cassandra)讓華為推送服務更快觸達

    摘要:推送服務(Push Kit)是華為提供的訊息推送平臺,建立了從云端到終端的訊息推送通道。通過集成推送服務,您可以向客戶端應用實時推送訊息,讓應用更精準觸達用戶,是開發者提升用戶感知度和活躍度的一件利器。 本文分享自華為云社區《無懼百萬級并發,GaussDB(for Cassandra)讓華為P ......

    uj5u.com 2023-04-24 09:00:33 more
  • mysql+proxysql+replication-manager的主從半同步復制+高可用+讀

    環境: AlmaLinux release 9.1 MySQL Community Server Ver 8.0.33 Replication Manager v2.2.40 for MariaDB 10.x and MySQL 5.7 Series ProxySQL version 2.5.1-9 ......

    uj5u.com 2023-04-24 09:00:18 more
  • 華為云GaussDB支撐華為MetaERP系統全面替換

    摘要:目前MetaERP已經覆寫了華為公司100%的業務場景和80%的業務量。 本文分享自華為云社區《強渡大渡河!華為云GaussDB支撐華為MetaERP系統全面替換》,作者: 華為云頭條。 近日,在“英雄強渡大渡河”MetaERP表彰會上,華為宣布實作自主可控的MetaERP研發,并完成對舊ER ......

    uj5u.com 2023-04-24 09:00:07 more
  • 詳解Redis三大集群模式,輕松實作高可用!

    Redis集群是一種通過將多個Redis節點連接在一起以實作高可用性、資料分片和負載均衡的技術。它允許Redis在不同節點上同時提供服務,提高整體性能和可靠性。根據搭建的方式和集群的特性,Redis集群主要有三種模式:主從復制模式(Master-Slave)、哨兵模式(Sentinel)和Clust... ......

    uj5u.com 2023-04-24 08:59:53 more
  • MySQL 移動資料目錄后啟動失敗

    GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯系小編并注明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者: 王權富貴 文章來源:GreatSQL社區投稿 背景概述 由于安裝資料庫時將MySQL的資料目錄放在了根目錄下,現在存盤空間不足,想通過mv將資料 ......

    uj5u.com 2023-04-24 08:59:20 more