開篇
在微服務的架構(gòu)設計中我們一般都會考慮服務之間互相調(diào)用的問題,如何做到更好的解耦設計。在秒殺的系統(tǒng)中會使用異步處理的方式來設計高并發(fā)、低延遲的系統(tǒng)架構(gòu)。提到這些相信大家都會想到使用MQ(消息隊列)來處理這些問題。
MQ(消息隊列) 是跨進程通信方式之一,可理解為異步RPC,上游系統(tǒng)對調(diào)用結(jié)果的態(tài)度往往是重要不緊急。使用消息隊列有幾個好處:業(yè)務解耦、流量削峰、靈活擴展
技術選型
現(xiàn)在業(yè)界主流的MQ有很多,比如:ActiveMQ、RabbitMQ、RocketMQ、Kafka 等,那么我們在技術選型中該怎么選擇呢?
| 特性 | Kafka | ActiveMQ | RabbitMQ | RocketMQ |
|---|---|---|---|---|
| 單擊吞吐量 | 10萬級別,這是Kafka最大的優(yōu)點,就是吞吐量高。一般配合大數(shù)據(jù)類的系統(tǒng)來進行實時數(shù)據(jù)計算,日志采集等場景。 | 萬級,吞吐量比RocketMQ和Kafka要低一個數(shù)量級 | 萬級,吞吐量比RocketMQ和Kafka要低一個數(shù)量級 | 10萬級,RocketMQ也是可以支撐高吞吐的一種MQ |
| topic數(shù)量對吞吐量的影響 | topic從幾十個到幾百個的時候,吞吐量會大幅度下降。所以在同等機器下,Kafka盡量保證topic數(shù)量不要過多。如果要支撐大規(guī)模topic,需要增加更多機器資源。 | topic可以達到幾百,幾千個的級別,吞吐量會較小幅度的下降,這是RocketMQ的一大優(yōu)勢,在同等機器下,可以支撐大量的topic。 | ||
| 時效性 | 延遲在ms以內(nèi) | ms級 | 微妙級,這是rabbitMq的一大特點,延遲是最低的 | ms級 |
| 可用性 | 非常高,kafka是分布式的,一個數(shù)據(jù)多個副本,少數(shù)機器宕機,不會丟失數(shù)據(jù),不會導致不可用 | 高,基于主從框架實現(xiàn)高可用性 | 高,基于主從架構(gòu)實現(xiàn)高可用性 | 非常高,分布式架構(gòu) |
| 消息可靠性 | 經(jīng)過參數(shù)優(yōu)化配置,消息可以做到0丟失 | 有較低的概率丟失數(shù)據(jù) | 經(jīng)過參數(shù)優(yōu)化配置可以做到0丟失 | |
| 功能支持 | 功能較為簡單,主要支持簡單的MQ功能,在大數(shù)據(jù)領域的實時計算以及日志采集被大規(guī)模使用,是事實上的標準 | MQ領域的功能及其完備 | 基于erlang開發(fā),所以并發(fā)能力很強,性能及其好,延時很低 | MQ功能較為完善,還是分布式的,擴展性好 |
| 優(yōu)劣勢總結(jié) | kafka的特點其實很明顯,就是僅僅提供較少的核心功能,但是提供超高的吞吐量,ms級的延遲,極高的可用性以及可靠性,而且分布式可以任意擴展 同時kafka最好是支撐較少的topic數(shù)量即可,保證其超高吞吐量 而且kafka唯一的一點劣勢是有可能消息重復消費,那么對數(shù)據(jù)準確性會造成極其輕微的影響,在大數(shù)據(jù)領域中以及日志采集中,這點輕微影響可以忽略 這個特性天然適合大數(shù)據(jù)實時計算以及日志收集 | 非常成熟,功能強大,在業(yè)內(nèi)大量的公司以及項目中都有應用。 偶爾會有較低概率的丟失消息。 而且現(xiàn)在社區(qū)以及國內(nèi)應用都越來越少,官方社區(qū)現(xiàn)在對ActiveMQ維護越來越少,幾個月才發(fā)布一個版本。 而且確實主要是基于解耦和異步來用的,較少在大規(guī)模吞吐的場景中使用。 | erlang語言開發(fā),性能及其好,延時很低:吞吐量到萬級,MQ功能比較完備,而且開源提供的管理界面非常棒,用起來很好用。社區(qū)相對比較活躍,幾乎每個月都發(fā)布幾個版本。在國內(nèi)公司用 rabbitmq也比較多一些 但是問題也是顯而易見的,RabbitMQ確實吞吐量會低一些,這是因為他做的實現(xiàn)機制比較重。 而且erlang開發(fā),國內(nèi)有幾個公司有實力做erlang源碼級別的研究和定制?如果說你沒這個實力的話,確實偶爾會有一些問題,你很難去看懂源碼,你公司對這個東西的掌控很弱,基本職能依賴于開源社區(qū)的快速維護和修復bug。 而且rabbitmq集群動態(tài)擴展會很麻煩,不過這個我覺得還好。其實主要是erlang語言本身帶來的問題。很難讀源碼,很難定制和掌控。 | 接口簡單易用,而且畢竟在阿里大規(guī)模應用過,有阿里品牌保障 日處理消息上百億之多,可以做到大規(guī)模吞吐,性能也非常好,分布式擴展也很方便,社區(qū)維護還可以,可靠性和可用性都是ok的,還可以支撐大規(guī)模的topic數(shù)量,支持復雜MQ業(yè)務場景 而且一個很大的優(yōu)勢在于,阿里出品都是java系的,我們可以自己閱讀源碼,定制自己公司的MQ,可以掌控 社區(qū)活躍度相對較為一般,不過也還可以,文檔相對來說簡單一些,然后接口這塊不是按照標準JMS規(guī)范走的有些系統(tǒng)要遷移需要修改大量代碼 還有就是阿里出臺的技術,你得做好這個技術萬一被拋棄,社區(qū)黃掉的風險,那如果你們公司有技術實力我覺得用RocketMQ挺好的 |
綜上比較,可以看出RocketMQ和Kafka 優(yōu)勢較為明顯,其實對于大多數(shù)的服務架構(gòu)來說,吞吐量 和 消息可靠性 是我們選型中考慮較多的因素。接下來我們就一起來了解下Kafka 相關的內(nèi)容
Kafka簡介
Apache Kafka 起源于LinkedIn,后來于2011年成為Apache開源項目。Kafka是用Scala和Java編寫的。
Apache Kafka官網(wǎng) 上介紹,Kafka是一個分布式流處理平臺,具有以下三個特性:
可以讓你發(fā)布和訂閱流式的記錄。這一方面與消息隊列或者企業(yè)消息系統(tǒng)類似;
可以儲存流式的記錄,并且有較好的容錯性;
可以在流式記錄產(chǎn)生時就進行處理。
接下來我們會就第一個特性,Kafka作為消息隊列所具有的優(yōu)勢和特點:
Kafka作為一個分布式消息隊列,具有高性能、持久化、多副本備份、橫向擴展能力。生產(chǎn)者往消息隊列里寫消息,消費者從隊列里取消息進行業(yè)務邏輯。一般在架構(gòu)設計中起到解耦、削峰、異步處理的作用。
Kafka 架構(gòu)總覽

上圖清晰的描述了Kafka的總體數(shù)據(jù)流,關于broker、topics、partitions 的一些元信息用zk來存
Producers負責往Brokers里面指定Topic中寫消息Consumers從Brokers 中拉取指定Topics的消息,然后進行自己的業(yè)務處理
圖中有3個topic:
topic1有2個partition(topic1-partition1、topic1-partition2),兩副本備份
topic2有3個parition(topic2-partition1、topic2-partition2、topic2-partition3),三副本備份
topic3有2個partition(topic3-partition1、topic3-partition2),兩副本備份
Topic
消息的主題、隊列,每一個消息都有它的topic,Kafka通過topic對消息進行歸類。Kafka中可以將Topic從物理上劃分成一個或多個分區(qū)(Partition),每個分區(qū)在物理上對應一個文件夾,以”topicName_partitionIndex”的命名方式命名,該dir包含了這個分區(qū)的所有消息(.log)和索引文件(.index),這使得Kafka的吞吐率可以水平擴展
Partition
每個分區(qū)都是一個 順序的、不可變的消息隊列, 并且可以持續(xù)的添加;分區(qū)中的消息都被分了一個序列號,稱之為偏移量(offset),在每個分區(qū)中此偏移量都是唯一的。
producer在發(fā)布消息的時候,可以為每條消息指定Key,這樣消息被發(fā)送到broker時,會根據(jù)分區(qū)算法把消息存儲到對應的分區(qū)中(一個分區(qū)存儲多個消息),如果分區(qū)規(guī)則設置的合理,那么所有的消息將會被均勻的分布到不同的分區(qū)中,這樣就實現(xiàn)了負載均衡。

Broker
Kafka server,用來存儲消息,Kafka集群中的每一個服務器都是一個Broker,消費者將從Broker拉取訂閱的消息
Producer
向Kafka發(fā)送消息,生產(chǎn)者會根據(jù)topic分發(fā)消息。生產(chǎn)者也負責把消息關聯(lián)到Topic上的哪一個分區(qū)。最簡單的方式從分區(qū)列表中輪流選擇。也可以根據(jù)某種算法依照權重選擇分區(qū)。算法可由開發(fā)者定義。
Cousumer
Consumer實例可以是獨立的進程,負責訂閱和消費消息。消費者用consumerGroup來標識自己。同一個消費組可以并發(fā)地消費多個分區(qū)的消息,同一個partition也可以由多個consumerGroup并發(fā)消費,但是在consumerGroup中一個partition只能由一個consumer消費
CousumerGroup
同一個Consumer Group中的Consumers,Kafka將相應Topic中的每個消息只發(fā)送給其中一個Consumer

如圖,這個 Kafka 集群有兩臺 server 的,四個分區(qū)(p0-p3)和兩個消費者組。消費組A有兩個消費者,消費組B有四個消費者。
通常情況下,每個 topic 都會有一些消費組,一個消費組對應一個"邏輯訂閱者"。一個消費組由許多消費者實例組成,便于擴展和容錯。這就是發(fā)布和訂閱的概念,只不過訂閱者是一組消費者而不是單個的進程。
在Kafka中實現(xiàn)消費的方式是將日志中的分區(qū)劃分到每一個消費者實例上,以便在任何時間,每個實例都是分區(qū)唯一的消費者。維護消費組中的消費關系由Kafka協(xié)議動態(tài)處理。如果新的實例加入組,他們將從組中其他成員處接管一些 partition 分區(qū);如果一個實例消失,擁有的分區(qū)將被分發(fā)到剩余的實例。
Kafka 只保證分區(qū)內(nèi)的記錄是有序的,而不保證主題中不同分區(qū)的順序。每個 partition 分區(qū)按照key值排序足以滿足大多數(shù)應用程序的需求。但如果你需要總記錄在所有記錄的上面,可使用僅有一個分區(qū)的主題來實現(xiàn),這意味著每個消費者組只有一個消費者進程。
Kafka配置項
http://kafka.apachecn.org/documentation.html#configuration
名詞解釋
- 吞吐量(TPS):吞吐量是指對網(wǎng)絡、設備、端口、虛電路或其他設施,單位時間內(nèi)成功地傳送數(shù)據(jù)的數(shù)量(以比特、字節(jié)、分組等測量)