工作中用過一段時間的Kafka,不過主要還是RabbitMQ用的多一些。今天主要來講講與RabbitMQ相關的一些知識。一些基本概念,以及實際使用場景及一些注意事項。
1 基本概念
RabbitMQ使用Erlang開發,實現了高階訊息佇列協議(AMQP),同時還支援MQTT、STOMP等。
1.1 核心概念
- Broker: 訊息佇列服務主機
- Exchange: 訊息交換機,可按照特定規則將訊息路由到具體佇列 (上圖
X
即為交換機) - Queue: 訊息佇列,每個訊息理論都要被投入到一個或多個佇列 (上圖紅色矩形即為佇列)
- Binding: 繫結關係,將
Exchange
和Queue
按照某個路由規則進行繫結 - RoutingKey: 路由關鍵字,
Exchange
根據該Key進行不同的路由 - Vhost: 虛擬主機,在同一物理主機上做資源邏輯隔離,類似
名稱空間?
- Connection: 客戶端與主機建立的TCP連線
- Channel: 訊息通道,同一
Connection
可以有多個Channel
,每個Channel
都有一個唯一ID
1.2 交換機
按型別分四種:Direct Exchange,Topic Exchage,Fanout Exchange,Headers Exchange
1.2.1 Direct Exchange
直接交換機,在沒複雜路由場景時,這種路由器使用的較多。特點是嚴格RoutingKey
路由。只有當RoutingKey
完全一致時,才會今天路由投遞。
1.2.2 Fanout Exchange
廣播交換機,一個訊息需要無差別投遞到所有繫結的佇列中時。無需設定RoutingKey
,即使設定了也不會生效。
1.2.3 Topic Exchange
主題交換機,類似直接交換機。不同的是可以根據RoutingKey
進行模糊匹配(#
表示一個或多個單詞,*
表示一個單詞)。
這樣來看的話。
- 當不使用
#
或*
進行模糊匹配時,它跟Direct Exchange
似乎沒啥區別。 - 當僅使用
#
進行模糊匹配時,它跟Fanout Exchange
似乎也沒啥區別。
可以看到:
Q1
可以匹配:x11.orange.x22
,但無法匹配x00.x11.orange.x22
。Q2
可以匹配:lazy.orange
,也能匹配lazy.orange.rabbit
。
1.2.4 Headers Exchange
請求頭交換機,不使用RoutingKey
路由,根據訊息頭中的key-value
屬性進行路由。
可以透過設定x-match: any
、x-match: all
來進行or
、and
邏輯匹配。
1.2.5 其他屬性
- Durability: Durable(持久的),Transiant(短暫的)。
- Auto delete:當有佇列或交換機繫結了本交換機,在佇列或者交換機都又進行了解綁後,自動刪除
- Internal: 是否為內部使用。true 表示為內部交換機,客戶端無法直接向該交換機傳送訊息。
Arguments:
- alternate-exchange: 備份交換機,當訊息無法路由到具體佇列時,將交給備份交換機處理。
1.3 佇列
訊息只能存在於佇列中,下面看一下佇列都有哪些屬性。
- Type: Classic,Quorum
- Durability: Durable(持久的),Transiant(短暫的)
- Auto delete: 當有消費者訂閱,然後所有的消費者又都斷開連線,則自動刪除
Arguments:
- x-message-ttl:
Message
在Queue
中存活時間,超時將被丟棄 - x-expires:
Queue
在沒有被使用的情況下,過期時間 - x-max-length:
Queue
中可以存放的最大訊息數,超過將被丟棄 - x-max-length-bytes:
Queue
中可以存放最大訊息體大小,超過將被丟棄 - x-overflow:
Queue
溢位後的行為,drop-head
、reject-publish
、reject-publish-dlx
。 - x-single-active-consumer: 確保只有一個消費者訂閱,當出現異常時自動轉向另外一個。
- x-max-priority: 佇列支援的最大優先順序,不設定將不支援。
- x-dead-letter-exchange: 當
Message
被rejected
或expire
時,將會被重新發布到哪個交換機。 - x-dead-letter-routing-key: 當成為死信時,使用的
RoutingKey
,未設定時則使用原始Key
。 - x-queue-mode: 當設定為
lazy
時,會將訊息儘可能的放置到磁碟上,以減少記憶體使用。 - x-queue-master-locator: 將佇列設定為主節點定位模式。
- x-message-ttl:
2 使用場景
應用場景一般為三類: 非同步、解耦、削峰填谷。
2.1 非同步
非同步一般是指: 同一個系統內,使用佇列將請求和響應非同步化,不阻塞主執行緒,從而獲得更高的處理速度,以提升系統效能。
最早在做商城專案時。使用者下單付款後先更新本地訂單記錄,然後再向第三方物流系統推送待發貨訊息、向使用者發放訂單相關站內信、並可能觸發積分贈送及推薦人的返利計算、以及可能觸發相關營銷規則發放優惠券等操作。
2.2 解耦
解耦一般指: 不同系統間,透過佇列方式進行通訊,使系統間不至於過度依賴,減少系統間耦合性。
在最近的專案中,有做的一個事件上報系統,其實就是各個系統都接入。每個系統即可做生產者、也可以做消費者。比如使用者在下單後,需要給推薦人贈送積分、向外部CRM同步商機已成單狀態等。
2.3 削峰填谷
削峰填谷一般指: 利用佇列作為緩衝,將短時大流量快取起來,由消費者來決定處理速度。使得系統負載趨於平穩,從而提高系統穩定性。
在做使用者增長業務相關,使用者由落地頁提交資料至內部系統,內部系統會將資料簡單處理後推送到外部CRM系統,銷售在跟客戶溝通後,會將線索轉換為商機。在這個轉換過程中,會由CRM向內部系統推送資料。內部系統會根據傳遞過來的資料進行各種操作,如建立、更新家長,建立、更新學生,以及各種附加業務資訊等。
3 常見問題
3.1 訊息重複問題
訊息重複的場景很多,比如系統出錯、呼叫超時重試機制、訊息消費異常時ReQueue
操作等情況。很多時候為了保證訊息的可靠傳遞,會保證訊息at-least-once
。這種情況下基本無法保證不重複傳送,所以消費端要保證冪等性。
既然提到重複性,必然要有重複的依據。比如訂單號,同一系統內不允許重複。
- 可藉助資料庫的
唯一索引
,也可藉助Redis來保證資料唯一。 - 如果沒有重複依據,只能大概根據引數值按一定規則做雜湊處理,不過這種可能存在誤判和漏判問題。
3.2 訊息堆積
消費堆積,無非就是消費速度趕不上生產速度。產生的原因有很多,消費者異常出現消費變慢問題、生產者突發大流量等情況。
解決方案要同時考慮生產者和消費者。發生堆積時,將部分生產者降級,關閉非核心業務,減少訊息產生。消費者最佳化效能,將堆積訊息臨時轉移至新佇列,啟用新的消費者去消費。
3.3 訊息丟失問題
從整個流程考慮,生產者、佇列、消費者三方面。
- 生產者: 保證訊息成功推送到佇列。
- 佇列: 保證已推送到佇列中的資料不會丟失。
- 消費者: 保證已消費的資料被正確處理。
4 後記
系統設計沒有銀彈。在引入一箇中介軟體時,要綜合考慮、權衡利弊。引入中介軟體,能解決一些問題,但可能會帶來更多的問題。
做好系統監控,像消費堆積有些場景下是可預知的,比如臨時做活動或者低價促銷之類造成的訂單激增。而有些像系統故障類,無法提前預知,則需要監控系統做到及時告警,提前介入處理。
echo '5Y6f5Yib5paH56ugOiDmjpjph5Eo5L2g5oCO5LmI5Zad5aW26Iy25ZWKWzkyMzI0NTQ5NzU1NTA4MF0pL+aAneWQpihscGUyMzQp' | base64 -d