一、釋出/訂閱模式
簡單佇列和工作佇列的共同特點:所發出的訊息只可能被一個消費者消費,因為一旦消費完就會從佇列裡刪除。這樣的特點不能滿足,需求:當使用者支付完成,去通知訂單服務、倉儲服務、簡訊服務,讓三個服務各自去完成自己的業務。也就是傳送訂單支付成功的訊息要被三個服務收到。
釋出訂閱模式與之前案例的區別就是允許將同一訊息傳送給多個消費者。實現方式是加入了exchange(交換機)
釋出訂閱的模型如圖:
可以看到,在訂閱模型中,多了一個exchange角色,而且過程略有變化:
- Publisher:生產者,也就是要傳送訊息的程式,但是不再傳送到佇列中,而是發給X(交換機)
- Exchange:交換機,圖中的X。一方面,接收生產者傳送的訊息。另一方面,知道如何處理訊息,例如遞交給某個特別佇列、遞交給所有佇列、或是將訊息丟棄。到底如何操作,取決於Exchange的型別。Exchange有以下3種型別:
- Fanout:廣播,將訊息交給所有繫結到交換機的佇列
- Direct:定向,把訊息交給符合指定routing key 的佇列
- Topic:萬用字元,把訊息交給符合routing pattern(路由模式) 的佇列
- Consumer:消費者,與以前一樣,訂閱佇列,沒有變化
- Queue:訊息佇列也與以前一樣,接收訊息、快取訊息。
注意:exchange負責訊息路由,而不是儲存,路由失敗則訊息丟失
Exchange(交換機)只負責轉發訊息,不具備儲存訊息的能力,因此如果沒有任何佇列與Exchange繫結,或者沒有符合路由規則的佇列,那麼訊息會丟失!
二、釋出訂閱 -Fanout Exchange
釋出訂閱 -Fanout Exchange
Fanout,英文翻譯是扇出,我覺得在MQ中叫廣播更合適。Fanout Exchange 會將接收到的訊息路由到每一個跟其繫結的queue
在廣播模式下,訊息傳送流程是這樣的:
- 1) 可以有多個佇列
- 2) 每個佇列都要繫結到Exchange(交換機)
- 3) 生產者傳送的訊息,只能傳送到交換機,交換機來決定要發給哪個佇列,生產者無法決定
- 4) 交換機把訊息傳送給繫結過的所有佇列
- 5) 訂閱佇列的消費者都能拿到訊息
我們的計劃是這樣的:
- 建立一個交換機 it.fanout,型別是Fanout
- 建立兩個佇列fanout.queue1和fanout.queue2,繫結到交換機it.fanout
三、釋出訂閱 -Fanout Exchange演示案例
實現思路:
1.在consumer服務中,利用程式碼宣告佇列,交換機,並將兩者繫結
2.在consumer服務中,編寫兩個消費者方法,分別監聽fanout.queue1和fanout.queue2
3.在publisher中編寫測試方法,向it.fanout傳送訊息
具體實現步驟:
步驟一:在consumer服務宣告Exchange、Queue、Binding
SpringAMQP提供了宣告交換機、佇列,繫結關係的API,如圖:
在consumer服務中建立一個類, 新增@configuration註解,並宣告FanoutExchange、Queue 和繫結物件binding,程式碼如下:
import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FanoutConfig { /** * 宣告交換機 * @return Fanout型別交換機 */ @Bean public FanoutExchange fanoutExchange(){ return new FanoutExchange("it.fanout"); } /** * 第1個佇列 */ @Bean public Queue fanoutQueue1(){ return new Queue("fanout.queue1"); } /** * 繫結佇列和交換機 */ @Bean public Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange){ return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange); } /** * 第2個佇列 */ @Bean public Queue fanoutQueue2(){ return new Queue("fanout.queue2"); } /** * 繫結佇列和交換機 */ @Bean public Binding bindingQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange){ return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange); } }
步驟二:訊息傳送
在publisher服務的SpringAmqpTest類中新增測試方法,程式碼如下:
@Test public void testFanoutExchange() { // 交換機名稱 String exchangeName = "it.fanout"; // 訊息 String message = "hello, everyone!"; rabbitTemplate.convertAndSend(exchangeName, "", message); }
步驟三:訊息接收
在consumer服務的SpringRabbitListener中新增兩個方法,作為消費者:
@RabbitListener(queues = "fanout.queue1") public void listenFanoutQueue1(String msg) { System.out.println("消費者1接收到Fanout訊息:【" + msg + "】"); } @RabbitListener(queues = "fanout.queue2") public void listenFanoutQueue2(String msg) { System.out.println("消費者2接收到Fanout訊息:【" + msg + "】"); }
步驟四:測試。執行測試,消費者1、消費者2收到了相同的訊息,實現了一次傳送,多個消費者都能接收。
總結:交換機的作用:
- 接收publisher傳送的訊息
- 將訊息按照規則路由到與之繫結的佇列
- 不能快取訊息,路由失敗,訊息丟失
- FanoutExchange的會將訊息路由到每個繫結的佇列