1.交換器
發消息者、隊列、收消息者,RabbitMQ 在這三個基本概念之上, 做了一層抽象, 在發消息者和 隊列之間, 加入了交換器 (Exchange). 這樣發消息者和隊列就沒有直接聯繫, 轉而變成發消息者把消息給交換器, 交換器根據調度策略再把消息再給隊列。
P 代表生產者,X代表交換器,後邊是隊列,C代表消費者。
其中中間一層server可以稱爲虛擬主機。
-
虛擬主機:一個虛擬主機持有一組交換機、隊列和綁定。爲什麼需要多個虛擬主機呢?很簡單,RabbitMQ當中,用戶只能在虛擬主機的粒度進行權限控制。 因此,如果需要禁止A組訪問B組的交換機/隊列/綁定,必須爲A和B分別創建一個虛擬主機。每一個RabbitMQ服務器都有一個默認的虛擬主機“/”。
-
交換機:Exchange 用於轉發消息,但是它不會做存儲 ,如果沒有 Queue bind 到 Exchange 的話,它會直接丟棄掉 Producer 發送過來的消息。
這裏有一個比較重要的概念:路由鍵 。消息到交換機的時候,交互機會轉發到對應的隊列中,那麼究竟轉發到哪個隊列,就要根據該路由鍵。 -
綁定:也就是交換機需要和隊列相綁定,這其中如上圖所示,是多對多的關係
交換機的功能主要是接收消息並且轉發到綁定的隊列,交換機不存儲消息,在啓用ack模式後,交換機找不到隊列會返回錯誤。交換機有四種類型:Direct, topic, Headers and Fanout
-
Direct:direct 類型的行爲是”先匹配, 再投送”. 即在綁定時設定一個 routing_key, 消息的routing_key 匹配時, 纔會被交換器投送到綁定的隊列中去.
-
Topic:按規則轉發消息(最靈活)
-
Headers:設置header attribute參數類型的交換機
-
Fanout:轉發消息到所有綁定隊列
代碼實現
Topic模式
新建兩個queue和一個topic交換器,並且,兩個隊列綁定到交換器上。
package com.haval.demo.mq;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
@Configuration
public class TopicRabbitConfig {
final static String message = "topic.queue";
final static String messages = "topic.queues";
@Bean
public Queue queueMessage() {
return new Queue(TopicRabbitConfig.message);
}
@Bean
public Queue queueMessages() {
return new Queue(TopicRabbitConfig.messages);
}
@Bean
TopicExchange exchange() {
return new TopicExchange("exchange");
}
@Bean
Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
return BindingBuilder.bind(queueMessage).to(exchange).with("topic.queue");
}
@Bean
Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) {
return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");
}
}
然後定義兩個消息發送的方法
public void sendMessage() {
for (int i = 0; i < 100; i++) {
String context = "Message " + new Date()+"-------"+String.valueOf(i);
System.out.println("Sender : " + context);
this.rabbitTemplate.convertAndSend("exchange",TopicRabbitConfig.message, context);
}
}
public void sendMessages() {
for (int i = 0; i < 100; i++) {
String context = "Messages " + new Date()+"---s----"+String.valueOf(22);
System.out.println("Sender : s " + context);
this.rabbitTemplate.convertAndSend("exchange",TopicRabbitConfig.messages, context);
}
}
然後定義兩個,隊列的監聽器
@Component
@RabbitListener(queues = TopicRabbitConfig.message)
public class TopicMessageReceiver {
@RabbitHandler
public void process(String hello) {
System.out.println("我是處理消息的111" + hello);
}
}
@Component
@RabbitListener(queues = TopicRabbitConfig.messages)
public class TopicMessagesReceiver {
@RabbitHandler
public void process(String hello) {
System.out.println("我是處理消息的222" + hello);
}
}
當執行sendMessage()的時候,兩個隊列都能收到消息,當執行sendMessages()的時候,只有queues能收到消息。
Fanout Exchange就是發佈和訂閱模式,給Fanout交換機發送消息,綁定了這個交換機的所有隊列都收到這個消息。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.amqp.core.TopicExchange;
@Configuration
public class FanoutRabbitConfig {
final static String Fanoutqueue1 = "Fanout.queue1";
final static String Fanoutqueue2 = "Fanout.queue2";
final static String Fanoutqueue3 = "Fanout.queue3";
@Bean
public Queue Fanoutqueue1() {
return new Queue(FanoutRabbitConfig.Fanoutqueue1);
}
@Bean
public Queue Fanoutqueue2() {
return new Queue(FanoutRabbitConfig.Fanoutqueue2);
}
@Bean
public Queue Fanoutqueue3() {
return new Queue(FanoutRabbitConfig.Fanoutqueue3);
}
@Bean
public FanoutExchange FanoutExchange() {
return new FanoutExchange("FanoutExchange");
}
@Bean
Binding bindingExchange1(Queue Fanoutqueue1, FanoutExchange FanoutExchange) {
return BindingBuilder.bind(Fanoutqueue1).to(FanoutExchange);
}
@Bean
Binding bindingExchange2(Queue Fanoutqueue2, FanoutExchange FanoutExchange) {
return BindingBuilder.bind(Fanoutqueue2).to(FanoutExchange);
}
@Bean
Binding bindingExchange3(Queue Fanoutqueue3, FanoutExchange FanoutExchange) {
return BindingBuilder.bind(Fanoutqueue3).to(FanoutExchange);
}
}
public void sendFanout(){
String context = "hi, fanout msg ";
System.out.println("Sender : " + context);
this.rabbitTemplate.convertAndSend("FanoutExchange","", context);
}
在綁定三個監聽器
@Component
@RabbitListener(queues = FanoutRabbitConfig.Fanoutqueue1)
public class FanoutMessageReceiver1 {
@RabbitHandler
public void process(String hello) {
System.out.println("我是處理消息的1" + hello);
}
}
Sender : hi, fanout msg
我是處理消息的1hi, fanout msg
我是處理消息的2hi, fanout msg
我是處理消息的3hi, fanout msg
三個隊列都會收到消息