為什么我們要用MQ? 最關(guān)鍵點我認為是解耦,這樣各系統(tǒng)間不用有太多的相互依賴,按需消費即可。再者是異步,提高服務(wù)性能。然后就是削峰,防止過多流量涌入而給服務(wù)器帶來壓力。
那用MQ會有哪些問題呢?我們調(diào)用接口的話基本就是調(diào)用成功或超時重試再不行打個日志,那MQ呢,首先屬于中間件產(chǎn)品,它本身的可用性有沒有保證?如何保證的?發(fā)的消息可能丟失嗎?會不會延遲?重復(fù)發(fā)?到達的消息能按順序嗎?如果都可以的話,該怎么選擇適合自己的MQ呢,運維成本怎么樣?...都可以反問自己。
MQ會不會丟消息呢?這點可以想下哪些鏈路可能導(dǎo)致消息丟失呢,無非是三點:調(diào)用方到MQ的過程丟了 ;MQ本身重啟了;MQ到消費方過程丟了。其實都是有辦法避免的,MQ本身有確認機制,比如我們配置當MQ收到消息并持久化成功后再返回調(diào)用方一個ack,這樣就能保證1,2點正常,同理,當消費方成功消費后再返回MQ一個ack,否則一直重試,這樣第3點也保證了。
MQ消息延遲怎么辦?也可以從為什么延遲出發(fā),是不是消息太多了呢,是否需要增加消費者,是否可以先簡單記錄快速丟棄再找個時間補齊數(shù)據(jù);
MQ消息會不會重復(fù)發(fā)?關(guān)于這點呢,我覺得最好的做法是自己服務(wù)去保證就算重發(fā)了也不會有什么問題,比如設(shè)置消息id什么的,或者一些冪等處理之類。
MQ消息順序能保證嗎?關(guān)于這點可以根據(jù)具體的MQ來思考下,比方說kafka,它可以根據(jù)key做分片,比如某一個產(chǎn)品id下的始終路由到一臺消費機,這個時候如果是單線程是有序的,多線程的話其實可以再考慮根據(jù)規(guī)則弄一個內(nèi)存隊列,這樣去保證某一個key下的消息順序消費。其它MQ類似的去思考。如果實在不能保證,消息可以帶上時間戳,比如評論的場景,先收到刪除,再收到添加,這個時候發(fā)現(xiàn)添加的時間戳大于刪除就放棄執(zhí)行也是可以的。
回到很重點的一個問題,MQ如何保證高可用呢,萬一運行著突然掛了,或者磁盤壞了,那沒有消費完成的數(shù)據(jù)怎么辦?這個又得結(jié)合具體的某一款MQ來分析了,比如kafka,這個據(jù)說天然分布式的設(shè)計是怎么樣的:它采用劃分分片+HA副本機制來設(shè)計的,一個topic的消息可以被劃分成多個partition,每個partition又可以放入不同的節(jié)點去,這樣達到了分布式;然后每個partition會同步數(shù)據(jù)到其它的機器,形成多副本,并選舉leader,生產(chǎn)消費只和leader打交道,還可以設(shè)置消息寫入所有副本之后才表示成功,這樣是不是就搞定啦。至于RabbitMQ,這個的設(shè)計并不是分布式的,但是有普通集群模式和鏡像集群模式兩種,單機的就不說了哈,普通集群模式:queue只會放在一臺實例,消費的時候如果拉到了其它實例,會從原queue在的實例再拉一下,這樣如果原queue的掛了,貌似就gg了;鏡像集群模式:每個實例都會同步queue的全部信息。這樣也有個問題是queue不能太大了,不能超過單實例的存儲限制。
如果是你自己設(shè)計MQ,你會怎么設(shè)計呢?