典型消息中間件的架構
消息中間件的價值:就是異步、解耦合、簡單化分布式系統,
減輕業務和數據庫的負擔,業務只需要最簡單的事情系統解耦合、減輕了系統的依賴
一般來講,設計消息隊列的整體思路是先構建一個整體的數據流,
例如Producer發送給Broker,Broker發送給consumer,consumer回復消費確認,Broker刪除/備份消息等。
利用RPC將數據流串起來。
然后考慮RPC的高可用性,盡量做到無狀態,方便水平擴展。
之后考慮如何承載消息堆積,然后在合適的時機投遞消息,而處理堆積的最佳方式,就是存儲,
存儲的選型需要綜合考慮性能/可靠性和開發維護成本等諸多因素。
為了實現廣播功能,我們必須要維護消費關系,可以利用zk/config server等保存消費關系。
當設計消息中間件時需要關心什么
消息的順序保證
消息中間件的擴展性、可靠性
業務操作與消息發送一致性
多集群訂閱消息
消息隊列的消息一致性問題
消息發送一致性是指產生消息的業務動作與消息發送的一致,如果業務操作成功了,那么對應的消息一定要發送出去,否則就丟失消息。而另一方面,如果這個業務行為沒有發生或者失敗,那么就不應該把消息發出去。
考慮如下的操作:
void test(){
//業務操作
//列如寫數據庫,調用服務等
//發送消息
}void test(){
//發送消息
//業務操作
//列如寫數據庫,調用服務等
}
上面偽代碼不管哪一步出現問題了,都很難保持一致性。
使用分布式事務實現消息一致性
JMS的API有很多XA開頭的接口,JMS中定義的XA系列的接口就是為了實現分布式事務的支持,使用XA系列的接口實現,可以保證發送消息給消息中間件及業務操作這兩件事情的事務,使用分布式事務雖然保證了消息一致性,但是有如下問題:
1.引入了分布式事務,這會帶來一些開銷並增加復雜性。
2.對於業務操作有限制,要求業務操作的資源必須支持XA協議,才能夠與發送消息一起來做分布式事務。
這會成為一個限制,因為並不是所有需要與發送消息一起做成分布式事務的業務操作都支持XA協議。
去哪兒網的消息中間件業務實踐
(1)結合數據庫事務和消息表保證一致性
將訂單持久化在同一個事務里,共享訂單操作的數據庫連接,在這個連接里將消息持久化到同一個實例里的消息庫里,
然后在事務提交之后將消息發送到消息的 server。如果事務回滾了,消息就不會發送出去了。
流程圖 :
開啟事務;
業務操作,比如說訂單進行持久化等等動作;
生成消息,並存儲,這和業務操作是在同一個事務里;
事務提交;
消息真正發送出去。
消息如果發送成功了,會將消息表里的消息刪除,而此時如果消息發送失敗了,后台有一個任務會把消息表里面發送失敗的消息重新進行發送,這樣最終達到一致性,保證業務操作成功了,消息一定能發出去。
(2)Producer向Broker兩次確認消息狀態
每次發送新的消息,直接發給Broker,這個消息發給Broker並不立即將消息投遞出去。
然后做本地的業務操作,再調Broker的接口,這一步真正把消息發送出去。
如果這個時候,即使第二步操作成功,第三步發送失敗了,第一步發送給Broker的消息就是一個未決狀態。
Broker反過來詢問 Producer,那條消息是發還是不發出去呢?
這種模型就不需要一個將一個消息庫放在業務庫實例了,比較靈活,成本也更低些。
但是業務使用的復雜度可能要高一些,需要提供一個接口供 Broker 反查。
參考
消息隊列設計精要
設計消息中間件時我關心什么?(解密電商數據一致性與完整性實現)
《大型網站系統與中間件實踐 》