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