老少皆宜的Kafka長(zhǎng)文,讓你明白什么叫本分
本文轉(zhuǎn)載自微信公眾號(hào)「小姐姐味道」,作者小姐姐養(yǎng)的狗。轉(zhuǎn)載本文請(qǐng)聯(lián)系小姐姐味道公眾號(hào)。
看完本文,你將明白為什么一個(gè)簡(jiǎn)單的消息隊(duì)列,能夠有那么多的知識(shí)點(diǎn);能夠了解到Kafka的主要功能和應(yīng)用場(chǎng)景;能夠了解到Kafka的主要技術(shù)術(shù)語。了解到什么叫本分!
作為一個(gè)分布式消息系統(tǒng),Kafka要有本分思想。它要搞清楚自己的定位,明白是為誰創(chuàng)造什么樣的價(jià)值,依賴誰活著,自己的職責(zé)又是什么。
很少有系統(tǒng)在這么有壓迫力的連環(huán)問下保持冷靜,不過Kafka頂住了,它是真的勇士。
Kafka的本分核心,就是當(dāng)作消息隊(duì)列用。那么消息隊(duì)列是什么呢?如果這個(gè)問題沒法搞懂,就證明Kafka的思想覺悟并不是很高,還需要繼續(xù)去思考、去深造。
為了弄清楚這個(gè)問題,我們采訪了一位送牛奶的工人。
1. 送奶工的故事
牛奶好喝而且有營(yíng)養(yǎng),不管是牛奶子里捏出來的新鮮牛奶還是合成的牛奶,所以小區(qū)里有很多人訂。
每天清晨,送奶工人都拉著一車牛奶開始送奶。剛開始,他按照本子上的門牌號(hào),一家家的敲門,然后把牛奶塞進(jìn)客戶手里。有時(shí)候,客戶不在家,他只好翻出通訊錄找到客戶的電話號(hào)碼進(jìn)行溝通。但過了不久,隨著業(yè)務(wù)做的越來越大,送奶工對(duì)這份工作的評(píng)價(jià)只有一句話:*費(fèi)力不討好。
有的客戶睡眼朦朧的開門,投訴他打擾生活;有的女客戶披著睡衣就來接奶,投訴他的眼光猥瑣;有的客戶上班比較早,但在送奶工的路線規(guī)劃上,卻是奶最后送到的,于是投訴他配送不及時(shí)。
好在送奶工以前是個(gè)程序員,稍一思考,他說服老板:給每一家客戶,配備一個(gè)奶箱。他的工作,只需要定時(shí)把鮮奶放入箱子里即可。至于客戶什么時(shí)候去拿,拿去洗臉了還是搓手了,他并不關(guān)心。
從此,他再也沒看到睡衣下若隱若現(xiàn)的胴體。
我們注意到,上面的場(chǎng)景,有兩個(gè)主要的參與方:送奶工和客戶。在加入奶箱之前,他們的交互是阻塞的,信息處理是低效的,而且存在嚴(yán)重的耦合問題,以至于送奶工看了不該看的東西。
當(dāng)然,加入奶箱之后,交互邏輯就發(fā)生了變化,這是需要適應(yīng)的;而且,奶箱是有成本的,如果業(yè)務(wù)量并不是很大,加這個(gè)玩意反而會(huì)增加成本。
我們來稍微see一下下:上面的奶箱,就是消息系統(tǒng)。每一個(gè)奶箱,就是一條消息隊(duì)列。牛奶工,就是生產(chǎn)者;客戶,就是消費(fèi)者;而牛奶,就是消息??蛻粢恢辈蝗∽吣?,就是消息積壓。客戶和你發(fā)消息,確認(rèn)奶已經(jīng)收到,就是ACK...
2. 最簡(jiǎn)單的廣義消息系統(tǒng)
消息系統(tǒng)!就是提供一個(gè)中間層,生產(chǎn)者只需要把消息提交到特定的中間層,消費(fèi)者只需要從中間層去拿信息就可以了。
所以,它最簡(jiǎn)單的表現(xiàn)形式,就是數(shù)據(jù)庫(kù)。
上圖是一些小系統(tǒng)的典型架構(gòu)??紤]訂單的業(yè)務(wù)場(chǎng)景,有大量的請(qǐng)求指向我們的業(yè)務(wù)系統(tǒng),如果直接經(jīng)過復(fù)雜的業(yè)務(wù)邏輯進(jìn)入業(yè)務(wù)表,將會(huì)有大量請(qǐng)求超時(shí)失敗。所以我們加入了一張中間緩沖表,用來承接用戶的請(qǐng)求。然后,有一個(gè)定時(shí)任務(wù),不斷的從緩沖表中獲取數(shù)據(jù),進(jìn)行真正的復(fù)雜的業(yè)務(wù)邏輯處理。
不要懷疑,這其實(shí)就是最簡(jiǎn)陋的消息系統(tǒng),只不過它存在不少問題。
- 定時(shí)任務(wù)的輪詢間隔不好控制。業(yè)務(wù)處理容易延遲。
- 無法橫向擴(kuò)容處理能力,且會(huì)引入分布式鎖、順序性保證等問題。
- 當(dāng)其他業(yè)務(wù)也需要這些訂單數(shù)據(jù)的時(shí)候,業(yè)務(wù)邏輯就必須要加入到定時(shí)任務(wù)里。
當(dāng)訪問量增加、業(yè)務(wù)邏輯復(fù)雜化的時(shí)候,更高的消息模型就呼之欲出了。
3. 消息系統(tǒng)的基本要求
我們對(duì)消息系統(tǒng)的本分要求有下面這些:
- 性能要高 包含消息投遞和消息消費(fèi),都要快。一般通過增加分片數(shù)獲取并行處理能力。數(shù)據(jù)庫(kù)顯然是有瓶頸的。
- 消息要可靠 在某些場(chǎng)景,不能丟消息。生產(chǎn)、消費(fèi)、MQ端都不能丟消息。一般通過增加副本,強(qiáng)制刷盤來解決。數(shù)據(jù)庫(kù)顯然也要通過主從來做備份的。
- 擴(kuò)展性要好 能夠陪你把項(xiàng)目做大,陪你到天荒地老。增加節(jié)點(diǎn)集群增大后,不能降低性能。數(shù)據(jù)庫(kù)的擴(kuò)展性肯定是存疑的,你可能會(huì)引入一些復(fù)雜的分庫(kù)分表組件。
- 生態(tài)成熟 監(jiān)控、運(yùn)維、多語言支持、社區(qū)的活躍。這決定了你用的消息隊(duì)列值不值得你信賴。
甚至有更多,xjjdog有另外一篇文章去說明它:分布式消息系統(tǒng),設(shè)計(jì)要點(diǎn)。畫龍畫虎難畫骨
要求這么多,但模型又如此簡(jiǎn)單,它的難點(diǎn)到底在哪里呢?為什么有些同學(xué)看到Kafka就頭疼呢?
4. 要你本分,到底多難
既然消息系統(tǒng)的模型就是一個(gè)簡(jiǎn)單的生產(chǎn)者消費(fèi)者模型,那為什么現(xiàn)在的消息系統(tǒng)都那么的復(fù)雜呢?其實(shí),它的復(fù)雜性,主要體現(xiàn)在分布式這三個(gè)字上,和消息隊(duì)列的關(guān)系不大,它需要處理一些所有分布式系統(tǒng)都要面臨的問題。
4.1 副本
單機(jī)上的任何數(shù)據(jù)都是不可信的,因?yàn)橛脖P會(huì)壞,會(huì)斷電,會(huì)被挖光纜。所以一般通過冗余多個(gè)副本來保證數(shù)據(jù)的安全。副本的另外一個(gè)作用,就是提供額外的計(jì)算能力,比如某些請(qǐng)求,會(huì)落到副本上。副本越多,可用性越高。
而加入副本以后,就涉及到數(shù)據(jù)的同步問題。即使是最快的局域網(wǎng),也會(huì)存在延遲,更不用說機(jī)器性能差異引起的同步延遲。這就存在一個(gè)問題,讀副本的請(qǐng)求讀到的數(shù)據(jù),可能不是最新的,這就是數(shù)據(jù)的一致性發(fā)生了改變。當(dāng)然有些手段能保證數(shù)據(jù)的一致性,但副本越多,延遲越大。
副本的加入還會(huì)引入主從的問題。主節(jié)點(diǎn)死掉以后,要有副本節(jié)點(diǎn)頂上去,這個(gè)過程的協(xié)調(diào)需要時(shí)間,其間部分不可用。
所有的消息系統(tǒng),需要有大量的代碼去處理這些異常情況。
4.2 分區(qū)
而當(dāng)一類數(shù)據(jù)足夠大(比如說某張表),在其上的操作已經(jīng)非常耗時(shí)的情況下,就需要對(duì)此類數(shù)據(jù)進(jìn)行切割,將其分布到多臺(tái)機(jī)器上。這個(gè)切割過程就是Sharding,通過一定規(guī)則的分片來減少單次查詢數(shù)據(jù)的規(guī)模,增加集群容量。
針對(duì)一個(gè)分片的數(shù)據(jù),只能有一個(gè)寫入的地方,這就是master,其他副本都是從master復(fù)制數(shù)據(jù)。
副本能夠增加讀操作的并行讀,但會(huì)讀到臟數(shù)據(jù)。如果你想要讀到的數(shù)據(jù)是一致的,可以采用同步寫副本的方式,比如KAFKA的ack=-1,只有全部同步成功了,才認(rèn)為本次提交成功。
但如果你的副本太多,這個(gè)過程會(huì)非常的慢。你可能想要通過分配寫入和讀取的副本個(gè)數(shù)來協(xié)調(diào)寫入和讀取的效率,Quorum的R+W>N就是一個(gè)權(quán)衡策略。
5. Kafka名詞解釋
我們反過來再看Kafka的名詞定義,就簡(jiǎn)單的多了。
Kafka是一個(gè)分布式消息(存儲(chǔ))系統(tǒng)。分布式系統(tǒng)通過分片增加并行度;通過副本增加可靠性,kafka也不例外。它的結(jié)構(gòu)逃不出我們上面介紹的基本分布式理論。如果你把副本、分區(qū)、主題通道,生產(chǎn)者、消費(fèi)者這些名詞放在一塊的話,圖就可以變得非常大。
你在一臺(tái)機(jī)器上安裝了Kafka,那么這臺(tái)機(jī)器就叫Broker,KAFKA集群包含了一個(gè)或者多個(gè)這樣的實(shí)例。這只是一個(gè)命名而已,并沒有什么特定含義。
負(fù)責(zé)往KAFKA寫入數(shù)據(jù)的組件就叫做Producer,消息的生產(chǎn)者一般寫在業(yè)務(wù)系統(tǒng)里。和我們的送奶工是一個(gè)維度。
發(fā)送到KAFKA的消息可能有多種,如何區(qū)別其分類?就是Topic的概念。一個(gè)主題分布式化后,可能會(huì)存在多個(gè)Broker上。
將Topic拆成多個(gè)段,增加并行度后,拆成的每個(gè)部分叫做Partition,分區(qū)一般平均分布在所有機(jī)器上。
那些消費(fèi)Kafka中數(shù)據(jù)的應(yīng)用程序,就叫做Consumer,我們給某個(gè)主題的某個(gè)消費(fèi)業(yè)務(wù)起一個(gè)名字,這么名字就叫做Consumer Group
再看一下Kafka Server的配置文件,最重要的兩個(gè)參數(shù):partitions和replication.factor,其實(shí)就非常好理解了。
再來說一個(gè)最重要的概念。Kafka解決副本之間的同步,采用的是ISR,這是一個(gè)面試Kafka必考的點(diǎn)之一。
ISR全稱"In-Sync Replicas",是保證HA和一致性的重要機(jī)制。副本數(shù)對(duì)Kafka的吞吐率是有一定的影響,但極大的增強(qiáng)了可用性。一般2-3個(gè)為宜。
副本有兩個(gè)要素,一個(gè)是數(shù)量要夠多,一個(gè)是不要落在同一個(gè)實(shí)例上。ISR是針對(duì)與Partition的,每個(gè)分區(qū)都有一個(gè)同步列表。N個(gè)replicas中,其中一個(gè)replica為leader,其他都為follower, leader處理partition的所有讀寫請(qǐng)求,其他的都是備份。與此同時(shí),follower會(huì)被動(dòng)定期地去復(fù)制leader上的數(shù)據(jù)。
如果一個(gè)flower比一個(gè)leader落后太多,或者超過一定時(shí)間未發(fā)起數(shù)據(jù)復(fù)制請(qǐng)求,則leader將其從ISR中移除。
當(dāng)ISR中所有Replica都向Leader發(fā)送ACK時(shí),leader才commit。
6. 消息系統(tǒng)的作用
說了這么多,是時(shí)候把消息隊(duì)列的作用,使用計(jì)算機(jī)的術(shù)語解釋一下了:
削峰 用于承接超出業(yè)務(wù)系統(tǒng)處理能力的請(qǐng)求,使業(yè)務(wù)平穩(wěn)運(yùn)行。這能夠大量節(jié)約成本,比如某些秒殺活動(dòng),并不是針對(duì)峰值設(shè)計(jì)容量。
緩沖 在服務(wù)層和緩慢的落地層作為緩沖層存在,作用與削峰類似,但主要用于服務(wù)內(nèi)數(shù)據(jù)流轉(zhuǎn)。比如批量短信發(fā)送。
解耦 項(xiàng)目伊始,并不能確定具體需求。消息隊(duì)列可以作為一個(gè)接口層,解耦重要的業(yè)務(wù)流程。只需要遵守約定,針對(duì)數(shù)據(jù)編程即可獲取擴(kuò)展能力。
冗余 消息數(shù)據(jù)能夠采用一對(duì)多的方式,供多個(gè)毫無關(guān)聯(lián)的業(yè)務(wù)使用。
健壯性 消息隊(duì)列可以堆積請(qǐng)求,所以消費(fèi)端業(yè)務(wù)即使短時(shí)間死掉,也不會(huì)影響主要業(yè)務(wù)的正常進(jìn)行。
但是,由于Kafka是個(gè)優(yōu)秀的小伙,它內(nèi)卷的非??梢?,就能做更多的事情。它的本分范圍更加大,包括但不限于:
- 傳遞業(yè)務(wù)消息
- 用戶活動(dòng)日志 • 監(jiān)控項(xiàng)等
- 日志
- 流處理,比如某些聚合
- Commit Log,作為某些重要業(yè)務(wù)的冗余
- Event Source,實(shí)踐溯源,DDD中的概念
下面是一個(gè)日志方面的典型使用場(chǎng)景。
7. KAFKA為什么快
一般用到Kafka,都是奔著它的速度去的,這一度讓人認(rèn)為它只能處理一些日志類的消息。事實(shí)上,Kafka就連最復(fù)雜的事務(wù)消息都支持,也算是被它的速度所掩蓋的一個(gè)光彩。
那么,它為什么那么快呢?總結(jié)下來有以下幾點(diǎn)原因:
- Cache Filesystem Cache PageCache緩存
- 順序?qū)?由于現(xiàn)代的操作系統(tǒng)提供了預(yù)讀和寫技術(shù),磁盤的順序?qū)懘蠖鄶?shù)情況下比隨機(jī)寫內(nèi)存還要快
- Zero-copy 零拷⻉,少了一次內(nèi)存交換
- Batching of Messages 批量量處理。合并小的請(qǐng)求,然后以流的方式進(jìn)行交互,直頂網(wǎng)絡(luò)上限
- Pull 拉模式 使用拉模式進(jìn)行消息的獲取消費(fèi),與消費(fèi)端處理能力相符
主要就這5點(diǎn),至于什么壓縮,JVM性能優(yōu)化之類的,都是小兒科,上不了臺(tái)面。
End
可以看到,Kafka是一個(gè)全能的選手,既能做消息處理,又能做數(shù)據(jù)存儲(chǔ)。它無怨無悔的工作,雖然效率奇高,也要一刻不停歇的工作,體現(xiàn)了打工人最悲催的命運(yùn)。
它的分布式系統(tǒng)設(shè)計(jì)也是非常棒的,這是它的設(shè)計(jì)者為它量身定做的一套體系:一臺(tái)Kafka節(jié)點(diǎn)倒下了,會(huì)有更多的Kafka節(jié)點(diǎn)頂上來,經(jīng)過十幾秒的陣痛,然后就可以徹底的忘掉它的犧牲。
Kafka是一個(gè)本分的分布式消息系統(tǒng),但也不要無限的壓迫它。只給它分配了1核cpu、512M的內(nèi)存,這是要磨練它在艱苦環(huán)境下不屈不撓的本分意志,啊!
丟了數(shù)據(jù)還罵ta不穩(wěn)定,你的良心不會(huì)痛么?
作者簡(jiǎn)介:小姐姐味道 (xjjdog),一個(gè)不允許程序員走彎路的公眾號(hào)。聚焦基礎(chǔ)架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。我的個(gè)人微信xjjdog0,歡迎添加好友,進(jìn)一步交流。