自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

開源分布式事件流平臺(tái)Kafka 雜談

開發(fā) 架構(gòu)
Consumer 從 Broker 里拉取數(shù)據(jù)消費(fèi),那 Consumer 如何知道自己消費(fèi)到哪兒了?Broker 如何知道 Consumer 消費(fèi)到哪兒了?雙方如何達(dá)成共識(shí)?

?概覽

什么是 Kafka?

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

專業(yè)點(diǎn)講,Kafka 是一個(gè)開源的分布式事件流的平臺(tái)。通俗點(diǎn)講,Kafka 就是一個(gè)消息隊(duì)列。

事件流的定義

這才是一個(gè)正常的拋概念的順序,而不是「我們要了解 Kafka,就需要先了解一下 事件流...」

怎么理解這個(gè)事件流呢?拿人來類比的話,你可以簡單的把它理解成人的中樞神經(jīng)系統(tǒng),它是人體神經(jīng)系統(tǒng)最主要的部分。中樞神經(jīng)接收全身各個(gè)部位的信息輸入,然后再發(fā)出命令,讓身體執(zhí)行適當(dāng)?shù)姆磻?yīng)。甚至可以說,神經(jīng)系統(tǒng)可以控制整個(gè)生物的行為。

通過這個(gè)類比相信你能夠理解件流的重要性。

而切回到技術(shù)視角來看,事件流其實(shí)就是從各種類型的數(shù)據(jù)源收取實(shí)時(shí)數(shù)據(jù)。對(duì)應(yīng)到我們平時(shí)對(duì)消息隊(duì)列的用途來說,可以理解為有很多個(gè)不同的、甚至說不同種類的生產(chǎn)者,都能夠向同一個(gè) Topic 寫入消息。

收集到這些事件流后,Kafka 會(huì)將它們持久化起來,然后根據(jù)需要,將這些事件路由給不同的目標(biāo)。也換個(gè)角度理解,一個(gè) Topic 中所存放的消息(或者說事件)可以被不同的消費(fèi)者消費(fèi)。

事件流的用途

現(xiàn)在我們知道了事件流的重要性,上面也拿中樞神經(jīng)系統(tǒng)做了對(duì)比,我們清楚中樞神經(jīng)系統(tǒng)可以做些什么,那么事件流呢?它能拿來做啥呢?

舉例來說,像我們平時(shí)網(wǎng)購東西,上面會(huì)顯示你的快遞現(xiàn)在走到哪里了。這就是通過事件流來實(shí)時(shí)跟蹤、監(jiān)控汽車、卡車或者船只,在物流、汽車行業(yè)這樣用的比較多;比如,持續(xù)的捕獲、分析來自物聯(lián)網(wǎng)設(shè)備或者其他設(shè)備的傳感器數(shù)據(jù);通過監(jiān)測住院病人的數(shù)據(jù),來預(yù)測病人的病情變化等等這些。

那這個(gè)跟 kafka 有啥關(guān)系呢?因?yàn)槌诉@些,還有一個(gè)比較重要的用途那就是作為一個(gè)數(shù)據(jù)平臺(tái)、事件驅(qū)動(dòng)架構(gòu)的基石,而 Kakfa 剛好就是這么一個(gè)平臺(tái)。

Kafka 由來

這塊,之前的文章有過介紹,為了避免贅述我就直接貼過來了

Kafka 最初來自于 LinkedIn,是用于做日志收集的工具,采用Java和Scala開發(fā)。其實(shí)那個(gè)時(shí)候已經(jīng)有 ActiveMQ了,但是在當(dāng)時(shí) ActiveMQ 沒有辦法滿足 LinkedIn 的需求,于是 Kafka 就應(yīng)運(yùn)而生。

在 2010 年底,Kakfa 的0.7.0被開源到了Github上。到了2011年,由于 Kafka 非常受關(guān)注,被納入了 Apache Incubator,所有想要成為 Apache 正式項(xiàng)目的外部項(xiàng)目,都必須要經(jīng)過 Incubator,翻譯過來就是孵化器。旨在將一些項(xiàng)目孵化成完全成熟的 Apache 開源項(xiàng)目。

你也可以把它想象成一個(gè)學(xué)校,所有想要成為 Apache 正式開源項(xiàng)目的外部項(xiàng)目都必須要進(jìn)入 Incubator 學(xué)習(xí),并且拿到畢業(yè)證,才能走入社會(huì)。于是在 2012 年,Kafka 成功從 Apache Incubator 畢業(yè),正式成為 Apache 中的一員。

Kafka 擁有很高的吞吐量,單機(jī)能夠抗下十幾w的并發(fā),而且寫入的性能也很高,能夠達(dá)到毫秒級(jí)別。而且 Kafka的功能較為簡單,就是簡單的接收生產(chǎn)者的消息,消費(fèi)者從 Kafka 消費(fèi)消息。

既然 Kafka 作為一個(gè)高可用的平臺(tái),那么肯定需要對(duì)消息進(jìn)行持久化,不然一旦重啟,所有的消息就都丟了。那 Kafka 是怎么做的持久化呢?

設(shè)計(jì)

持久化

當(dāng)然是磁盤了,并且還是強(qiáng)依賴磁盤。

不了解的可能會(huì)認(rèn)為:「磁盤?不就是那個(gè)很慢很慢的磁盤?」這種速度級(jí)的存儲(chǔ)設(shè)備是怎么樣和 Kafka 這樣的高性能數(shù)據(jù)平臺(tái)沾上邊的?

確實(shí)我們會(huì)看到大量關(guān)于磁盤的描述,就是慢。但實(shí)際上,磁盤同時(shí)集快、慢于一身,其表現(xiàn)具體是快還是慢,還得看我們?nèi)绾问褂盟?/p>

舉個(gè)例子,我們可能都聽過,內(nèi)存的順序 IO 是慢于內(nèi)存的隨機(jī) IO 的,確實(shí)是這樣。磁盤自身的隨機(jī) IO 和順序 IO 也有非常大的差異。比如在某些情況下,磁盤順序?qū)懙乃俣瓤赡苁?600MB/秒,而對(duì)于磁盤隨機(jī)寫的速度可能才 100KB/秒,這個(gè)差異達(dá)到了恐怖的 6000 倍。

對(duì)磁盤的一些原理感興趣可以看看我之前寫的文章

Kafka 其實(shí)就是用實(shí)際行動(dòng)來告訴我們「Don't fear the filesystem」,現(xiàn)在順序?qū)?、讀的性能表現(xiàn)是很穩(wěn)定的,并且我們的大哥操作系統(tǒng)也對(duì)此進(jìn)行了大量的優(yōu)化。

了解了持久化,解決了消息的存、取問題,還有什么更重要呢?

效率

當(dāng)然是效率,持久化能保證你的數(shù)據(jù)不丟,這可能只做到了一半,如果對(duì)消息的處理效率不高,仍然不能滿足實(shí)際生產(chǎn)環(huán)境中海量的數(shù)據(jù)請(qǐng)求。

舉個(gè)例子,現(xiàn)在請(qǐng)求一個(gè)系統(tǒng)的一個(gè)頁面都有可能會(huì)產(chǎn)生好幾十條消息,這個(gè)在復(fù)雜一些的系統(tǒng)里絲毫不夸張。如果投遞、消費(fèi)的效率不提上去,會(huì)影響到整個(gè)核心鏈路。

影響效率的大頭一半來說有兩個(gè):

  • 大量零散的小 IO
  • 大量的數(shù)據(jù)拷貝

這也是為啥大家都要搞 Buffer,例如 MySQL 里有 Log Buffer,操作系統(tǒng)也有自己的 Buffer,這就是要把盡量減少和磁盤的交互,減少小 IO 的產(chǎn)生,提高效率。

比如說,Consumer 現(xiàn)在需要消費(fèi) Broker 上的某條消息,Broker 就需要將此消息從磁盤中讀取出來,再通過 Socket 將消息發(fā)送給 Consumer。那通常拷貝一個(gè)文件再發(fā)送會(huì)涉及到哪些步驟?

  • 用戶態(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)卡會(huì)將數(shù)據(jù)發(fā)送出去
  • 內(nèi)核態(tài)切換到用戶態(tài)

可能你看文字有點(diǎn)懵逼,簡單總結(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ù)的拷貝,都是耗時(shí)的操作,那 Kafka 是怎么解決這個(gè)問題的呢?

其實(shí)就是我們常說的零拷貝了,但是不要看到零就對(duì)零拷貝有誤解,認(rèn)為就是一次都沒有拷貝,那你想想,不拷貝怎么樣把磁盤的數(shù)據(jù)讀取出來呢?

所謂的零拷貝是指數(shù)據(jù)在用戶態(tài)、內(nèi)核態(tài)之間的拷貝次數(shù)是 0。

最初,從磁盤讀取數(shù)據(jù)的時(shí)候是在內(nèi)核態(tài)。

最后,將讀取到的數(shù)據(jù)發(fā)送出去的時(shí)候也在內(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ā)送?!?/p>

這里嚴(yán)格上來說還有 offset 的拷貝,但影響太小可以忽略不就,就先不討論

你會(huì)發(fā)現(xiàn),這里也應(yīng)證了我上面說的「零拷貝并不是說沒有拷貝」。算下來,零拷貝總共也有 2 次態(tài)的切換,2 次數(shù)據(jù)的拷貝。但這已經(jīng)能大大的提升效率了。

到此為止,我們聊到了消息已經(jīng)被發(fā)送出去了,接下來就是消費(fèi)者接收到這條消息然后開始處理了。那這部分會(huì)有效率問題嗎?

答案是肯定的,隨著現(xiàn)在的計(jì)算機(jī)發(fā)展,系統(tǒng)的瓶頸很多時(shí)候已經(jīng)不是 CPU 或者磁盤了,而是網(wǎng)絡(luò)帶寬。對(duì)帶寬不理解的你就把帶寬理解成一條路的寬度。路寬了,就能同時(shí)容納更多的車行進(jìn),堵車的概率也會(huì)小一些。

那在路寬不變的基礎(chǔ)上,我們要怎么樣跑更多的車呢?讓車變?。ìF(xiàn)實(shí)中別這么干,手動(dòng)狗頭)。

換句話說,就是要對(duì)發(fā)送給 Consumer 的信息進(jìn)行壓縮。并且,還不能是來一條壓縮一條,為啥呢?因?yàn)橥愋偷囊慌⒅g會(huì)有大量的重復(fù),將這一批進(jìn)行壓縮能夠極大的減少重復(fù),而相反,壓縮單條消息效果并不理想,因?yàn)槟銢]有辦法提取公共冗余的部分。Kafka 通過批處理來對(duì)消息進(jìn)行批量壓縮。

Push vs Pull

關(guān)于這個(gè)老生常談的問題,確實(shí)可以簡單的聊聊。我們都知道 Consumer 消費(fèi)數(shù)據(jù),無非就是 pull 或者 push??赡茉诖蠖鄶?shù)的情況下,這兩個(gè)沒啥區(qū)別,但實(shí)際上大多數(shù)情況下還是用的 pull 的方式。

那為啥是 pull?

假設(shè)現(xiàn)在是采取的 push 的方式,那么當(dāng) Broker 內(nèi)部出現(xiàn)了問題,向 Consumer push 的頻率降低了,此時(shí)作為消費(fèi)方是不是只能干著急。想象一下,現(xiàn)在產(chǎn)生了消息堆積,我們確啥也干不了,只能等著 Broker 恢復(fù)了繼續(xù) push 消息到 Consumer。

那如果是 pull 我們?cè)趺唇鉀Q呢?我們可以新增消費(fèi)者,以此來增加消費(fèi)的速率。當(dāng)然新增消費(fèi)者并不總是有效,例如在 RocketMQ 中,消費(fèi)者的數(shù)量如果大于了 MessageQueue 的數(shù)量,多出來的這部分消費(fèi)者是無法消費(fèi)消息的,資源就被白白浪費(fèi)了。

Kafka 中的 Partition 也是同理,在新增消費(fèi)者的時(shí)候,也需要注意消費(fèi)者、Partition 的數(shù)量。

除此之外,采用 pull 能使 Consumer 更加的靈活,能夠根據(jù)自己的情況決定什么時(shí)候消費(fèi),消費(fèi)多少。

關(guān)于消費(fèi)

這個(gè)問題其實(shí)在消息系統(tǒng)里也很經(jīng)典。

Consumer 從 Broker 里拉取數(shù)據(jù)消費(fèi),那 Consumer 如何知道自己消費(fèi)到哪兒了?Broker  如何知道 Consumer 消費(fèi)到哪兒了?雙方如何達(dá)成共識(shí)?

我們假設(shè),Broker 在收到 Consumer 的拉取消息請(qǐng)求并發(fā)送之后,就將剛剛發(fā)送的消息給刪除了,這樣 OK 嗎?

廢話,這當(dāng)然不行,假設(shè) Broker 把消息發(fā)給 Consumer 了,但由于 Consumer 掛了并沒有收到這些消息,那這些消息就會(huì)丟失。

所以才有了我們都熟悉的 ACK(Acknowlegement)機(jī)制,Broker 在將消息發(fā)出后,將其標(biāo)識(shí)為「已發(fā)送|未消費(fèi)」,Broker 會(huì)等待 Consumer 返回一個(gè) ACK,然后再將剛剛的消息標(biāo)識(shí)為「已消費(fèi)」。

這個(gè)機(jī)制在一定程度上解決了上面說的消息丟失的問題,但事情總有雙面性, ACK 機(jī)制又引入了新的問題。

舉個(gè)例子,假設(shè) Consumer 收到了、并且正確的消費(fèi)了消息,但偏偏就是在返回 ACK 時(shí)出了問題,導(dǎo)致 Broker 沒有收到。則在 Broker 側(cè),消息的狀態(tài)仍然是「已發(fā)送|未消費(fèi)」,下次 Consumer 來拉,仍然會(huì)拉取到這條消息,此時(shí)就發(fā)生了重復(fù)消費(fèi)。

責(zé)任編輯:武曉燕 來源: SH的全棧筆記
相關(guān)推薦

2019-01-04 11:08:38

開源分布式流存儲(chǔ)Pravega

2023-10-07 08:46:22

KafkaJava

2015-05-12 13:03:54

開源分布式存儲(chǔ)HDFS

2017-07-27 14:32:05

大數(shù)據(jù)分布式消息Kafka

2023-02-28 07:01:11

分布式緩存平臺(tái)

2021-09-09 06:55:43

kafka冪等生產(chǎn)者

2017-08-30 16:47:49

Kafka設(shè)計(jì)原理

2010-06-03 19:46:44

Hadoop

2022-01-10 11:58:51

SpringBootPulsar分布式

2023-10-23 14:35:54

ApacheKafka規(guī)模

2019-10-10 09:16:34

Zookeeper架構(gòu)分布式

2023-05-29 14:07:00

Zuul網(wǎng)關(guān)系統(tǒng)

2017-09-01 05:35:58

分布式計(jì)算存儲(chǔ)

2019-06-19 15:40:06

分布式鎖RedisJava

2023-03-26 12:43:31

數(shù)據(jù)庫KeyValue

2009-02-10 08:57:01

分布式緩存.Net開發(fā)

2014-05-23 10:30:25

負(fù)載均衡分布式架構(gòu)

2022-06-13 07:43:21

分布式Spring

2018-05-19 00:26:13

UAI Train分布式訓(xùn)練
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)