RabbitMQ
RabbitMQ 即一個消息隊列,其是實現 AMQP(高級消息隊列協議)的消息中間件的一種
AMQP,即 Advanced Message Queuing Protocol,高級消息隊列協議,是應用層協議的一個開放標準,爲面向消息的中間件設計。消息中間件主要用於組件之間的解耦,消息的發送者無需知道消息使用者的存在,反之亦然。AMQP 的主要特徵是面向消息、隊列、路由(包括點對點和發佈/訂閱)、可靠性、安全。
RabbitMQ 是一個開源的 AMQP 實現,服務器端用Erlang語言編寫,支持多種客戶端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP 等,支持 AJAX。用於在分佈式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。
相關概念
RabbitMQ 多了一層交換器的概念,消息的發佈者不直接把消息發給隊列(Queue),而是先發給交換器(Exchange),交換器再根據調度策略發送給相應的隊列
Publisher
消息的發佈者,向交換器發佈消息的客戶端應用程序
Exchange
交換器,用來接收發布者發送的消息並將這些消息根據調度策略路由給相應的消息隊列,Exchange 用於轉發消息,但是它不會做存儲 ,如果沒有 Queue綁定到 Exchange 的話,它會直接丟棄掉 Producer 發送過來的消息。
這裏有一個比較重要的概念:**路由鍵 **。消息到交換機的時候,交互機會轉發到對應的隊列中,那麼究竟轉發到哪個隊列,就要根據該路由鍵。
交換器有四種類型:direct(默認),fanout,topic,headers,不同類型的交換器調度策略都不同
-
direct :路由鍵如果和綁定的鍵一致的畫,交換器就將消息發到對應的隊列中,路由鍵與隊列名完全匹配,如一個隊列綁定到交換器要求的路由鍵爲"key",則只轉發路由鍵標記爲"key"的消息,不會轉發其他消息,它是完成匹配,單播的模式
-
fanout:該類型交換器會把消息發到所有綁定的消息隊列上去,它不處理路由鍵,只是簡單的將隊列綁定到交換器上,類型廣播,速度快
-
topic:該類型交換器通過模式匹配來調度消息給消息隊列。將路由鍵和某個模式進行匹配,此時隊列需要綁定到一個模式上,它將路由鍵和綁定鍵的字符串切分成單詞,這些單詞之間用點隔開,還會識別兩個通配符,符號“#”,“*”,#匹配0個或多個單詞,*匹配一個單詞
Queue
消息隊列,用來保存消息直到發送給接收者,是消息的容器,一個消息可以發給一個或多個隊列
Binding
綁定,用於消息隊列和交換器之間的關聯,一個綁定就是基於路由鍵將交換器和消息隊列連接起來的路由規則,所有可以將交換器理解成一個由綁定構成的路由表,交換器和和隊列的綁定可以是多對多的關係
consumer
消息的接收者,表示從消息隊列中獲得消息
Virtual Host
一個虛擬主機持有一組交換機、隊列和綁定。爲什麼需要多個虛擬主機呢?很簡單, RabbitMQ 當中,用戶只能在虛擬主機的粒度進行權限控制。 因此,如果需要禁止A組訪問B組的交換機/隊列/綁定,必須爲A和B分別創建一個虛擬主機。每一個 RabbitMQ 服務器都有一個默認的虛擬主機“/”
RabbitMQ啓動
在Centos上用docker安裝RabbitMQ,安裝帶有management版本的,其帶有web管理界面
後臺啓動RabbitMQ
[root@LB ~]# docker run -d -p 5672:5672 -p 15672:15672 --name myrabbitmq 6e50ab4d9a6c
往後啓動RabbitMQ 只需要restart即可
[root@LB ~]# docker restart myrabbitmq
管理界面:
SpringBoot使用RabbitMQ
pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
RabbitMQ的配置
spring:
rabbitmq:
host: 192.168.1.118
username: guest
password: guest
SpringBoot中RabbitMQ
- 1.RabbitAutoConfiguration
- 2.rabbitConnectionFactory連接工廠,通過RabbitProperties獲得rabbitmq的配置
- 3.RabbitTemplate給Rabbitmq發送和接收消息
- AmqpAdmin,管理rabbitmq組件
使用
rabbitTemplate 是 Spring Boot 提供的默認實現
發送消息,事先已經創建好一個交換器
@Test
public void contextLoads() {
//Message需要自己構造一個且定義消息體內容和消息頭
//rabbitTemplate.send(exchange,routingKey ,message );
//只需要傳入要發送的對象,自動序列化發送給rabbitmq
//rabbitTemplate.convertAndSend(exchange,routingKey ,object );
Map<String,Object> map = new HashMap<>();
map.put("msg","這是一個測試消息" );
map.put("data", Arrays.asList("Hello Rabbitmq",123,true));
//對象被自動序列化發送
rabbitTemplate.convertAndSend("exchange.direct", "lb.news",map);
}
SpringBoot默認使用SimpleMessageConverter() 把數據序列化,我們還可以使其變成Jsno格式
@Configuration
public class MyConfig {
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
}
讓消息轉化器使用Jackson2JsonMessageConverter() 使其變成Jsno格式
接收消息:
@Test
public void receive(){
Object o = rabbitTemplate.receiveAndConvert("lb.news");
System.out.println(o.getClass());
System.out.println(o);
}
創建
SpringBoot中交換器是一個接口,其有五個實現類
- DirectExchange
/**
* 只有路由鍵於綁定完全匹配纔會發送
*/
@Test
public void directExchangeTest(){
//創建交換器
amqpAdmin.declareExchange(new DirectExchange("amqpAdmin.exchange"));
//創建消息隊列
amqpAdmin.declareQueue(new Queue("amqpAdmin.queue"));
amqpAdmin.declareQueue(new Queue("amqpAdmin.queue2"));
//創建綁定規則
amqpAdmin.declareBinding(new Binding("amqpAdmin.queue", Binding.DestinationType.QUEUE, "amqpAdmin.exchange","amqpAdmin.key" ,null ));
amqpAdmin.declareBinding(new Binding("amqpAdmin.queue2", Binding.DestinationType.QUEUE, "amqpAdmin.exchange","amqpAdmin.key2" ,null ));
//發送消息
rabbitTemplate.convertAndSend("amqpAdmin.exchange","amqpAdmin.key" , "amqpAdmin.key");
rabbitTemplate.convertAndSend("amqpAdmin.exchange","amqpAdmin.key2" , "amqpAdmin.key2");
}
- FanoutExchange
/**
* 所有綁定的隊列都會收到消息
*/
@Test
public void fanoutExchangeTest(){
//創建交換器
amqpAdmin.declareExchange(new FanoutExchange("amqpAdmin.fanout"));
//創建消息隊列
amqpAdmin.declareQueue(new Queue("fanout.queue"));
amqpAdmin.declareQueue(new Queue("fanout.queue2"));
//創建綁定規則
amqpAdmin.declareBinding(new Binding("fanout.queue", Binding.DestinationType.QUEUE, "amqpAdmin.fanout","fanout.key" ,null ));
amqpAdmin.declareBinding(new Binding("fanout.queue2", Binding.DestinationType.QUEUE, "amqpAdmin.fanout","fanout.key2" ,null ));
//發送消息
rabbitTemplate.convertAndSend("amqpAdmin.fanout","fanout.key" , "FanoutExchange");
// rabbitTemplate.convertAndSend("amqpAdmin.fanout","fanout.key2" , "amqpAdmin.key2");
}
- TopicExchange
/**
* 根據匹配策略匹配對應的路由鍵,發送給對應的隊列
*/
@Test
public void topicExchangeTest(){
//創建交換器
amqpAdmin.declareExchange(new TopicExchange("amqpAdmin.topic"));
//創建消息隊列
amqpAdmin.declareQueue(new Queue("topic.queue"));
amqpAdmin.declareQueue(new Queue("topic.queue2"));
//創建綁定規則,#匹配0個或多個單詞,*匹配一個單詞
amqpAdmin.declareBinding(new Binding("topic.queue", Binding.DestinationType.QUEUE, "amqpAdmin.topic","topic.*" ,null ));
amqpAdmin.declareBinding(new Binding("topic.queue2", Binding.DestinationType.QUEUE, "amqpAdmin.topic","topic.#" ,null ));
//發送消息
rabbitTemplate.convertAndSend("amqpAdmin.topic","topic.key" , "topic.key");
rabbitTemplate.convertAndSend("amqpAdmin.topic","topic.lb" , "topic.lb");
rabbitTemplate.convertAndSend("amqpAdmin.topic","topic.lb.nm" , "topic.lb.nm");
}
根據規則,topic.queue2會收到"topic.key" ,“topic.lb” ,“topic.lb.nm” 發來的消息
topic.queue會收到"topic.key" ,"topic.lb"發來的消息
完整的代碼在我的GitHub