這個(gè) Kafka 的專(zhuān)題啡直,我會(huì)從系統(tǒng)整體架構(gòu)烁涌,設(shè)計(jì)到代碼落地。和大家一起杠源碼酒觅,學(xué)技巧撮执,漲知識(shí)。希望大家持續(xù)關(guān)注一起見(jiàn)證成長(zhǎng)舷丹!
我相信:技術(shù)的道路抒钱,十年如一日!十年磨一劍!
簡(jiǎn)介
Kafka 是一種分布式的谋币,基于發(fā)布 / 訂閱的消息系統(tǒng)仗扬。最初被 LinkedIn 開(kāi)發(fā),并在 2011 年初開(kāi)源蕾额,2012 年 10 月從 Apache 孵化器破殼而出早芭,成為 Apache 的頂級(jí)項(xiàng)目。
Kafka 最初被設(shè)計(jì)的目的是 LinkedIn 流量和運(yùn)維數(shù)據(jù)分析诅蝶。流量數(shù)據(jù)包含 PV (Page View) , UV (Unique Visitor) ,搜索數(shù)據(jù)退个,詳情頁(yè)數(shù)據(jù)等。在高并發(fā)場(chǎng)景對(duì)于這些數(shù)據(jù)的統(tǒng)計(jì)并非實(shí)時(shí)的秤涩,不是簡(jiǎn)單的對(duì)于數(shù)據(jù)庫(kù)的某個(gè)字段數(shù)據(jù)量 +1 這么簡(jiǎn)單帜乞,超大的流量洪峰下并不能因?yàn)榻y(tǒng)計(jì)數(shù)據(jù)將業(yè)務(wù)主流程阻塞。所以通常會(huì)將這些數(shù)據(jù)記錄在文件或大數(shù)據(jù)存儲(chǔ)引擎中筐眷,然后周期性的進(jìn)行統(tǒng)計(jì)分析黎烈。
Kafka 被越來(lái)越多的公司青睞主要和他的特性?xún)?yōu)勢(shì)有關(guān):
- 以 O(1) 時(shí)間復(fù)雜度消息持久化,對(duì)于 TB 級(jí)別的數(shù)據(jù)也能夠保證 O(1) 的訪問(wèn)效率
- 支持批量
讀
和寫(xiě)
數(shù)據(jù)匀谣,并且對(duì)于數(shù)據(jù)進(jìn)行壓縮保證高吞吐 - 支持消息分區(qū)照棋,分布式發(fā)送,分布式消費(fèi)武翎,便于水平擴(kuò)展 (Scale out)烈炭,具有很高的并發(fā)能力
應(yīng)用場(chǎng)景
那為何需要使用消息隊(duì)列,或者說(shuō)在什么場(chǎng)景下 Kafka 更加合適
解耦
在大數(shù)據(jù)宝恶,高并發(fā)的場(chǎng)景下為了突破性能瓶頸會(huì)對(duì)系統(tǒng)進(jìn)行水平擴(kuò)展和垂直拆分符隙,將一個(gè)復(fù)雜的系統(tǒng)拆分多個(gè)獨(dú)立,純凈的子系統(tǒng)垫毙。數(shù)據(jù)在各個(gè)系統(tǒng)之間流轉(zhuǎn)霹疫,但是如果某一個(gè)服務(wù)處理速度過(guò)慢,就會(huì)拖累整個(gè)鏈路的性能综芥,形成瓶頸降低整個(gè)系統(tǒng)的性能丽蝎,造成“旱的旱死澇的澇死”的局面。
舉個(gè)簡(jiǎn)單例子:在淘寶下單時(shí)膀藐,交易系統(tǒng)完成扣款屠阻,后續(xù)會(huì)有很多動(dòng)作:提醒賣(mài)家發(fā)貨,生成賣(mài)家工作流额各,核銷(xiāo)優(yōu)惠券国觉,增加購(gòu)物積分等等,如果這一步全部寫(xiě)到交易系統(tǒng)的扣款代碼之后虾啦,很有可能交易系統(tǒng)就會(huì)被拖死蛉加,下游任何一個(gè)環(huán)節(jié)失敗也會(huì)導(dǎo)致扣款回滾蚜枢,并且如果需要添加一個(gè)新的動(dòng)作需要交易去做大量修改,設(shè)計(jì)肯定是不合理的针饥。實(shí)際上交易系統(tǒng)在處理完扣款后會(huì)發(fā)送一個(gè)扣款完成消息厂抽,下游接這個(gè)消息即可,下游失敗不會(huì)影響核心流程失敗丁眼,并且各個(gè)系統(tǒng)的邊界更加清楚筷凤,分層更更加合理。
數(shù)據(jù)持久化
如今的應(yīng)用程序基本都會(huì)涉及到多個(gè)系統(tǒng)之間的對(duì)接苞七,數(shù)據(jù)在系統(tǒng)之間通過(guò) RPC 進(jìn)行傳遞藐守,處理數(shù)據(jù)的過(guò)程失敗就會(huì)導(dǎo)致數(shù)據(jù)丟失,除非數(shù)據(jù)被持久化到磁盤(pán)上蹂风。而 Kafka 將所有需要流轉(zhuǎn)的數(shù)據(jù)都 持久化到磁盤(pán)上
卢厂,保證數(shù)據(jù)不會(huì)丟失。另外還有一個(gè)很重要的能力就是保留現(xiàn)場(chǎng)便于后續(xù)問(wèn)題排查跟蹤惠啄,經(jīng)歷過(guò)系統(tǒng)失敗但是無(wú)法復(fù)現(xiàn)的人才會(huì)體會(huì)到的痛慎恒!
為了保證磁盤(pán)上的數(shù)據(jù)不會(huì)爆炸式瘋漲,Kafka 提供了數(shù)據(jù)清理撵渡,數(shù)據(jù)壓縮等功能融柬,清除處理完成的歷史數(shù)據(jù)。
擴(kuò)展性
在應(yīng)用的訪問(wèn)量劇增的情況下趋距,代碼優(yōu)化往往沒(méi)有直接進(jìn)行水平擴(kuò)展來(lái)的那么及時(shí)粒氧。診斷,分析节腐,方案外盯,優(yōu)化,驗(yàn)證 一系列復(fù)雜流程讓代碼優(yōu)化看起來(lái)只能是一個(gè)從長(zhǎng)計(jì)議的方案翼雀。這時(shí)止血的方案只能是降級(jí)饱苟,限流,擴(kuò)機(jī)器 三板斧锅纺。Kafka 的擴(kuò)展性主要就體現(xiàn)在能熱擴(kuò)容,不需要修改參數(shù)肋殴,不需要修改代碼囤锉,上機(jī)器 -> 注冊(cè)服務(wù) 就完成了擴(kuò)容。并非所有系統(tǒng)都具備這個(gè)像 調(diào)節(jié)音量旋鈕一樣簡(jiǎn)單的提高系統(tǒng)性能
的能力 护锤,這里會(huì)涉及到擴(kuò)容之前的數(shù)據(jù)是否會(huì)有熱點(diǎn)官地,新節(jié)點(diǎn)對(duì)集群的同步,流量重分配等等一系列復(fù)雜流程烙懦。
容災(zāi)
系統(tǒng)的部分組件失敗不會(huì)影響這個(gè)系統(tǒng)的運(yùn)行驱入,消息隊(duì)列降低了進(jìn)程間的耦合度,上游或者下游服務(wù)掛掉后不會(huì)影響其他系統(tǒng)的運(yùn)行,在服務(wù)重新在線后能夠繼續(xù)處理之前未處理的數(shù)據(jù)亏较,只是會(huì)存在一定的延時(shí)但是能夠保證 最終業(yè)務(wù)正確性
莺褒。
保序
強(qiáng)哥:你這瓜保熟嗎?哦不雪情,你這隊(duì)列保序嗎遵岩?
在大多數(shù)場(chǎng)景下,數(shù)據(jù)處理順序是至關(guān)重要的巡通,順序錯(cuò)亂很可能導(dǎo)致數(shù)據(jù)結(jié)果錯(cuò)誤尘执。除非這個(gè)處理過(guò)程是無(wú)狀態(tài)的,此時(shí)消息只是起到事件觸發(fā)的作用宴凉,觸發(fā)下游進(jìn)行計(jì)算誊锭。Kafka 可以保證分區(qū)內(nèi)部有序而不能保證全局有序。
核心概念
架構(gòu)圖
上圖是一個(gè)典型的 Kafka 架構(gòu)圖弥锄,左邊為消息生產(chǎn)者(Producer) 丧靡,發(fā)送消息到一個(gè)特定的主題(Topic),由于 Kafka 的分布式設(shè)計(jì)每個(gè) Topic 被分成多個(gè)分區(qū)叉讥,因此發(fā)送到每個(gè) Topic 的消息會(huì)被存儲(chǔ)到對(duì)應(yīng)的分區(qū)窘行。另外如果 Topic 設(shè)置了副本,則每個(gè)分區(qū)都會(huì)有對(duì)應(yīng)的副本图仓。這些 Topic 被不同的消費(fèi)者(Consumer)訂閱罐盔,如果兩個(gè)消費(fèi)者在同一個(gè)消費(fèi)者組,那么里面的消費(fèi)者只能訂閱一個(gè)固定的分區(qū)救崔。
用上圖的 Topic A 舉例惶看, Producer 1 發(fā)送消息到 Topic-A ,消息會(huì)在存放在 Broker-2 和 Broker-3 的兩個(gè)分區(qū)上六孵,并且由于 Topic-A 開(kāi)啟了分區(qū)備份纬黎,所以每個(gè)分區(qū)都會(huì)由另外一個(gè)節(jié)點(diǎn) Topic-A' 備份分區(qū)數(shù)據(jù) 。發(fā)送到 Broker 的數(shù)據(jù)會(huì)被消費(fèi)者訂閱劫窒,由于 Consumer-1 和 Consumer-2 在同一個(gè)消費(fèi)者組中本今,他們只能消費(fèi)一個(gè)固定分區(qū)的消息, Consumer-1 只會(huì)接收到 Topic-A Partition-1 的消息,Consumer-2 只會(huì)接收到 Topic-A Partition-0 的消息主巍。
Broker
在 Kafka 集群中的一個(gè) Kafka Server 就是一個(gè) Broker 冠息,生產(chǎn)者將消息投遞到 Broker ,Broker 保證消息的 持久化孕索,容災(zāi)逛艰,準(zhǔn)確性等。同時(shí)接受消費(fèi)者的消息訂閱搞旭,向消費(fèi)者分發(fā)消息散怖。一般來(lái)說(shuō)在生產(chǎn)環(huán)境一臺(tái) Kafka 服務(wù)器就是一個(gè) Broker菇绵。
Topic & Partition & Log
Topic 可以認(rèn)為是用來(lái)存儲(chǔ)消息的邏輯概念,可簡(jiǎn)單認(rèn)為他是一個(gè) 信箱
镇眷。每條消息發(fā)送的時(shí)候都需要指定需要發(fā)送到哪個(gè) Topic 咬最,消息被消費(fèi)的時(shí)候也需要指定消費(fèi)哪個(gè) Topic 中的消息。
Kafka 為了提高可擴(kuò)展性以及吞吐量偏灿,Topic 被分成多個(gè)分區(qū) (Partition) 丹诀,每個(gè) Partition 對(duì)應(yīng)一個(gè) Log,Log 是一個(gè)邏輯概念翁垂, 它會(huì)對(duì)應(yīng)服務(wù)器上一個(gè)文件夾铆遭,這個(gè)文件夾下存放的是這個(gè) Partition 下所有的消息數(shù)據(jù)和消息索引 。在面對(duì)海量數(shù)據(jù)的時(shí)候沿猜,為了避免出現(xiàn)巨大文件出現(xiàn) I/O 瓶頸枚荣,Kafka 又將 Log 分為多個(gè) Segment 。每個(gè) Segment 包含 log 文件
和 index 文件
文件命名是以該 Segment 第一條消息的 offset 命名啼肩。這樣說(shuō)下來(lái)其實(shí)還是很繞的直接看下面的架構(gòu)圖橄妆,可以仔細(xì)留意一下各個(gè)部分的標(biāo)識(shí)和數(shù)字再結(jié)合這段文字,理解起來(lái)應(yīng)該就很輕松了祈坠。
Replication
在生產(chǎn)環(huán)境中,我們一般會(huì)開(kāi)啟 Kafka 消息冗余特性躺同,每個(gè) Partition 都有 1 個(gè)或多個(gè)副本阁猜,我們稱(chēng)之為 Replication。當(dāng)分區(qū)只有一個(gè)副本的時(shí)候蹋艺,該分區(qū)數(shù)據(jù)只保留了一份剃袍。每個(gè)分區(qū)副本都會(huì)選出一個(gè) Leader , Leader 是所有讀寫(xiě)請(qǐng)求的 “接口人”
捎谨,其余副本均為 Follower 民效。Follower 作用有兩個(gè):拉取 Leader 的 Log 數(shù)據(jù)做 備份
,在 Leader 失敗后作為候選人 參與 Leader 選舉
涛救。
Producer
消息產(chǎn)出的源頭畏邢,通過(guò)一定的策略推送到 Topic 的各個(gè)分區(qū) 。這里所說(shuō)的推送策略就是消息路由機(jī)制州叠,Kafka 內(nèi)置多種策略可選例如:按照消息 Key 棵红,輪訓(xùn)等等凶赁,甚至用戶(hù)可以寫(xiě)擴(kuò)展代碼來(lái)自定義路由策略咧栗。
Consumer & Consumer Group
消費(fèi)者(Consumer)
主要工作是從 Broker 拉取消息逆甜,進(jìn)行消費(fèi)處理。每個(gè)消費(fèi)者維護(hù)自己的消費(fèi)進(jìn)度致板,這樣的設(shè)計(jì)有諸多好處交煞,比如:每個(gè)消費(fèi)者進(jìn)度能夠輕松的進(jìn)行區(qū)分,并且可以修改單個(gè)消費(fèi)者的消費(fèi)位點(diǎn)跳過(guò)或者重新消費(fèi)某些消息斟或,避免了位點(diǎn)信息的集中化管理的單點(diǎn)故障問(wèn)題素征。
現(xiàn)在的應(yīng)用程序大部分為分布式的系統(tǒng),一個(gè)應(yīng)用有幾十臺(tái)上百臺(tái)服務(wù)器萝挤,這些服務(wù)器上運(yùn)行著相同的代碼御毅,那么一個(gè)消息過(guò)來(lái),每臺(tái)服務(wù)器都執(zhí)行一次消費(fèi)邏輯怜珍,豈不是會(huì)造成巨大的問(wèn)題端蛆。
所以 Kafka 引入了一個(gè)新的概念: 消費(fèi)者組(Consumer Group)
。我們可以將這些應(yīng)用的服務(wù)器都放到同一個(gè)消費(fèi)者組中酥泛,而 Kafka 規(guī)定一條消息只能被同一個(gè)消費(fèi)者組中的一個(gè)消費(fèi)者消費(fèi)今豆,這樣就能完美避免分布式情況下的重復(fù)消費(fèi)問(wèn)題了。上面所說(shuō)的情況簡(jiǎn)單來(lái)說(shuō)是希望實(shí)現(xiàn)消息被某臺(tái)服務(wù)器獨(dú)占柔袁,也就是 單播
問(wèn)題呆躲。假如我們希望這條消息被廣播出去,每臺(tái)收到這個(gè)消息的服務(wù)器都做處理捶索,例如發(fā)消息做日志清理插掂,這種情況稱(chēng)為 廣播
, 那我們只需要將每個(gè)消費(fèi)者放到不同的消費(fèi)者組即可。
Kafka 引入消費(fèi)者組的概念巧妙解決了單播和廣播問(wèn)題情组,而沒(méi)有區(qū)分訂閱類(lèi)型燥筷,通過(guò)一種邏輯概念來(lái)屏蔽掉多種訂閱實(shí)現(xiàn)。
另外在同一個(gè)消費(fèi)者組中的消費(fèi)者訂閱的分區(qū)是確定的院崇,只有在消費(fèi)者組中的消費(fèi)者有變化的時(shí)候才會(huì)進(jìn)行重分配肆氓。例如我們有四個(gè)分區(qū),三個(gè)消費(fèi)者底瓣,就會(huì)出現(xiàn)一個(gè)消費(fèi)者訂閱兩個(gè)分區(qū)的情況谢揪。而三個(gè)分區(qū)四個(gè)消費(fèi)者就會(huì)出現(xiàn)有消費(fèi)者處于空閑狀態(tài),造成浪費(fèi)捐凭,所以一般消費(fèi)者的數(shù)量盡量不要大于 Topic 的分區(qū)數(shù)拨扶。尾聲(嘮叨)
這是我 2021 年的第一篇博客,年底做回顧的時(shí)候才知道去年我過(guò)的究竟有多么糟糕茁肠。既沒(méi)有輸入也沒(méi)有輸出患民,雖然工作進(jìn)入一個(gè)新的階段,會(huì)越來(lái)越忙垦梆,但忙不是拒絕成長(zhǎng)的借口匹颤,必須保證每月一到兩本書(shū)的輸入仅孩,一到兩周輸出一篇優(yōu)質(zhì)文章。 最長(zhǎng)的路屬于一顆孤獨(dú)的心印蓖,與君共勉
下期我會(huì)從整體梳理 Kafka 生產(chǎn)者辽慕,包括消息發(fā)送客戶(hù)端,發(fā)送端數(shù)據(jù)緩存從源碼角度看看其中的設(shè)計(jì)模式赦肃,代碼組織技巧溅蛉。