介紹
RabbitMQ是一個消息代理:它接受和轉發消息。你可以將其視爲郵局:當你把你想要寄出的郵件放在一個郵箱裏時,你可以確定,郵遞員先生或女士最終會把郵件交給你的收件人。在這個類比中,RabbitMQ是一個郵箱,郵局和郵遞員
安裝
Rabbit MQ 是建立在強大的Erlang OTP平臺上,因此安裝RabbitMQ之前要先安裝Erlang
下載Erlang:http://www.erlang.org/downloads
下載RabbitMQ:https://www.rabbitmq.com/download.html
下載安裝完成即可
使用web管理工具
運行命令:rabbitmq-plugins enable rabbitmq_management,即可開啓web管理插件
默認的端口是15672,通過http://localhost:15672即可訪問
默認的管理員賬戶密碼都是:guest
使用
rabbitMQ的官方文檔上共提供了6種使用方式:
下面將通過springboot(即 Spring AMQP)的方式來一一展示。
0.依賴以及配置
使用rabbitMQ只需要引入如下依賴即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
賬號配置:
默認端口是5672,虛擬主機爲/,賬戶爲guest,根據自己的情況填寫
spring.rabbitmq.host=localhost
spring.rabbitmq.virtual-host=/test
spring.rabbitmq.port=5672
spring.rabbitmq.username=test
spring.rabbitmq.password=test
1.Hello world模式
如圖所示,一個生產者將詳細發送到隊列中,消費者從隊列裏獲取消息。
1.1 定義一個隊列(也可以在RabbitMQ服務端創建隊列)
@Bean
public Queue myQueue() {
return new Queue("test");
}
1.2 定義一個生產者,將消息發送至隊列
package cn.coolwind.rabbitmqdemo.controller;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SenderController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/hello")
public Object hello() {
String msg = "Hello World 消息發送!";
rabbitTemplate.convertAndSend("test",msg);
return msg;
}
}
只需要使用 rabbitTemplate.convertAndSend("隊列名",發送的內容) 即可。
1.3 定義一個消費者,接收隊列裏的消息
package cn.coolwind.rabbitmqdemo.consumer;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class Consumer {
@RabbitListener(queues = "test")
public void helloWorldMsg(String msg) {
System.out.println("helloWorld消費者接收到的消息:"+msg);
}
}
只需要在處理消息的方法上加 @RabbitListener(queues = "隊列名")註解即可,方法裏的參數即爲接收到的消息。
1.4 運行
發送消息:
接收消息:
2.Work Queues模式
如圖所示,一個生產者將消息發送到隊列,可以由多個消費者來對隊列裏的消息進行消費,但每個消息只消費一次,即一條消息只有一個消費者處理。
2.1 定義一個生產者,向隊列發送消息
@GetMapping("/work")
public Object work() {
StringBuffer msg = new StringBuffer();
for (int i = 0; i < 10; i++) {
String content = "第" + i + "條Work Queues消息發送!";
rabbitTemplate.convertAndSend("workQueue",content);
msg.append(content);
}
return msg;
}
與helloworld模式的發送一致,這裏模擬發送10條消息。
2.2 定義消費者來接收消息
@RabbitListener(queues = "workQueue")
public void workMsg1(String msg) {
System.out.println("Work消費者1:"+msg);
}
@RabbitListener(queues = "workQueue")
public void workMsg2(String msg) {
System.out.println("Work消費者2:"+msg);
}
方式與helloworld模式相同,這裏定義了2個消費者來獲取消息
2.3運行
發送消息:
接收消息:
可以看出一條消息只會由一個消費者來消費,默認情況下,RabbitMQ將按順序將每條消息發送給下一個消費者。平均而言,每個消費者將獲得相同數量的消息。這種分發消息的方式稱爲循環法。
3.Publish/Subscribe模式
如圖所示,生產者先將消息發送至一個交換機X,然後交換機X將消息發送到選定的隊列裏(隊列1、隊列2...),最後由消費者去消費對應隊列裏的消息。
3.1 首先定義一個交換機
使用new FanoutExchange("名字")創建一個交換機
@Bean
public FanoutExchange fanout() {
return new FanoutExchange("publish");
}
3.2 創建需要使用的隊列
@Bean
public Queue subscribe1() {
return new AnonymousQueue();
}
@Bean
public Queue subscribe2() {
return new AnonymousQueue();
}
3.3 將隊列與交換機綁定
@Bean
public Binding binding1(FanoutExchange fanout, Queue subscribe1) {
return BindingBuilder.bind(subscribe1).to(fanout);
}
@Bean
public Binding binding2(FanoutExchange fanout, Queue subscribe2) {
return BindingBuilder.bind(subscribe2).to(fanout);
}
3.4 生產者發送消息
同樣是使用rabbitTemplate.convertAndSend()方法,裏面的參數爲:
(String exchange, String routingKey, Object object),分別是(交換機名字,路由,需要發送的消息)
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private FanoutExchange fanout;
@GetMapping("/publish")
public Object publish() {
StringBuffer msg = new StringBuffer();
for (int i = 0; i < 10; i++) {
String content = "第" + i + "條publish消息發送!";
rabbitTemplate.convertAndSend(fanout.getName(),"",content);
msg.append(content).append("</br>");
}
return msg;
}
3.5 消費者接受消息
同樣是使用之前的方式
@RabbitListener(queues = "#{subscribe1.name}")
public void publish1(String msg) {
log.info("發佈訂閱模式-消費者1:"+msg);
}
@RabbitListener(queues = "#{subscribe2.name}")
public void publish2(String msg) {
log.info("發佈訂閱模式-消費者2:"+msg);
}
3.6 運行
可以看到,兩個隊列裏的消費者都分別把消息接收了。(如果一個隊列裏有多個消費者的話,該隊列就會和之前的work模式一樣默認使用輪詢的機制接收消息)
4.Routing模式
如圖所示,生產者P將消息發送到交換機X,交換機X再根據對應的路由KEY(orange、black、green)來判斷髮送至哪一個隊列中(Q1、Q2),最後由相應的消費者(C1,C2)來進行消費。
4.1 首先定義一個交換機
使用new DirectExchange("名字"); 創建一個交換機
@Bean
public DirectExchange direct() {
return new DirectExchange("direct");
}
4.2 定義隊列
同3.2
@Bean
public Queue routingQueue1() {
return new AnonymousQueue();
}
@Bean
public Queue routingQueue2() {
return new AnonymousQueue();
}
4.3 綁定隊列與交換機
@Bean
public Binding bindRouting1AndQueue1(DirectExchange directExchange, Queue routingQueue1) {
//隊列1與directExchange交換機綁定,並且接收routingKey爲“routing1”的消息
return BindingBuilder.bind(routingQueue1).to(directExchange).with("routing1");
}
@Bean
public Binding bindRouting1AndQueue2(DirectExchange directExchange, Queue routingQueue2) {
//隊列2與directExchange交換機綁定,並且接收routingKey爲“routing1”的消息
return BindingBuilder.bind(routingQueue2).to(directExchange).with("routing1");
}
@Bean
public Binding bindRouting2AndQueue2(DirectExchange directExchange, Queue routingQueue2) {
//隊列2與directExchange交換機綁定,並且接收routingKey爲“routing2”的消息
return BindingBuilder.bind(routingQueue2).to(directExchange).with("routing2");
}
4.4 生產者發送消息
同樣是使用rabbitTemplate.convertAndSend()方法,裏面的參數爲:
(String exchange, String routingKey, Object object),分別是(交換機名字,路由,需要發送的消息)
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private DirectExchange direct;
@GetMapping("/routing")
public Object routing() {
StringBuffer msg = new StringBuffer();
for (int i = 0; i < 10; i++) {
String content = "第" + i + "條routing1消息發送!";
String content2 = "第" + i + "條routing2消息發送!";
rabbitTemplate.convertAndSend(direct.getName(),"routing1",content);
rabbitTemplate.convertAndSend(direct.getName(),"routing2",content2);
msg.append(content).append("</br>").append(content2).append("</br>");
}
return msg;
}
4.5 消費者接受消息
與之前相同
@RabbitListener(queues = "#{routingQueue1.name}")
public void routing1(String msg) {
log.info("queue1接收到的消息:"+msg);
}
@RabbitListener(queues = "#{routingQueue2.name}")
public void routing2(String msg) {
log.info("queue2接收到的消息:"+msg);
}
4.6 運行結果
隊列1只綁定了routing1,隊列2綁定了routing1,routing2;
所以隊列1只接受到了routing1的消息,而隊列2兩個消息都收到了
5 Topics 模式
topics 模式是routing模式的升級版,也就是將routing模式中的key變成了通配符的形式(圖中的:*.orange.* 、*.*.rabbit、lazy.#)。使用方式與routing模式大同小異。
以下只列出有差異的代碼,步驟與之前的事例相同,可以從之前的代碼類推
@Bean
public TopicExchange topic() {
return new TopicExchange("topic");
}
@Bean
public Binding bindTopic1AndQueue1(TopicExchange topicExchange, Queue topicQueue1) {
//隊列1與directExchange交換機綁定,並且接收topicKey爲“*.*.topic1”的消息
return BindingBuilder.bind(topicQueue1).to(topicExchange).with("*.*.topic1");
}
//發送消息時使用:第二個參數與綁定時的“*.*.topic1”相對應
rabbitTemplate.convertAndSend(topic.getName(),"a.b.topic1",content);
代碼:https://github.com/fi00wind/rabbitmq-demo