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