一文詳解消息隊列——Kafka如何實現高性能IO?

一文詳解消息隊列——Kafka如何實現高性能IO?

本文已收錄GitHub,更有互聯網大廠面試真題,面試攻略,高效學習資料等

Kafka 是一個高性能的消息隊列,在衆多消息隊列產品中,Kafka 的性能絕對是處於第一梯隊的。我曾經在一臺配置比較好的服務器上,對 Kafka 作過極限的性能壓測,Kafka 單個節點的極限處理能力接近每秒鐘 2000 萬條消息,吞吐量達到每秒鐘 600MB。git

你可能會問,Kafka 是如何作到這麼高的性能的?github

以前就曾探討過:怎麼開發一個高性能的網絡應用程序。其中提到了像全異步化的線程模型、高性能的異步網絡傳輸、自定義的私有傳輸協議和序列化、反序列化等等,這些方法和優化技巧,你均可以在 Kafka 的源代碼中找到對應的實現。面試

在性能優化方面,除了這些通用的性能優化手段以外,Kafka 還有哪些「獨門絕技」呢?算法

使用批量消息提高服務端處理能力

咱們知道,批量處理是一種很是有效的提高系統吞吐量的方法。在 Kafka 內部,消息都是以「批」爲單位處理的。一批消息從發送端到接收端,是如何在 Kafka 中流轉的呢?編程

咱們先來看發送端,也就是 Producer 這一端。緩存

在 Kafka 的客戶端 SDK(軟件開發工具包)中,Kafka 的 Producer 只提供了單條發送的send() 方法,並無提供任何批量發送的接口。緣由是,Kafka 根本就沒有提供單條發送的功能,是的,你沒有看錯,雖然它提供的 API 每次只能發送一條消息,但實際上,Kafka的客戶端 SDK 在實現消息發送邏輯的時候,採用了異步批量發送的機制。性能優化

當你調用 send() 方法發送一條消息以後,不管你是同步發送仍是異步發送,Kafka 都不會當即就把這條消息發送出去。它會先把這條消息,存放在內存中緩存起來,而後選擇合適的時機把緩存中的全部消息組成一批,一次性發給 Broker。簡單地說,就是攢一波一塊兒發。在 Kafka 的服務端,也就是 Broker 這一端,又是如何處理這一批一批的消息呢?服務器

在服務端,Kafka 不會把一批消息再還原成多條消息,再一條一條地處理,這樣太慢了。網絡

Kafka 這塊兒處理的很是聰明,每批消息都會被當作一個「批消息」來處理。也就是說,在Broker 整個處理流程中,不管是寫入磁盤、從磁盤讀出來、仍是複製到其餘副本這些流程中,批消息都不會被解開,一直是做爲一條「批消息」來進行處理的框架

在消費時,消息一樣是以批爲單位進行傳遞的,Consumer 從 Broker 拉到一批消息後,在客戶端把批消息解開,再一條一條交給用戶代碼處理。

好比說,你在客戶端發送 30 條消息,在業務程序看來,是發送了 30 條消息,而對於Kafka 的 Broker 來講,它其實就是處理了 1 條包含 30 條消息的「批消息」而已。顯然處理 1 次請求要比處理 30 次請求要快得多。

構建批消息和解開批消息分別在發送端和消費端的客戶端完成,不只減輕了 Broker 的壓力,最重要的是減小了 Broker 處理請求的次數,提高了整體的處理能力。

這就是 Kafka 用批量消息提高性能的方法。

咱們知道,相比於網絡傳輸和內存,磁盤 IO 的速度是比較慢的。對於消息隊列的服務端來講,性能的瓶頸主要在磁盤 IO 這一塊。接下來咱們看一下,Kafka 在磁盤 IO 這塊兒作了哪些優化。

使用順序讀寫提高磁盤IO性能

對於磁盤來講,它有一個特性,就是順序讀寫的性能要遠遠好於隨機讀寫。在 SSD(固態硬盤)上,順序讀寫的性能要比隨機讀寫快幾倍,若是是機械硬盤,這個差距會達到幾十倍。爲何呢?

操做系統每次從磁盤讀寫數據的時候,須要先尋址,也就是先要找到數據在磁盤上的物理位置,而後再進行數據讀寫。若是是機械硬盤,這個尋址須要比較長的時間,由於它要移動磁頭,這是個機械運動,機械硬盤工做的時候會發出咔咔的聲音,就是移動磁頭髮出的聲音。

順序讀寫相比隨機讀寫省去了大部分的尋址時間,它只要尋址一次,就能夠連續地讀寫下去,因此說,性能要比隨機讀寫要好不少。

Kafka 就是充分利用了磁盤的這個特性。它的存儲設計很是簡單,對於每一個分區,它把從Producer 收到的消息,順序地寫入對應的 log 文件中,一個文件寫滿了,就開啓一個新的文件這樣順序寫下去。消費的時候,也是從某個全局的位置開始,也就是某一個 log 文件中的某個位置開始,順序地把消息讀出來。

這樣一個簡單的設計,充分利用了順序讀寫這個特性,極大提高了 Kafka 在使用磁盤時的IO 性能。

接下來咱們說一下 Kafka 是如何實現緩存的。

利用PageCache加速消息讀寫

在 Kafka 中,它會利用 PageCache 加速消息讀寫。PageCache 是現代操做系統都具備的一項基本特性。通俗地說,PageCache 就是操做系統在內存中給磁盤上的文件創建的緩存。不管咱們使用什麼語言編寫的程序,在調用系統的 API 讀寫文件的時候,並不會直接去讀寫磁盤上的文件,應用程序實際操做的都是 PageCache,也就是文件在內存中緩存的副本。

應用程序在寫入文件的時候,操做系統會先把數據寫入到內存中的 PageCache,而後再一批一批地寫到磁盤上。讀取文件的時候,也是從 PageCache 中來讀取數據,這時候會出現兩種可能狀況。

一種是 PageCache 中有數據,那就直接讀取,這樣就節省了從磁盤上讀取數據的時間;另外一種狀況是,PageCache 中沒有數據,這時候操做系統會引起一個缺頁中斷,應用程序的讀取線程會被阻塞,操做系統把數據從文件中複製到 PageCache 中,而後應用程序再從PageCache 中繼續把數據讀出來,這時會真正讀一次磁盤上的文件,這個讀的過程就會比較慢。

用戶的應用程序在使用完某塊 PageCache 後,操做系統並不會馬上就清除這個PageCache,而是儘量地利用空閒的物理內存保存這些 PageCache,除非系統內存不夠用,操做系統纔會清理掉一部分 PageCache。清理的策略通常是 LRU 或它的變種算法,這個算法咱們不展開講,它保留 PageCache 的邏輯是:優先保留最近一段時間最常使用的那些 PageCache。

Kafka 在讀寫消息文件的時候,充分利用了 PageCache 的特性。通常來講,消息剛剛寫入到服務端就會被消費,按照 LRU 的「優先清除最近最少使用的頁」這種策略,讀取的時候,對於這種剛剛寫入的 PageCache,命中的概率會很是高。

也就是說,大部分狀況下,消費讀消息都會命中 PageCache,帶來的好處有兩個:一個是讀取的速度會很是快,另一個是,給寫入消息讓出磁盤的 IO 資源,間接也提高了寫入的性能。

ZeroCopy:零拷貝技術

Kafka 的服務端在消費過程當中,還使用了一種「零拷貝」的操做系統特性來進一步提高消費
的性能。

咱們知道,在服務端,處理消費的大體邏輯是這樣的:

  • 首先,從文件中找到消息數據,讀到內存中;
  • 而後,把消息經過網絡發給客戶端。

這個過程當中,數據實際上作了 2 次或者 3 次複製:

  1. 從文件複製數據到 PageCache 中,若是命中 PageCache,這一步能夠省掉;
  2. 從 PageCache 複製到應用程序的內存空間中,也就是咱們能夠操做的對象所在的內存;
  3. 從應用程序的內存空間複製到 Socket 的緩衝區,這個過程就是咱們調用網絡應用框架的 API 發送數據的過程。

Kafka 使用零拷貝技術能夠把這個複製次數減小一次,上面的 二、3 步驟兩次複製合併成一次複製。直接從 PageCache 中把數據複製到 Socket 緩衝區中,這樣不只減小一次數據複製,更重要的是,因爲不用把數據複製到用戶內存空間,DMA 控制器能夠直接完成數據複製,不須要 CPU 參與,速度更快。

下面是這個零拷貝對應的系統調用:

#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

它的前兩個參數分別是目的端和源端的文件描述符,後面兩個參數是源端的偏移量和複製數據的長度,返回值是實際複製數據的長度。

若是你遇到這種從文件讀出數據後再經過網絡發送出去的場景,而且這個過程當中你不須要對這些數據進行處理,那必定要使用這個零拷貝的方法,能夠有效地提高性能。

總結

咱們總結了 Kafka 的高性能設計中的幾個關鍵的技術點:

  • 使用批量處理的方式來提高系統吞吐能力。
  • 基於磁盤文件高性能順序讀寫的特性來設計的存儲結構。
  • 利用操做系統的 PageCache 來緩存數據,減小 IO 並提高讀性能。
  • 使用零拷貝技術加速消費流程。

以上這些,就是 Kafka 之因此能作到如此高性能的關鍵技術點。你能夠看到,要真正實現一個高性能的消息隊列,是很是不容易的,你須要熟練掌握很是多的編程語言和操做系統的底層技術。這些優化的方法和技術,一樣能夠用在其餘適合的場景和應用程序中。我但願你能充分理解這幾項優化技術的原理,知道它們在什麼狀況下適用,什麼狀況下不適用。這樣,當你遇到合適場景的時候,再深刻去學習它的細節用法,最終就能把它真正地用到你開發的程序中。

相關文章
相關標籤/搜索