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

快到起飛的Kafka,是如何設(shè)計的?

開發(fā) 架構(gòu) 開發(fā)工具 Kafka
消息隊列是分布式系統(tǒng)中重要的組件,在很多生產(chǎn)環(huán)境如商品搶購等需要控制并發(fā)量的場景下都需要用到。

[[409438]]

圖片來自包圖網(wǎng)

消息隊列主要解決了應(yīng)用耦合、異步處理、流量削鋒等問題,當前使用較多的消息隊列有 RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMQ 等。

而部分數(shù)據(jù)庫如 Redis,MySQL 以及 phxsql 也可實現(xiàn)消息隊列的功能。

消息隊列在實際應(yīng)用場景:

  • 應(yīng)用解耦:多應(yīng)用間通過消息隊列對同一消息進行處理,避免調(diào)用接口失敗導致整個過程失敗。
  • 異步處理:多應(yīng)用對消息隊列中同一消息進行處理,應(yīng)用間并發(fā)處理消息,相比串行處理,減少處理時間。
  • 限流削峰:廣泛應(yīng)用于秒殺或搶購活動中,避免流量過大導致應(yīng)用系統(tǒng)掛掉的情況。

今天分享一篇經(jīng)典的 Kafka 設(shè)計剖析文章給大家,Kafka 作為頂級消息中間件,據(jù) Confluent 稱,超過三分之一的財富 500 強公司使用 Apache Kafka。

Kafka 的性能快,吞吐量大,并且高于其他消息隊列一個水平,即使在消息量巨大的情況下還能保持高性能,在互聯(lián)網(wǎng)公司中非常流行,希望大家領(lǐng)悟到 Kafka 設(shè)計的核心原理。

Kafka 架構(gòu)

Kafka 是一個被精心設(shè)計的東西,我只能這樣說。我這里所謂的精心不是說它很完備的實現(xiàn)了某種規(guī)范。

像個學生那般完成了某個作業(yè),比如 JMS,恰恰相反,Kafka 突破了類似 JMS 這種規(guī)范性的束縛,它是卓越的,乃 yet another JMS。

當我用 yet…如此稱呼一個技術(shù)的時候,意味著這玩意兒已經(jīng)進入了我的視野。好了,現(xiàn)在是 Kafka 和 Storm 時間,本文先談 Kafka。

Kafka 是什么?

參見官方文檔,它是 Apache 的一個項目。它是一個消息隊列。

消息隊列若何:消息隊列是生產(chǎn)者和消費者之間的信使,避免了二者之間直接的接觸。

在效果上,它可能和緩存所起的作用一樣,平滑了生產(chǎn)者和消費者之間的代謝速率差,但是在其根本目的上,它是為了解除生產(chǎn)者和消費者之間的耦合。如果你覺得有點費解,那么簡單點說。

fire and forget,這句話的意思再簡單點說,就是真男人從不看爆炸,煙頭往油箱里一丟,把風衣的領(lǐng)子一豎,手插褲兜里,徑直走開,決不不回頭。

消息隊列,以下簡稱 MQ,就是造就這種真男人的。它能讓生產(chǎn)者把消息扔進 MQ 就不管了,然后消費者從 MQ 里取消息即可,不用和生產(chǎn)者交互。

下面的篇幅,我將逐步用我的方式演化出 Kafka 的原型,為了掌握整體脈絡(luò),難免會隱掉很多細節(jié)。

當然這些細節(jié)可以隨便在其官方文檔以及別人的博客里搜到,我的目的只是希望能整理出一個脈絡(luò),在設(shè)計類似的系統(tǒng)的時候,見招拆招以備參考。

MQ 朝著“正確”方向的演化

Kafka 就一定正確嗎?客觀講,肯定不,但是它是本文的主角,所以它就一定正確。

我們先來看看作為通用的 MQ,其最簡單的形式,一般而言,這是大家在首次接觸到 MQ 后的一個課后作業(yè)。

現(xiàn)在有個問題,如果有兩個或者多個消費者需要消費消息,怎么辦?很簡單,廣播唄:

消費者是上帝,很難搞的,你推給它們的東西,并不是它們?nèi)慷枷胍?,只要一部分怎么辦?

好吧,消費者一定在怪 MQ 服務(wù)不周,然而 MQ 有什么錯,它又不理解消息的語義,面對百般刁難的消費者,它最多只能要求生產(chǎn)者把消息細分一下,因此就出現(xiàn)了多個 Topic:

這是很顯然的想法,就是是在消息入隊處區(qū)分消息的 Topic,然消費者從取自己感興趣的消息隊列取消息即可。

但還是會潛在的多個消費對同一 Topic 消息感興趣的情況:

如果采用廣播,那么就仍然會出現(xiàn)冗余傳播問題,如果單播,那么一個消費者取出消息后,這條消息該不該刪除呢?如果刪除了,另一個消費者怎么辦?廣播會浪費帶寬,不廣播也不行…

這貌似進入了一個死循環(huán),必須一勞永逸地從根源解決問題才行。顯然的想法是下面的方案(至少我自己設(shè)計的話就會這么做):

問題是解決了,然而我的天啊,仔細想一下先前的架構(gòu),把簡圖畫出來后,會發(fā)現(xiàn)事情會一發(fā)而不可收拾,MQ 本身的邏輯太復雜了:

回到 UNIX 哲學,遇到新問題的時候,要新編一個程序,而不是為已有的程序添加一個功能。

本著這個思路,為什么不把這件因為消費者而導致復雜化的事情完全交給消費者呢?

有點往 Kafka 上靠了啊。如果把 MQ 里面的數(shù)據(jù)全部持久化存儲,消費者不就可以各取所需了嗎?

這是一個根本的轉(zhuǎn)變,如果以前的方式是限量商務(wù)套餐-套餐強行推給你,不想要的自己扔掉,那么現(xiàn)在的方式就是無限量自助餐-想要什么自己去拿即可。

消息自取,消息永遠都在 MQ,消費者隨便取,取哪個消息都行,什么時候取都行。

消費者只需要告訴 MQ 它想要哪個消息就好,因此需要傳遞一個消息的 offset 參數(shù):

然而自助餐也有打烊的時候,部分也會限制就餐時長,這是 Kafka 策略化存儲的問題,詳見文檔。

簡化一下,現(xiàn)在看下圖:

一切 OK 了。嗯,是的,這就是 Kafka 的原始模型。然而 Kafka 遠不僅此而已。且看下文繼續(xù)演化。

集群化,容錯

先看一下現(xiàn)在的情況:

這是在邏輯上一個 Kafka 類似的 MQ 應(yīng)有的結(jié)構(gòu)。但是在物理實現(xiàn)上,它又如何呢?

常聽人說,Kafka 一開始就是為分布式而生的,這話怎么理解呢?我們只需要先理解它如何擴容,然后再理解它如何將擴容作用于不同的機器即可。先看擴容。

類似高速公路,一般當你聽到廣深高速的時候,我們知道這是從廣州到深圳的一條高速公路,這是邏輯上的說法,類似到目前為止我們討論的 MQ 的 Topic。

然而這條高速公路到底長什么樣子,沿途怎么路由,這就是物理實現(xiàn)了。此外,所有的道路都會分多個車道用于并行。

嚴格來講,每一個車道都會被細分,比如小型車道,客車道,大貨車道,超車道等等,所有這些車道上的車都是到達同一個目的地(屬于同一個 Topic),然而它們確實是細分的不同種類。

把一個叫做 partition 的概念類比為車道,如下圖:

注意這個 key hash 模塊,這里就是區(qū)分車子要進入哪個車道的邏輯。在 Kafka 的術(shù)語中,車道就是 partition,即分區(qū)。

在同一個 Topic 中分發(fā)消息的時候,你要自己設(shè)計 hash 函數(shù),該 hash 函數(shù)就是一個分發(fā)策略,決定把消息按序放到哪一個分區(qū)中去。

溫州皮鞋廠老板說類比和舉例不好,但這是技術(shù)散文,不是技術(shù)文檔,多半是給自己看,所以還要類比。

Topic Routing 做的事是決定從哪條高速公路到哪里,而 key hash 則是決定你是坐轎車,客車還是卡車過去。

值得注意的是,Kafka 只保證同一 Topic 內(nèi)同一 partition 內(nèi)消息的有序性,無法做到全局有序性。

這并不是一個缺陷,這是兩全不能齊美的。完全的順序就需要串行化,然而串行化就無法并行,這簡直就是廢話!

現(xiàn)在,在 Topic 之下,我們又有了一個新的單位,叫做 partition,這個叫做 partition 的就是 Kafka 中最基本的部署單位,這一點務(wù)必要記住,它關(guān)乎到如何組織你的集群。

好了,看一下這些 Topic 以及其旗下的 partition 是如何部署在 M1 和 M2 兩臺機器上的吧:

以上是花開兩朵,各表一枝,現(xiàn)在該說說消費者了。

消費者面對 MQ 本身進化到如此細粒度,該如何應(yīng)對呢?其實消費者也有橫向擴展的需求,如果說消費者對應(yīng) partition,那么對應(yīng) Topic 的就是消費者的上級了。

因此多加了一個層次,引出消費組的概念,解決問題:

從 CPU cache 到 Kafka,設(shè)計思路殊途同歸,這就是一個典型的全方位組相聯(lián)結(jié)構(gòu):

到此為止,全部圖景已經(jīng)完全繪制完畢,是時候展示集群的部署了。我們知道所謂的 Kafka 集群,就是將各個 Topic 的 partition 部署在不同的機器上,達到兩個目的。

一個是負載均衡,即提供訪問的并行性,另一個就是提供高可用性,即做熱備份,這兩個功能我希望能用一個圖展示:

總體的一個結(jié)構(gòu)如下:

持久化存儲/查詢機制

上面的兩個小節(jié),我已經(jīng)展示了 Kafka 是如何一步一步地肚子里面的勾當內(nèi)外有別的,雖然我不知道作者怎么去設(shè)計,但如果是我自己,我肯定就是上面這個思路了…

前面的敘述終究是概覽,不甚過癮。本節(jié)將給出半點細節(jié),瑾闡釋一下 Kafka 存儲的半景。

我們知道,Kafka 為了卸載 MQ 本身的復雜性,為了其真正無狀態(tài)的設(shè)計,它將狀態(tài)維護機制這口鍋完全甩給了消費者。

因此取消息的問題就轉(zhuǎn)化成了消費者拿著一個 offset 索引來 Kafka 存儲器里取消息的問題,這就涉及到了性能。But 如何能查的更快?How?

還是先給出一個最簡單的場景。假設(shè) Kafka 的每一個 partition 都一個完整獨立的文件,那么如果這個文件非常大,事實上也確實非常大(有可能到達 T 級別甚至 P 級別…)。

那么在大文件中檢索一個特定的消息本身就是一個頭疼的問題,并且該文件還在磁盤中,這更是雪上加霜,我們都知道磁盤的隨機讀寫是硬傷,順序讀寫也好不到哪去,這怎么辦?

遍歷?如果每一個 partition 只是一個獨立文件,那么只能遍歷:

面對這個遍歷問題,一般的解決方案就是建立索引,并且把索引數(shù)據(jù)常駐內(nèi)存,很多數(shù)據(jù)庫就是這么干的,Kafka 當然也可以這么干。

Kafka 比較帥的一點就是它并不借助任何特殊的文件系統(tǒng),它的數(shù)據(jù)就存在一般的文件中。

然而它把一個 partition 分成了等大小的一系列小文件,因此在物理上,并不存在一個完整的 partition 文件,partiotion 只是表現(xiàn)為一個目錄。

我們知道,文件系統(tǒng)管理幾個等大的文件是非常方便的:

以上的例子中,一個 partiton 被分成了 100M 大小的文件,這種小文件叫做分段。

在 Kafka 存儲的時候,每一個段文件存滿為止再開辟下一個,由于消息的長度并不一定統(tǒng)一,因此每一個小段文件里面包含的消息數(shù)量并不一定一樣多。

但是不管怎樣,抽取每一個段文件的首尾消息偏移作為元數(shù)據(jù)保存起來是一件一勞永逸的事情,這便于建立一個常駐內(nèi)存的索引:

通過這個區(qū)間查找樹,很快就能定位到特定的段文件,但是事情并沒有結(jié)束。

在 Kafka 中,每一個 partition 的段文件,均配帶一個 index 索引文件,這個文件是做什么的呢?它是段文件內(nèi)部消息的稀疏索引,見下圖:

最終,經(jīng)過兩次區(qū)間樹查找之后,最多再經(jīng)歷一次簡單的遍歷即可完成 offset 定位工作。

誠然,最終的遍歷可能是少不了的,但是 Kafka 盡可能地避免了大長段耗時的遍歷計算,而是將遍歷壓縮到一個很小的量級,這是一個權(quán)衡!跟誰權(quán)衡呢?為什么不把段文件所有消息的索引均建立起來呢?

很簡單,建立全部的索引會造成索引非常大,這樣如果你還想其常駐內(nèi)存的話,內(nèi)存占用會很大,這確實又是一個時間和空間之間的權(quán)衡了。

稀疏索引閑談

稀疏索引很有用,除了本文列舉的 Kafka 的 segment index 稀疏索引之外,還有兩個更為常見的例子(我不是應(yīng)用編程的,我是搞內(nèi)核網(wǎng)絡(luò)協(xié)議棧的,所以在我看來 Kafka 更不常見)

索引整個內(nèi)存地址空間,稀疏化的做法就是分頁,即采用規(guī)則的方式將內(nèi)存劃分為等大小的塊,叫做內(nèi)存頁,然后索引這些內(nèi)存頁即可,頁表而不是地址表稀疏化索引,減小索引的大小。

另外,IP 地址具有地域聚集性,因此對于路由器物理設(shè)備而言,對于每一個接口引出的方向,其 IP 地址集在很大程度上是可以聚集的。

路由表一開始采用地址分類的方法,后來采用了前綴匹配的方法稀疏化索引,地址分類有點像內(nèi)存地址分頁,只是頁面有多種大小而不僅僅是一種。

而這里的地址前綴則比較像 Kafka 使用的兩種索引,第一種是段索引,這是規(guī)則的,第二種是消息索引,這是不規(guī)則的。因為消息并不定長。

兩種說法總結(jié)如下:

OS 內(nèi)存頁表:在從虛擬地址定位物理地址的時候,需要一一對應(yīng)定位到每一個地址嗎?

假如真是這樣子,那么光頁表項這種管理內(nèi)存就要耗多少你算過嗎?虛擬地址和物理地址將會是全相聯(lián)結(jié)構(gòu)。

采用稀疏索引后,只需要定位一個 4K 大小的頁面即可,這將大大減小內(nèi)存頁表的內(nèi)存占用。從而更加高效。

路由表:將每一個 IP 地址均對應(yīng)到路由器設(shè)備的接口嗎?這不現(xiàn)實。解決方案一開始是基于分配機構(gòu)的分類地址稀疏索引,后來采用了基于使用結(jié)構(gòu)的無類子網(wǎng)的前綴系數(shù)索引,無論哪種情況,均大大減少了路由表項的數(shù)量。

UNIX 哲學的出路

沒出路了!Why?因為只有復雜才能體現(xiàn)自己的工作量。

人們都希望制造門檻,把程序做的非常復雜,方才體現(xiàn)自己的能力,畢竟簡單的東西大家都會,想體現(xiàn)區(qū)別,只能讓自己的東西更復雜。

如果你用幾行 Bash 腳本完成了一項艱巨的工作,經(jīng)理大概率會覺得你這是奇技淫巧,完全無法和 C++ 的方案相比。Python 好一點,Java 則更好。

4,5 年以前的曾經(jīng),我們有個編程道場的活動,有一次的一個題目是拼接字符串,即 join 操作,當時的經(jīng)理兼主持者強調(diào)盡量用現(xiàn)成的接口,然而…

多少個優(yōu)秀的極簡方案沒有被表揚,最后被表揚的方案你們知道其特征是什么嗎?其特征就是復雜。

我記得當時這個方案的作者上臺介紹他的方案,上來就說”我這個設(shè)計非常簡單…”結(jié)果呢,唉,用技術(shù)術(shù)語講,過度設(shè)計了,用白話講,裝逼了。主持者顯然也是完全半瓶子晃蕩的吧,哈哈。

事情必須做的盡量復雜,這樣才是能力的體現(xiàn),2 行能搞定的東西,必須湊夠 30 行才算牛逼。UNIX 哲學,在我們這,顯然不合適吧。

作者:極客重生

編輯:陶家龍

出處:轉(zhuǎn)載自公眾號極客重生(ID:geek__coding)

 

責任編輯:武曉燕 來源: 極客重生
相關(guān)推薦

2022-01-05 16:16:02

查詢編程工程師

2024-02-23 08:18:32

首屏產(chǎn)品瀏覽器

2019-10-12 10:39:44

AR眼鏡Facebook蘋果

2022-11-07 09:25:02

Kafka存儲架構(gòu)

2021-01-28 05:11:26

HDFS架構(gòu)Hadoop

2019-12-16 09:37:19

Kafka架構(gòu)數(shù)據(jù)

2020-05-25 08:05:11

KafkaActiveMQRabbitMQ

2019-12-11 10:14:23

Kafka吞吐量架構(gòu)

2017-02-11 09:58:19

2023-11-10 08:18:27

JavaGraalVM

2024-10-22 15:25:20

2019-04-04 14:51:57

banner廣告設(shè)計分析

2022-06-01 07:49:43

索引數(shù)據(jù)Mysql

2020-10-22 09:37:39

存儲Kafka設(shè)計

2013-07-24 16:08:01

Android模擬器Genymotion

2018-12-25 09:44:42

2019-04-01 13:53:43

2019-12-23 09:25:29

日志Kafka消息隊列

2012-08-03 09:09:59

網(wǎng)頁設(shè)計設(shè)計

2022-04-19 20:51:20

軟件開發(fā)耦合代碼
點贊
收藏

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