新聞中心
?概覽
什么是 Kafka?
這里先給出結(jié)論,我不太希望在解釋概念 X 的時候,說到「為了了解 X,我們需要先了解一下 Y」,閱讀的人思緒會被遷到另一個地方。既然小標題里說了要解釋什么是 Kafka,那么我們就只說什么是 Kafka。

成都創(chuàng)新互聯(lián)作為成都網(wǎng)站建設(shè)公司,專注網(wǎng)站建設(shè)、網(wǎng)站設(shè)計,有關(guān)成都定制網(wǎng)站方案、改版、費用等問題,行業(yè)涉及成都門窗定制等多個領(lǐng)域,已為上千家企業(yè)服務(wù),得到了客戶的尊重與認可。
專業(yè)點講,Kafka 是一個開源的分布式事件流的平臺。通俗點講,Kafka 就是一個消息隊列。
事件流的定義
這才是一個正常的拋概念的順序,而不是「我們要了解 Kafka,就需要先了解一下 事件流...」
怎么理解這個事件流呢?拿人來類比的話,你可以簡單的把它理解成人的中樞神經(jīng)系統(tǒng),它是人體神經(jīng)系統(tǒng)最主要的部分。中樞神經(jīng)接收全身各個部位的信息輸入,然后再發(fā)出命令,讓身體執(zhí)行適當?shù)姆磻?yīng)。甚至可以說,神經(jīng)系統(tǒng)可以控制整個生物的行為。
通過這個類比相信你能夠理解件流的重要性。
而切回到技術(shù)視角來看,事件流其實就是從各種類型的數(shù)據(jù)源收取實時數(shù)據(jù)。對應(yīng)到我們平時對消息隊列的用途來說,可以理解為有很多個不同的、甚至說不同種類的生產(chǎn)者,都能夠向同一個 Topic 寫入消息。
收集到這些事件流后,Kafka 會將它們持久化起來,然后根據(jù)需要,將這些事件路由給不同的目標。也換個角度理解,一個 Topic 中所存放的消息(或者說事件)可以被不同的消費者消費。
事件流的用途
現(xiàn)在我們知道了事件流的重要性,上面也拿中樞神經(jīng)系統(tǒng)做了對比,我們清楚中樞神經(jīng)系統(tǒng)可以做些什么,那么事件流呢?它能拿來做啥呢?
舉例來說,像我們平時網(wǎng)購東西,上面會顯示你的快遞現(xiàn)在走到哪里了。這就是通過事件流來實時跟蹤、監(jiān)控汽車、卡車或者船只,在物流、汽車行業(yè)這樣用的比較多;比如,持續(xù)的捕獲、分析來自物聯(lián)網(wǎng)設(shè)備或者其他設(shè)備的傳感器數(shù)據(jù);通過監(jiān)測住院病人的數(shù)據(jù),來預(yù)測病人的病情變化等等這些。
那這個跟 kafka 有啥關(guān)系呢?因為除了這些,還有一個比較重要的用途那就是作為一個數(shù)據(jù)平臺、事件驅(qū)動架構(gòu)的基石,而 Kakfa 剛好就是這么一個平臺。
Kafka 由來
這塊,之前的文章有過介紹,為了避免贅述我就直接貼過來了
Kafka 最初來自于 LinkedIn,是用于做日志收集的工具,采用Java和Scala開發(fā)。其實那個時候已經(jīng)有 ActiveMQ了,但是在當時 ActiveMQ 沒有辦法滿足 LinkedIn 的需求,于是 Kafka 就應(yīng)運而生。
在 2010 年底,Kakfa 的0.7.0被開源到了Github上。到了2011年,由于 Kafka 非常受關(guān)注,被納入了 Apache Incubator,所有想要成為 Apache 正式項目的外部項目,都必須要經(jīng)過 Incubator,翻譯過來就是孵化器。旨在將一些項目孵化成完全成熟的 Apache 開源項目。
你也可以把它想象成一個學(xué)校,所有想要成為 Apache 正式開源項目的外部項目都必須要進入 Incubator 學(xué)習(xí),并且拿到畢業(yè)證,才能走入社會。于是在 2012 年,Kafka 成功從 Apache Incubator 畢業(yè),正式成為 Apache 中的一員。
Kafka 擁有很高的吞吐量,單機能夠抗下十幾w的并發(fā),而且寫入的性能也很高,能夠達到毫秒級別。而且 Kafka的功能較為簡單,就是簡單的接收生產(chǎn)者的消息,消費者從 Kafka 消費消息。
既然 Kafka 作為一個高可用的平臺,那么肯定需要對消息進行持久化,不然一旦重啟,所有的消息就都丟了。那 Kafka 是怎么做的持久化呢?
設(shè)計
持久化
當然是磁盤了,并且還是強依賴磁盤。
不了解的可能會認為:「磁盤?不就是那個很慢很慢的磁盤?」這種速度級的存儲設(shè)備是怎么樣和 Kafka 這樣的高性能數(shù)據(jù)平臺沾上邊的?
確實我們會看到大量關(guān)于磁盤的描述,就是慢。但實際上,磁盤同時集快、慢于一身,其表現(xiàn)具體是快還是慢,還得看我們?nèi)绾问褂盟?/p>
舉個例子,我們可能都聽過,內(nèi)存的順序 IO 是慢于內(nèi)存的隨機 IO 的,確實是這樣。磁盤自身的隨機 IO 和順序 IO 也有非常大的差異。比如在某些情況下,磁盤順序?qū)懙乃俣瓤赡苁?600MB/秒,而對于磁盤隨機寫的速度可能才 100KB/秒,這個差異達到了恐怖的 6000 倍。
對磁盤的一些原理感興趣可以看看我之前寫的文章
Kafka 其實就是用實際行動來告訴我們「Don't fear the filesystem」,現(xiàn)在順序?qū)?、讀的性能表現(xiàn)是很穩(wěn)定的,并且我們的大哥操作系統(tǒng)也對此進行了大量的優(yōu)化。
了解了持久化,解決了消息的存、取問題,還有什么更重要呢?
效率
當然是效率,持久化能保證你的數(shù)據(jù)不丟,這可能只做到了一半,如果對消息的處理效率不高,仍然不能滿足實際生產(chǎn)環(huán)境中海量的數(shù)據(jù)請求。
舉個例子,現(xiàn)在請求一個系統(tǒng)的一個頁面都有可能會產(chǎn)生好幾十條消息,這個在復(fù)雜一些的系統(tǒng)里絲毫不夸張。如果投遞、消費的效率不提上去,會影響到整個核心鏈路。
影響效率的大頭一半來說有兩個:
- 大量零散的小 IO
- 大量的數(shù)據(jù)拷貝
這也是為啥大家都要搞 Buffer,例如 MySQL 里有 Log Buffer,操作系統(tǒng)也有自己的 Buffer,這就是要把盡量減少和磁盤的交互,減少小 IO 的產(chǎn)生,提高效率。
比如說,Consumer 現(xiàn)在需要消費 Broker 上的某條消息,Broker 就需要將此消息從磁盤中讀取出來,再通過 Socket 將消息發(fā)送給 Consumer。那通??截愐粋€文件再發(fā)送會涉及到哪些步驟?
- 用戶態(tài)切換到內(nèi)核態(tài),操作系統(tǒng)將消息從磁盤中讀取到內(nèi)核緩沖區(qū)
- 內(nèi)核態(tài)切換到用戶態(tài),應(yīng)用將內(nèi)核緩沖區(qū)的數(shù)據(jù) Copy 到用戶緩沖區(qū)
- 用戶態(tài)切換到內(nèi)核態(tài),應(yīng)用將用戶緩沖區(qū)的內(nèi)容 Copy 到 Socket 緩沖區(qū)
- 將數(shù)據(jù)庫 Copy 到網(wǎng)卡,網(wǎng)卡會將數(shù)據(jù)發(fā)送出去
- 內(nèi)核態(tài)切換到用戶態(tài)
可能你看文字有點懵逼,簡單總結(jié)就是,涉及到了 4 次態(tài)的切換,4 次數(shù)據(jù)的拷貝,2次系統(tǒng)調(diào)用。
紅色的是態(tài)的切換,綠色的是數(shù)據(jù)拷貝。
不清楚什么是用戶態(tài)、內(nèi)核態(tài)的可以去看看《用戶態(tài)和內(nèi)核態(tài)的區(qū)別》
態(tài)的切換、數(shù)據(jù)的拷貝,都是耗時的操作,那 Kafka 是怎么解決這個問題的呢?
其實就是我們常說的零拷貝了,但是不要看到零就對零拷貝有誤解,認為就是一次都沒有拷貝,那你想想,不拷貝怎么樣把磁盤的數(shù)據(jù)讀取出來呢?
所謂的零拷貝是指數(shù)據(jù)在用戶態(tài)、內(nèi)核態(tài)之間的拷貝次數(shù)是 0。
最初,從磁盤讀取數(shù)據(jù)的時候是在內(nèi)核態(tài)。
最后,將讀取到的數(shù)據(jù)發(fā)送出去的時候也在內(nèi)核態(tài)。
那讀取——發(fā)送這中間,是不是就沒有必要再將數(shù)據(jù)從內(nèi)核態(tài)拷貝到用戶態(tài)了?Linux 里封裝好的系統(tǒng)調(diào)用 sendfile 就已經(jīng)幫我們做了這件事了。
簡單描述一下:「在從磁盤將數(shù)據(jù)讀取到內(nèi)核態(tài)的緩沖區(qū)內(nèi)之后(也就是 pagecache),直接將其拷貝到網(wǎng)卡里,然后發(fā)送。」
這里嚴格上來說還有 offset 的拷貝,但影響太小可以忽略不就,就先不討論
你會發(fā)現(xiàn),這里也應(yīng)證了我上面說的「零拷貝并不是說沒有拷貝」。算下來,零拷貝總共也有 2 次態(tài)的切換,2 次數(shù)據(jù)的拷貝。但這已經(jīng)能大大的提升效率了。
到此為止,我們聊到了消息已經(jīng)被發(fā)送出去了,接下來就是消費者接收到這條消息然后開始處理了。那這部分會有效率問題嗎?
答案是肯定的,隨著現(xiàn)在的計算機發(fā)展,系統(tǒng)的瓶頸很多時候已經(jīng)不是 CPU 或者磁盤了,而是網(wǎng)絡(luò)帶寬。對帶寬不理解的你就把帶寬理解成一條路的寬度。路寬了,就能同時容納更多的車行進,堵車的概率也會小一些。
那在路寬不變的基礎(chǔ)上,我們要怎么樣跑更多的車呢?讓車變?。ìF(xiàn)實中別這么干,手動狗頭)。
換句話說,就是要對發(fā)送給 Consumer 的信息進行壓縮。并且,還不能是來一條壓縮一條,為啥呢?因為同類型的一批消息之間會有大量的重復(fù),將這一批進行壓縮能夠極大的減少重復(fù),而相反,壓縮單條消息效果并不理想,因為你沒有辦法提取公共冗余的部分。Kafka 通過批處理來對消息進行批量壓縮。
Push vs Pull
關(guān)于這個老生常談的問題,確實可以簡單的聊聊。我們都知道 Consumer 消費數(shù)據(jù),無非就是 pull 或者 push??赡茉诖蠖鄶?shù)的情況下,這兩個沒啥區(qū)別,但實際上大多數(shù)情況下還是用的 pull 的方式。
那為啥是 pull?
假設(shè)現(xiàn)在是采取的 push 的方式,那么當 Broker 內(nèi)部出現(xiàn)了問題,向 Consumer push 的頻率降低了,此時作為消費方是不是只能干著急。想象一下,現(xiàn)在產(chǎn)生了消息堆積,我們確啥也干不了,只能等著 Broker 恢復(fù)了繼續(xù) push 消息到 Consumer。
那如果是 pull 我們怎么解決呢?我們可以新增消費者,以此來增加消費的速率。當然新增消費者并不總是有效,例如在 RocketMQ 中,消費者的數(shù)量如果大于了 MessageQueue 的數(shù)量,多出來的這部分消費者是無法消費消息的,資源就被白白浪費了。
Kafka 中的 Partition 也是同理,在新增消費者的時候,也需要注意消費者、Partition 的數(shù)量。
除此之外,采用 pull 能使 Consumer 更加的靈活,能夠根據(jù)自己的情況決定什么時候消費,消費多少。
關(guān)于消費
這個問題其實在消息系統(tǒng)里也很經(jīng)典。
Consumer 從 Broker 里拉取數(shù)據(jù)消費,那 Consumer 如何知道自己消費到哪兒了?Broker 如何知道 Consumer 消費到哪兒了?雙方如何達成共識?
我們假設(shè),Broker 在收到 Consumer 的拉取消息請求并發(fā)送之后,就將剛剛發(fā)送的消息給刪除了,這樣 OK 嗎?
廢話,這當然不行,假設(shè) Broker 把消息發(fā)給 Consumer 了,但由于 Consumer 掛了并沒有收到這些消息,那這些消息就會丟失。
所以才有了我們都熟悉的 ACK(Acknowlegement)機制,Broker 在將消息發(fā)出后,將其標識為「已發(fā)送|未消費」,Broker 會等待 Consumer 返回一個 ACK,然后再將剛剛的消息標識為「已消費」。
這個機制在一定程度上解決了上面說的消息丟失的問題,但事情總有雙面性, ACK 機制又引入了新的問題。
舉個例子,假設(shè) Consumer 收到了、并且正確的消費了消息,但偏偏就是在返回 ACK 時出了問題,導(dǎo)致 Broker 沒有收到。則在 Broker 側(cè),消息的狀態(tài)仍然是「已發(fā)送|未消費」,下次 Consumer 來拉,仍然會拉取到這條消息,此時就發(fā)生了重復(fù)消費。
網(wǎng)站題目:開源分布式事件流平臺Kafka雜談
本文URL:http://fisionsoft.com.cn/article/cdidhdc.html


咨詢
建站咨詢
