訊息佇列的常見問題及解決

2022-10-08 16:33:18 字數 2982 閱讀 3665

服務間解耦

提高服務併發、效能

突發流量削峰

...服務間解耦

微服務系統業務之間相互依賴,各種呼叫錯綜複雜,如果不能良好對服務進行解耦那乙個服務的可用性、併發都會受到其他服務的影響。

在沒有引用mq的之前服務呼叫大概是這些步驟:

圖上的a服務是直接呼叫的,這是沒啥問題的,但是服務上線後要迭代更新的麻,這個時候要是服務c的開發人員有點**小潔癖說:我這個c服務介面命名不太好,我需要重新更新下,當a服務的小哥哥還戴著小耳機聽著小歌曲,突然就得改**了~~。

服務a不直接呼叫c而是向訊息佇列中傳送訊息(生產者),另一邊的c取出佇列中的訊息(消費者)進行處理,這樣a、c就完成了解耦。

提高服務併發、效能

舉個例子,在沒引入mq之前服務呼叫多個服務都是同步呼叫,比如像這樣:

服務a要順序的呼叫b、c服務來完成業務邏輯如果a->b需要200ms,a->c需要200ms,再加上自身業務邏輯處理可能需要花費500ms,其中有400ms是呼叫a和b的花費,明明自身100ms就能處理完還白白浪費400ms,不能忍啊於是可以引入mq做一下改造:

這下有了mq,a服務只需要發一條訊息比如花費50ms,再加上自身業務邏輯的100ms,那整個呼叫過程只需要花費150ms了,這樣對併發和效能都有一定的改善。

突發流量削峰

突發流量就是網際網路很常見的情況,有時候有熱點、突發事件,那平常qps為100的介面,突然提公升10-20倍這個時候沒有mq所有流量直接進入服務,這對服務和資料庫都是很大的挑戰:

再次引入mq就情況就不一樣了,服務a先將請求丟給mq,然後可以慢慢消費掉:

使用mq還有很多好處,但是他也會帶一些麻煩事。首先就是會降低系統的可用性,比如mq掛了怎麼辦呢?所以在引入mq之前就需要考慮之後帶來的哪些問題,不能只看它的好處也需要考慮它不好的地方。比如下面列出的這些問題要如果解決:

如何保證訊息佇列的高可用?

如何保證訊息不被重複消費?

如何保證訊息不丟失?

如何保證訊息的消費順序?

下面我們來分析下這些問題。

如果是單機訊息佇列,一台機器掛了訊息佇列都就不用了,這是不能接受的,如果是乙個訊息佇列群集,一台機器掛了還有其他機器能正常提供服務,所以要保證訊息佇列的高可用,我們就需要做訊息佇列集群。

以rabbitmq為例它有兩種集群模式:

普通模式

映象模式

普通模式

普通模式,rabbitmq會同步各個節點的資料/狀態,但不包括訊息佇列,預設情況下,訊息佇列駐留在乙個節點上,儘管它們在所有節點上都是可見且可訪問的。

在這種模式下,每個節點都有會所有節點的元資料資訊,所以當傳送訊息到佇列時,無論連線的是哪乙個節點都能正確的傳送,但是節點只會同步其他節點的元資料,訊息佇列的資料還是在乙個節點上,如果這個節點掛了那就意味著發訊息就會失敗,無法保證訊息佇列的高可用。

映象模式

預設情況下,rabbitmq中queue與binding、exchange不一樣,它只會存於宣告佇列的節點中,但是可以選擇使queue跨多個節點進行映象。

每乙個映象佇列由乙個master和乙個或多個映象組成,任何佇列的的操作,都會先應用到master節點上然後傳播到多個映象節點。如果master節點掛了,最老的映象節點將會成為新的master節點。

總結rabbitmq有兩種集群方法:普通模式映象模式,要實現訊息佇列的高可用可以選一種合適的集群方式來達到,關於rabbitmq的集群搭建方式,由於篇幅有限這裡就不多說,可自行檢視 distributed rabbitmq

文章。想象下消費者收到重複的訊息會發生什麼情況,比如訂單支付訊息,如果支付服務收到兩條重複的訊息讓使用者去支付兩次,那使用者肯定是不願意的,明明已經支付過了還要支付。

如上圖中第四步消費訊息b的時候失敗了,如果支付服務在做完業務之後,傳送ack之前服務掛了,mq沒有收到ack,由於訊息還存在佇列中,服務恢復正常後會再次收到訊息,如果支付不做檢查那使用者就會發生兩次支付。

要避免這個重複消費的問題,可以在消費端引入記憶體、redis、資料庫來儲存訊息消費記錄,根據訊息id來判斷訊息是否已經被消費過。

假設有訂單服務和支付服務,正常流程是使用者下單成功,然後向支付服務傳送支付訊息,這裡面就涉及訂單服務、支付服務、mq的互動了,訊息丟失可以分為三種情況:

生產者訊息丟失

mq訊息丟失

消費者訊息丟失

生產者訊息丟失

生產者訊息丟失,可以使用本地訊息表解決、訊息確認/重發等方式來解決。以rabbitmq為例,它有confirm機制,發出去的訊息是否入佇列,會使用**的形式告知生產者,生產者收到訊息後判斷是ack還是nak,如果是nak則重發訊息。

此時還會有問題,如果極端情況下訂單服務掛了,再次重啟後訊息就真丟失了,所以最好還是在生產中對訊息做持久化,待訂單服務恢復後使用job重新傳送訊息。

mq訊息丟失

mq訊息丟失一般為未開啟持久化,mq掛了再次重啟後訊息丟失,所以應當將訊息持久化到磁碟中。如果mq收到訊息後在同步到磁碟之前mq掛了,那磁碟中也沒有訊息,這樣還是會導致訊息丟失訊息,不過這只是小概率事件。

消費者訊息丟失

消費者訊息丟失,大都為開啟了autoack選項,消費者收到訊息後還未完成處理,此時服務掛了,由於開啟了autoack, mq會以為此訊息已經被成功消費,將訊息從佇列中移除,而服務恢復過後也不會收到原來的訊息了。

有些場景下要保持訊息的順序消費怎麼辦?比如寫log都是一條條列印出來,如果發到訊息佇列後出現消費順序不一致那訊息的那日誌就會亂掉,給看日誌的人帶來不必要的麻煩。比如為了加快日誌的處理速度使用三個消費都處理日誌:

按圖上的流程,消費者a、b、c可能分別消費日誌1、2、3,這時候就無法保證訊息的處理順序。要保證訊息的消費順序,首先讓訊息都傳送到同乙個佇列,然後使用乙個消費者去處理訊息:

這樣訊息的處理速度就大大降低,要保持訊息的順序,則又想讓訊息的處理速度不至於太慢,可以引用本地佇列:

訊息佇列常見問題

2 系統解耦 3 流量削峰 提到mq那麼我們必然會討論mq順序性問題,比如生產者傳送訊息1,2,3.對於消費者必須按照1,2,3.這樣的順序來消費,那麼訊息佇列應該怎麼樣去考慮這樣事情呢,有人說了訊息佇列是先進先出不就保證了順序性,其實並非如此,而且想通過佇列來保證順序性是非常困難的,那麼我們來看看...

linux 常見問題及解決

平時開發中需要連線到虛擬機器linux centos 進行,期間有些常見問題,在此記錄備忘 1 ssh連線突然變慢,在centos中ping一些常見 也特別慢 分析 估計dns解析有問題,檢視vm中的 etc resolv.conf 與本機dns差異,發現第乙個備用dns不同。ping 第乙個nam...

eclipse 常見問題及解決

1.target runtime apache tomcat v6.0 is not defined.錯誤解決方法 原文 解決方法 方法是 在工程目錄下的.settings資料夾裡,開啟org.eclipse.wst.common.project.facet.core.xml檔案,其內容是 將其修改...