分布式消息系統(tǒng)的設(shè)計要點
分布式緩存方面,redis勇奪花魁。但對于消息隊列mq來說,還處于百花齊放的年代。
緩存系統(tǒng),基本上解決一個存取問題,就萬事大吉了,調(diào)用是同步的。對于消息隊列來說,就不太一樣。它的使用場景多樣,可靠級別多變,從生產(chǎn)端到消費端,過程是異步的。
消息系統(tǒng)的設(shè)計要點,有很多?,F(xiàn)在,很難有一個消息系統(tǒng),能夠兼顧下面提到的設(shè)計要點。它要是說可以,那就是母體在吹。
所以很多時候,現(xiàn)在流行的Kafka、RabbitMQ、RocketMQ等,會被同時使用。如果你在做相關(guān)方面的選型,下面這些技術(shù)點就是權(quán)衡之處。那句話叫什么來著:牝雞司晨,惟家之索。
要點
本文將針對這些mq,從整體上抽象一些共有特性。包括:協(xié)議、類型、消費方式、堆積能力、高可用、高可靠、高性能、擴展性和生態(tài)。如果你想要深入某個mq,這里也有幾篇關(guān)于kafka的文章。
高可用
高可用主要解決集群單節(jié)點,在異常情況下的failover和HA。解決高可用問題的一般思路就是副本機制。
通過增加副本,可以將數(shù)據(jù)的風(fēng)險分散到多臺機器上。這就需要在主分片出現(xiàn)問題時,能夠從副本中找出一個作為新的主分片。有很多這樣的協(xié)調(diào)工具,比如zk。也有的mq,自己去實現(xiàn)這個過程。
有的模式就比較浪費資源了,比如rocketmq,使用standby從機進行高可用保證,出問題再頂上來。
高可靠
消息系統(tǒng)的可靠性和性能是相悖的。一般的mq,可靠性級別都是可以調(diào)節(jié)的,但性能會發(fā)生相反的聯(lián)動性。從消息級別來說,大體路線有:
發(fā)出去就不管了->單節(jié)點確認->多節(jié)點確認->多節(jié)點確認同步刷盤->所有節(jié)點同步刷盤->事務(wù)消息等。
單機高可靠
集群的高可靠方面,會有ack機制和多副本機制進行保證。對于單個節(jié)點來說,斷電或者主機異常,會是一個比較大的挑戰(zhàn)。為了處理這種情況,需要有刷盤機制或者其他持久化機制。同時,數(shù)據(jù)的完整性校驗也是需要的,這也是類似kafka這種消息系統(tǒng),數(shù)據(jù)量大的時候,啟動時間非常長的原因。
生產(chǎn)端
生產(chǎn)端除了要考慮buffer丟失的問題,還要考慮到一些發(fā)送錯誤的情況,包括與集群通信的超時和重試處理。
消費端
消費端通過消息確認機制來保證消息已經(jīng)被正確消費。由于其間會發(fā)生很多異常情況,所以大多數(shù)消息系統(tǒng)保證at least once語義。即確保消息至少被消費1次。
言外之意,消息是會重復(fù)的,消費者需要做到冪等,保證重復(fù)消費不會引起業(yè)務(wù)異常。
消費端同樣會發(fā)生一些錯誤情況,有些mq可以在多次消費失敗后自動進入死信隊列,有些mq需要自行設(shè)計topic進行規(guī)劃。
高性能
作為一個數(shù)據(jù)傳輸?shù)耐ǖ溃阅苁且粋€非常有分量的考量點。其中兩個比較重要的指標,一個是消息的延遲性,一個就是消息的吞吐量。
消息從生產(chǎn)端發(fā)出,到消費者處理,其間的過程不能太長,對于使用拉模式來消費的mq來說,就要加快輪詢速度,并使用零拷貝一類的技術(shù)加快數(shù)據(jù)傳輸。
對于消息吞吐量來說,是一個生產(chǎn)端、mq節(jié)點、消費端共同優(yōu)化的結(jié)果。目前主要有以下手段:
異步化
消息采用異步發(fā)送的方式,發(fā)送端不用同步等待,加快了處理速度。
batch
采用批量發(fā)送的方式,減少網(wǎng)絡(luò)傳輸?shù)拇螖?shù),方便進行數(shù)據(jù)壓縮。一般是內(nèi)存中緩沖一個buffer,如果buffer滿了,或者到達了時間窗口,則進行一次傳輸。這能夠顯著增加傳輸速度,但處理不當容易丟失數(shù)據(jù)。
順序IO
xjjdog已經(jīng)在多篇文章提到,順序性操作磁盤,比隨機操作內(nèi)存速度快的多。這也是kafka之類的消息隊列速度快的原因之一,但要注意主題的數(shù)量(想下為什么)。
另外,還有其他手段。比如優(yōu)化操作系統(tǒng)參數(shù),使用分片增加并行度等。
消息類型
消息有點對點的,一條消息只會被消費一次。Pub/Sub通過發(fā)布/訂閱模式,一條消息能夠被多個消費端消費。還有一種消息是通過廣播模式進行廣播,即producer發(fā)送消息,所有的consumer都會收到。
除了普通發(fā)送的消息,還有一些特殊用途的消息。順序性消息有全局有序和分區(qū)有序之分,一般用于有嚴格順序要求的業(yè)務(wù)。通過業(yè)務(wù)的設(shè)計,可以規(guī)避全局有序這種非常耗性能的操作。
有些mq還支持定時消息(私以為這種放業(yè)務(wù)系統(tǒng)更佳)。事務(wù)消息更加耗費性能,慎用。
還有一些mq,提供打tag、進行消息過濾的功能。比如訂單信息發(fā)送到一個topic,消費者只訂閱相關(guān)商品的訂單,某些有求隔離的情況,非常有用。
消費模式
消費模式,主要有推模式和拉模式。拉模式最為實用和流行,因為消費處理速度可以由消費端進行調(diào)節(jié)。
推模式的實時性更好一些,但不好評估消費端能力,容易將其壓垮。同時,處理pub/sub,失敗重試等,也有很多挑戰(zhàn)。
協(xié)議
大家都知道java中有一個JMS規(guī)范,但是類似于kafka這種卻沒有實現(xiàn)這個規(guī)范。所以一些協(xié)議,比如amqp、openwire等,有更加明顯的定制型。
這個傳輸協(xié)議,與功能關(guān)系不大。比如就有基于http協(xié)議的,或者redis協(xié)議,甚至websocket之上的stomp。
mqtt是物聯(lián)網(wǎng)IoT的應(yīng)用協(xié)議,你會發(fā)現(xiàn)一大坨基于它的消息隊列。
堆積能力
現(xiàn)在的數(shù)據(jù)都長這么大,mq的堆積能力是非常非常重要的。就拿redis這種內(nèi)存型的隊列來說,分分鐘就給撐爆。mq除了作為消息處理的通道,還可以作為備用存儲用。
堆積能力的體現(xiàn)在海量存儲上,比如存放在數(shù)據(jù)庫中(矛盾轉(zhuǎn)移),掛載非常大的磁盤等。但別高興的太早,大型集群的啟動加載,以及故障再平衡,通常會花費比較長的時間。
堆積能力的另外一個體現(xiàn),就是對歷史消息的清理。一般有兩個策略:磁盤上線和過期清理,可以結(jié)合需求靈活設(shè)置。
生態(tài)
一個開源軟件的生態(tài)是非常重要的,對于mq來說也是如此。主要體現(xiàn)在兩個方面,一個是支持的的開發(fā)語言多樣(需要提供producer和consumer兩方的包),一個是針對周邊軟件的支持。比如spring,spark,hadoop,flink等,減少集成成本。
這方面除了比較新的mq系統(tǒng),都做的不錯。
消息系統(tǒng)的作用
消息系統(tǒng)在目前的分布式系統(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ù)的正常進行。
End
根據(jù)消息的體量和用途,目前可以將分布式mq大體分為兩類。
一類用于業(yè)務(wù)系統(tǒng),保證極高的可靠性。要求不能夠丟失消息,比如訂單、支付等,有較高的SLA服務(wù)水準。這種情況,對mq的功能要求也比較多,包括消息的可查性。
另外一類用于大數(shù)據(jù)相關(guān)的系統(tǒng),典型的特點就是吞吐量非常大。異常情況下,丟失幾條消息,無傷大雅。
但消息系統(tǒng),可能關(guān)注的只是mq本身。怎么保證生產(chǎn)端、消費端、mq本身三者的可用性,是需要業(yè)務(wù)進行權(quán)衡的。
比如,前段時間xjjdog開源的okmq,就是用來解決一個特定場景的高可用問題。
開源一個kafka增強:okmq-1.0.0