日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

kafka优秀设计

發布時間:2024/1/23 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 kafka优秀设计 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

??優秀設計之基于NIO編程?

Kafka 底層的 IO 用的是 NIO,這個事雖然簡單,但是也需要提一提。我們開發一個分布式文件系統的時候避免不了需要思考需要什么樣的 IO?BIO 性能較差,NIO 性能要比 BIO 要好很多,而且編程難度也不算大,當然性能最好的那就是 AIO 了,但是 AIO 編程難度較大,代碼設計起來較為復雜,所以 Kafka 選擇的是 NIO,也是因為這些原因,目前我們看到很多開源的技術也都是用的 NIO。

優秀設計之高性能網絡設計?

個人認為 Kafka 的網絡部分的代碼設計是整個 Kafka 比較精華的部分。我們接下來一步一步分析一下 Kafka Server 端為了支持超高并發是如何設計其網絡架構的?

我們先不看 kafka 本身的網絡架構,我們先簡單了解一下 Reactor 模式:

???圖1 ?Reactor模型

(1) 首先服務端創建了 ServerSocketChannel 對象并在 Selector 上注冊了 OP_ACCEPT 事件,ServerSocketChannel 負責監聽指定端口上的連接。
(2)當客戶端發起到服務端的網絡連接請求時,服務端的 Selector 監聽到 OP_ACCEPT 事件,會觸發 Acceptor 來處理 OP_ACCEPT 事件.
(3)當 Acceptor 接收到來自客戶端的 socket 請求時會為這個連接創建對應的 SocketChannel,將這個 SocketChannel 設置為非阻塞模式,并在 Selector 上注冊它關注的 I/O 事件。如:OP_WRITER,OP_READ 事件。此時客戶端與服務端的 socket 連接正式建立完成。
(4)當客戶端通過上面建立好的 socket 連接向服務端發送請求時,服務端的 Selector 會監聽到 OP_READ 事件,并觸發對應的處理邏輯(read handler)。服務端像客戶端發送響應時,服務端的 Selector 可以監聽到 OP_WRITER 事件,并觸發對應的處理邏輯(writer handler)。

我們看到這種設計就是將所有的事件處理都在同一個線程中完成。這樣的設計適合用在客戶端這種并發比較小的場景。如果并發量比較大,或者有個請求處理邏輯要較為復雜,耗時較長,那么就會影響到后續所有的請求,接著就會導致大量的任務超時。要解決這個問題,我們對上述的架構稍作調整,如下圖所示:

圖2 Reactor 改進模型

Accept 單獨運行在一個線程中,這個線程使用 ExecutorService 實現,因為這樣的話,當 Accept 線程異常退出的時候,ExecutorService 也會創建新的線程進行補償。Read handler 里面也是一個線程池,這個里面所有的線程都注冊了 OP_READ 事件,負責接收客戶端傳過來的請求,當然也是一個線程對應了多個 socket 連接。Read handler 里的線程接收到請求以后把請求都存入到 MessageQueue 里面。Handler Poll 線程池中的線程就會從 MessageQueue 隊列里面獲取請求,然后對請求進行處理。這樣設計的話,即使某個請求需要大量耗時,Handler Poll 線程池里的其它線程也會去處理后面的請求,避免了整個服務端的阻塞。當請求處理完了以后 handler Pool 中的線程注冊 OP_WRITER 事件,實現往客戶端發送響應的功能。

通過這種設計就解決了性能瓶頸的問題,但是如果突然發生了大量的網絡 I/O。單個 Selector 可能會在分發事件的時候成為性能瓶頸。所以我們很容易想的到應該將上面的單獨的 Selector 擴展為多個,所以架構圖就變成了如下的這幅圖:

圖3 Reactor 改進模型

如果我們理解了上面的設計以后,再去理解 Kafka 的網絡架構就簡單多了,如下圖所示:

圖4 Kafka 網絡模型

這個就是 Kafka 的 Server 端的網絡架構設計,就是按照前面的網路架構演化出來的。Accepetor 啟動了以后接收連接請求,接收到了請求以后把請求發送給一個線程池(Processor)線程池里的每個線程獲取到請求以后,把請求封裝為一個個 SocketChannel 緩存在自己的隊列里面。接下來給這些 SocketChannel 注冊上 OP_READ 事件,這樣就可以接收客戶端發送過來的請求了,Processor 線程就把接收到的請求封裝成 Request 對象存入到 RequestChannel 的 RequestQueue 隊列。接下來啟動了一個線程池,默認是 8 個線程來對隊列里面的請求進行處理。處理完了以后把對應的響應放入到對應 ReponseQueue 里面。每個 Processor 線程從其對應的 ReponseQueue 里面獲取響應,注冊 OP_WRITER 事件,最終把響應發送給客戶端。

個人覺得 Kafka 的網絡設計部分代碼設計得很漂亮,就是因為這個網絡架構,保證了 kafka 的高性能。

? 優秀設計之順序寫?

一開始很多人質疑 kafka,大家認為一個架構在磁盤之上的系統,性能是如何保證的。這點需要跟大家解釋一下,客戶端寫入到 Kafka 的數據首先是寫入到操作系統緩存的(所以很快),然后緩存里的數據根據一定的策略再寫入到磁盤,并且寫入到磁盤的時候是順序寫,順序寫如果磁盤的個數和轉數跟得上的話,都快趕上寫內存的速度了!

?優秀設計之跳表、稀松索引、零拷貝

上面我們看到 kafka 通過順序寫的設計保證了高效的寫性能,那讀數據的高性能又是如何設計的呢?kafka 是一個消息系統,里面的每個消息都會有 offset,如果消費者消費某個 offset 的消息的時候是如何快速定位呢?

?

01 /? 跳 表

如下截圖是我們線上的 kafka 的存儲文件,里面有兩個重要的文件,一個是 index 文件,一個是 log 文件。

?

圖5 Kafka 存儲文件

?

log 文件里面存儲的是消息,index 存儲的是索引信息,這兩個文件的文件名都是一樣的,成對出現的,這個文件名是以 log 文件里的第一條消息的 offset 命名的,如下第一個文件的文件名叫 00000000000012768089,代表著這個文件里的第一個消息的 offset 是 12768089,也就是說第二條消息就是 12768090 了。

?

在 kafka 的代碼里,我們一個的 log 文件是存儲是 ConcurrentSkipListMap 里的,是一個 map 結構,key 用的是文件名(也就是 offset),value 就是 log 文件內容。而 ConcurrentSkipListMap 是基于跳表的數據結構設計的。

?

圖6 concurrentSkipListMap設計

?

這樣子,我們想要消費某個大小的 offset,可以根據跳表快速的定位到這個 log 文件了。

02?/? 稀松索引

經過上面的步驟,我們僅僅也就是定位了 log 文件而已,但是要消費的數據具體的物理位置在哪兒?,我們就得靠 kafka 的稀松索引了。假設剛剛我們定位要消費的偏移量是在 00000000000000368769.log 文件里,如果說要整個文件遍歷,然后一個 offset 一個 offset 比對,性能肯定很差。這個時候就需要借助剛剛我們看到的 index 文件了,這個文件里面存的就是消息的 offset 和其對應的物理位置,但 index 不是為每條消息都存一條索引信息,而是每隔幾條數據才存一條 index 信息,這樣 index 文件其實很小,也就是這個原因我們就管這種方式叫稀松索引。

?

圖7 稀松索引

?

比如現在我們要消費 offset 等于 368776 的消息,如何根據 index 文件定位呢?(1)首先在 index 文件里找,index 文件存儲的數據都是成對出現的,比如我們到的 1,0 代表的意思是,offset=368769+1=368770 這條信息存儲的物理位置是 0 這個位置。那現在我們現在想要定位的消息是 368776 這條消息,368776 減去 368769 等于 7,我們就在 index 文件里找 offset 等于 7 對應的物理位置,但是因為是稀松索引,我們沒找到,不過我們找到了 offset 等于 6 的物理值 1407。

(2)接下來就到 log 文件里讀取文件的 1407 的位置,然后遍歷后面的 offset,很快就可以遍歷到 offset 等于 7(368776)的數據了,然后從這兒開始消費即可。

03?/? 零拷貝

接下來消費者讀取數據的流程用的是零拷貝技術,我們先看一下如下是非零拷貝的流程:

(1)操作系統將數據從磁盤文件中讀取到內核空間的頁面緩存;
(2)應用程序將數據從內核空間讀入用戶空間緩沖區;
(3)應用程序將讀到數據寫回內核空間并放入 socket 緩沖區;
(4)操作系統將數據從 socket 緩沖區復制到網卡接口,此時數據才能通過網絡發送。

?

圖8 非零拷貝流程

?

上圖我們發現里面會涉及到兩次數據拷貝,Kafka 這兒為了提升性能,所以就采用了零拷貝,零拷貝”只用將磁盤文件的數據復制到頁面緩存中一次,然后將數據從頁面緩存直接發送到網絡中(發送給不同的訂閱者時,都可以使用同一個頁面緩存),避免了重復復制操作,提升了整個讀數據的性能。

?

圖9 零拷貝流程

?優秀設計之批處理?

在 kafka-0.8 版本的設計中,生產者往服務端發送數據,是一條發送一次,這樣吞吐量低,后來的版本里面加了緩沖區和批量提交的概念,一下子吞吐量提高了很多。下圖就是修改過后的生產者發送消息的原理圖:(1) 消費先被封裝成為 ProducerRecord 對象.
(2)對消息進行序列化(因為涉及到網絡傳輸).
(3)使用分區器進行分區(到這兒就決定了這個消息要被發送到哪兒了).
(4)接著下來這條消息不著急被發送出去,而是被存到緩沖區里.
(5)會有一個 sender 線程,從緩沖區里取數據,把多條數據封裝成一個批次,再一把發送出去,因為有了這個批量發送的設計,吞吐量成倍的提升了。

圖10 緩存區設計

這個緩存區里的代碼技術含量很高,感興趣的同學,可以自己去閱讀以下源碼。

總結

以上是生活随笔為你收集整理的kafka优秀设计的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。