參考文章:https://blog.csdn.net/hellozpc/article/details/81436980
安裝時需注意:
1.需要安裝erlang環境,配置環境變量。
2.需要注意RabbitMQ對應的erLang的版本,版本不對會出錯。
3.我的代碼,RabbitMQ版本爲 最新版本3.8.3,消費者代碼有所不同
項目下載地址:
https://github.com/prettycharacter/myRabbitMQ.git
五種配置模式的實現
準備工作:
1.引入RabbitMQ客戶端依賴
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.創建連接類ConnectionUtil
package com.ampq.myrabbitmq.utils;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @author xpf
* @date 2020/5/14 14:35
*/
public class ConnectionUtil {
public static Connection getConnection() throws Exception {
//定義連接工廠
ConnectionFactory factory = new ConnectionFactory();
//設置服務地址
factory.setHost("localhost");
//端口
factory.setPort(5672);
//設置賬號信息,用戶名、密碼、vhost
factory.setVirtualHost("/");
//自己定義的用戶.
factory.setUsername("mqadmin");
factory.setPassword("mqadmin123");
// 通過工程獲取連接
Connection connection = factory.newConnection();
return connection;
}
}
1.簡單隊列
生產者將消息發送到隊列,消費者從隊列中獲取消息
生產者發送消息
public static void main(String[] args) throws Exception {
//獲取連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
//從連接中創建通道
Channel channel = connection.createChannel();
//創建隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//消息內容
for (int i = 0; i < 10; i++) {
String message = "Hello World+" + i;
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println("send:" + message);
Thread.sleep(10);
}
//關閉通道和連接
channel.close();
connection.close();
}
//輸出:
send:Hello World
生產者發送消息以後,在RabbitMQ服務端可以看見這條消息。
消費者接受消息
public static void main(String[] args) throws Exception {
//獲取連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
//創建通道
Channel channel = connection.createChannel();
//創建隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//獲取消息
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
System.out.println("[Receive]:" + message);
}
};
channel.basicConsume(QUEUE_NAME, consumer);
}
//輸出:
[Receive]:Hello World
2.Work模式
一個生產者,多個消費者, 一個消息只能被一個消費者獲取。
①輪詢分發消息
輪詢(Round-Robin)分發 :使用任務隊列的優點之一就是可以輕易的並行工作。默認情況下,RabbitMQ將逐個發送消息到在序列中的下一個消費者(而不考慮每個任務的時長等等,且是提前一次性分配,並非一個一個分配)。平均每個消費者獲得相同數量的消息。這種方式分發消息機制稱爲輪詢。
運行測試時先啓動消費者監聽。
消費者接收消息
//消費者1
private final static String QUEUE_NAME = "q_test_2";
public static void main(String[] args) throws Exception {
//獲取連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
//創建通道
Channel channel = connection.createChannel();
//創建隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//獲取消息
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
System.out.println("[Receive]:" + message);
try {
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
}
};
//監聽隊列,false表示手動返回完成狀態,true表示自動
channel.basicConsume(QUEUE_NAME,true,consumer);
}
//消費者2
private final static String QUEUE_NAME = "q_test_2";
public static void main(String[] args) throws Exception {
//獲取連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
//創建通道
Channel channel = connection.createChannel();
//創建隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//獲取消息
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
System.out.println("[Receive]:" + message);
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
};
//監聽隊列,false表示手動返回完成狀態,true表示自動
channel.basicConsume(QUEUE_NAME,true,consumer);
}
生產者發送消息
public static void main(String[] args) throws Exception {
//獲取連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
//從連接中創建通道
Channel channel = connection.createChannel();
//創建隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//消息內容
for (int i = 0; i < 100; i++) {
String message = "Hello World+" + i;
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println("send:" + message);
Thread.sleep(10);
}
//關閉通道和連接
channel.close();
connection.close();
}
上面測試發現:兩個消費者處理的消息一樣多
②公平分發消息(能者多勞)
公平分發 :上面的分配法方式也還行,但是有個問題就是:有兩個消費者,都能處理B消息,消費者1號非常繁忙,消費者2號空閒,但是rabbitMQ並不知道這個情況,只會輪詢發送消息,造成了不均衡的情況。
解決方式:我們通過設置 basicQos(prefetchCount=1)方法,限制RabbitMQ只發一條消息給同一個消費者,消費者處理完消息,有了反饋,才發送第二次消息。
需要注意:關閉自動應答,改爲手動應答。
消費者接收消息
public static void main(String[] args) throws Exception {
//獲取連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
//創建通道
Channel channel = connection.createChannel();
//創建隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//同一時刻服務器只會發送一條消息
channel.basicQos(1);
//獲取消息
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
//表示使用手動確認模式
System.out.println("[Receive]:" + message);
try {
Thread.sleep(10);//兩個消費者這裏的代碼不一樣
}catch (InterruptedException e){
e.printStackTrace();
}finally {
//改爲手動確認
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//監聽隊列,false表示手動返回完成狀態,true表示自動
channel.basicConsume(QUEUE_NAME,false,consumer);
}
生產者發送消息
生產者代碼不變
上面測試發現:睡眠時間長的處理消息少(能者多勞)
③消息的確認模式
模式1:自動確認-只要消費者獲取到消息,無論消費者是否執行成功,都認爲消息已經消費
模式2:手動確認-消費者獲取消息-消息變爲不可用狀態-消費者成功後反饋-消息消費
channel.basicConsume("",true/false,consumer);
true 表示自動確認
false 表示手動確認
//反饋消息的狀態
channel.basicAck(envelope.getDeliveryTag(),false);
3.訂閱模式(Publish/Subscribe)
- 1個生產者-多個消費者
- 每個消費者都有自己的一個隊列
- 生產者沒有將消息直接發送給隊列,而是發送到了交換機
- 每個隊列都要綁定到交換機
- 生產者發送的消息,經過交換機到達隊列,實現一個消息被多個消費者獲取的目的。
- 一個消費者隊列可以有多個消費者實例,只有其中一個消費者實例會消費。
生產者發送消息
private final static String EXCHANGE_NAME = "exchange_fanout";
public static void main(String[] args) throws Exception {
//獲取連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
//從連接中創建通道
Channel channel = connection.createChannel();
//聲明exchange
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
//消息內容
String message = "Hello World";
channel.basicPublish(EXCHANGE_NAME,"", null, message.getBytes());
System.out.println("send:" + message);
//關閉通道和連接
channel.close();
connection.close();
}
//輸出:
send:Hello World
生產者發送消息以後,在RabbitMQ服務端可以看見這條消息。
消費者接受消息
//消費者1
private final static String QUEUE_NAME = "test_queue_1";
private final static String EXCHANGE_NAME = "exchange_fanout";
public static void main(String[] args) throws Exception {
//獲取連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
//創建通道
Channel channel = connection.createChannel();
//創建隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//同一時刻服務器只會發送一條消息
channel.basicQos(1);
//獲取消息
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
//表示使用手動確認模式
System.out.println("[Receive]:" + message);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//監聽隊列,false表示手動返回完成狀態,true表示自動
channel.basicConsume(QUEUE_NAME, false, consumer);
}
//消費者2
private final static String QUEUE_NAME = "test_queue_2";
private final static String EXCHANGE_NAME = "exchange_fanout";
public static void main(String[] args) throws Exception {
//獲取連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
//創建通道
Channel channel = connection.createChannel();
//創建隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//同一時刻服務器只會發送一條消息
channel.basicQos(1);
//獲取消息
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "utf-8");
//表示使用手動確認模式
System.out.println("[Receive]:" + message);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//監聽隊列,false表示手動返回完成狀態,true表示自動
channel.basicConsume(QUEUE_NAME, false, consumer);
}
//輸出:
[Receive]:Hello World
上面測試發現:同一個消息被多個消費者獲取。一個消費者隊列可以有多個消費者實例,只有其中一個消費者實例會消費到消息。
4.路由模式
- 跟訂閱模式類似,只不過在訂閱模式的基礎上加上了類型,訂閱模式是分發到所有綁定到交換機的隊列,路由模式只分發到綁定在交換機上面指定路由鍵的隊列。
- 生產者申明一個direct類型交換機,然後發送消息到這個交換機指定路由鍵。
- 消費者指定消費這個交換機的這個路由鍵,即可接收到消息,其他消費者收不到。
生產者發送消息
//聲明交換機(exchange),direct爲交換機類型。
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//輸出:
send:Hello World
生產者發送消息以後,在RabbitMQ服務端可以看見這條消息。
消費者接受消息
//消費者1
//綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "routingtest");//消費者1
//消費者2
//綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "routingtest");
//輸出:
[Receive]:Hello World
上面測試發現:路由模式只分發到綁定在交換機上面指定路由鍵的隊列
5.主題模式(通配符模式)
- 將路由鍵和某模式進行匹配
- 隊列綁定到一個模式上, #匹配一個或者多個詞,*匹配一個詞。
生產者發送消息
//聲明交換機(exchange),direct爲交換機類型。
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//消息key爲 routingtest
channel.basicPublish(EXCHANGE_NAME,"topic.notice.all", null, message.getBytes());
//輸出:
send:Hello World
生產者發送消息以後,在RabbitMQ服務端可以看見這條消息。
消費者接受消息
//消費者1
//綁定隊列到交換機
//綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "topic.*");
//消費者2
//綁定隊列到交換機
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "topic.#");
//輸出:
[Receive]:Hello World
*上面測試發現:topic模式可以模糊匹配路由鍵,只會匹配一個詞,#匹配多個詞
SpringBoot集成RabbitMQ
SpringBoot集成RabbitMQ比較簡單,使用的配置非常少,SpringBoot提供了Spring-boot-starter-amqp對各種消息的支持
準備工作:
1.配置pom文件,引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.配置application.yml文件
//配置rabbitmq的安裝地址、端口以及賬戶信息
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: mqadmin
password: xykj.8387
server:
port: 8888
3.靜態常量文件
package com.ampq.myrabbitmq.utils;
/**
* @author xpf
* @date 2020/5/18 14:13
*/
public class Constants {
//簡單模式隊列名稱
public final static String BOOT_QUEUE_SIMPLE = "boot_queue_simple";
//work模式隊列名稱
public final static String BOOT_QUEUE_WORK = "boot_queue_work";
//計算能者多勞接收的數量
public static int RECEIVE_1 = 0;
public static int RECEIVE_2 = 0;
//訂閱模式
//隊列
public final static String A_BOOT_QUEUE_PUBLISH = "a_boot_queue_publish";
public final static String B_BOOT_QUEUE_PUBLISH = "b_boot_queue_publish";
public final static String C_BOOT_QUEUE_PUBLISH = "c_boot_queue_publish";
//交換機
public final static String BOOT_EXCHANGE_PUBLISH = "boot_exchange_publish";
//topic模式
//隊列
public final static String BOOT_QUEUE_TOPIC_A = "boot_queue_topic_a";
public final static String BOOT_QUEUE_TOPIC_B = "boot_queue_topic_b";
//交換機
public final static String BOOT_EXCHANGE_TOPIC = "boot_exchange_topic";
}
4.配置隊列
package com.ampq.myrabbitmq.springboot.rabbitmq;
import com.ampq.myrabbitmq.utils.Constants;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author xpf
* @date 2020/5/18 10:09
*/
@Configuration
public class RabbitConfig {
//簡單模式
@Bean
public Queue queue() {
return new Queue(Constants.BOOT_QUEUE_SIMPLE);
}
//工作模式
@Bean
public Queue queueWork() {
return new Queue(Constants.BOOT_QUEUE_WORK);
}
@Bean("workListenerFactory")
public RabbitListenerContainerFactory myFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
containerFactory.setConnectionFactory(connectionFactory);
//自動ack,沒有異常情況下自動發送ack
//auto 自動確認,默認是Auto
//MANUAL 手動確認
//none 不確認,發完自動丟棄
containerFactory.setAcknowledgeMode(AcknowledgeMode.AUTO);
//拒絕策略,true回到隊列,false是丟棄,默認爲true
containerFactory.setDefaultRequeueRejected(true);
//默認的prefetchCount是250,效率低
//設置爲1
containerFactory.setPrefetchCount(1);
return containerFactory;
}
//訂閱模式
//隊列
@Bean
public Queue aPublishQueue(){
return new Queue(Constants.A_BOOT_QUEUE_PUBLISH);
}
@Bean
public Queue bPublishQueue(){
return new Queue(Constants.B_BOOT_QUEUE_PUBLISH);
}
@Bean
public Queue cPublishQueue(){
return new Queue(Constants.C_BOOT_QUEUE_PUBLISH);
}
//交換機
@Bean
FanoutExchange fanoutExchange(){
return new FanoutExchange(Constants.BOOT_EXCHANGE_PUBLISH);
}
//綁定交換機
@Bean
Binding bindingExchangeA(Queue aPublishQueue,FanoutExchange fanoutExchange){
return BindingBuilder.bind(aPublishQueue).to(fanoutExchange);
}
@Bean
Binding bindingExchangeB(Queue bPublishQueue,FanoutExchange fanoutExchange){
return BindingBuilder.bind(bPublishQueue).to(fanoutExchange);
}
@Bean
Binding bindingExchangeC(Queue cPublishQueue,FanoutExchange fanoutExchange){
return BindingBuilder.bind(cPublishQueue).to(fanoutExchange);
}
//topic模式
//隊列
@Bean
Queue topicQueueA(){
return new Queue(Constants.BOOT_QUEUE_TOPIC_A);
}
@Bean
Queue topicQueueB(){
return new Queue(Constants.BOOT_QUEUE_TOPIC_B);
}
//topic交換機
@Bean
TopicExchange topicExchange(){
return new TopicExchange(Constants.BOOT_EXCHANGE_TOPIC);
}
//綁定交換機
@Bean
Binding bindingTopicExchangeA(Queue topicQueueA,TopicExchange topicExchange){
return BindingBuilder.bind(topicQueueA).to(topicExchange).with("topic.message.*");
}
@Bean
Binding bindingTopicExchangeB(Queue topicQueueB,TopicExchange topicExchange){
return BindingBuilder.bind(topicQueueB).to(topicExchange).with("topic.message.#");
}
}
1.簡單模式
之後的代碼沒有導包路徑跟下面一樣的
生產者發送消息
package com.ampq.myrabbitmq.springboot.rabbitmq.simple;
import com.ampq.myrabbitmq.utils.Constants;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author xpf
* @date 2020/5/18 10:15
*/
@Component
public class SimpleSender {
@Resource
private AmqpTemplate rabbitTemplate;
public void send(){
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String context = "hello "+date;
System.out.println("sender :"+ context);
//簡單隊列情況下routingkey即爲Q名
this.rabbitTemplate.convertAndSend(Constants.BOOT_QUEUE_SIMPLE,context);
}
}
生產者發送消息以後,在RabbitMQ服務端可以看見這條消息。
消費者接受消息
package com.ampq.myrabbitmq.springboot.rabbitmq.simple;
import com.ampq.myrabbitmq.utils.Constants;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @author xpf
* @date 2020/5/18 10:20
*/
@Component
@RabbitListener(queues = Constants.BOOT_QUEUE_SIMPLE)
public class SimpleReceiver {
@RabbitHandler
public void process(String hello){
//接收消息
System.out.println("receiver:"+ hello);
}
}
測試代碼
package com.ampq.myrabbitmq;
import com.ampq.myrabbitmq.springboot.rabbitmq.simple.SimpleSender;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class MyRabbitmqApplicationTests {
@Resource
private SimpleSender simpleSender;
@Test
void contextLoads() {
simpleSender.send();
}
}
//輸出
...
sender :hello 2020-05-18 10:28:41
2020-05-18 10:28:41.179 INFO 12560 --- [extShutdownHook] o.s.a.r.l.SimpleMessageListenerContainer : Waiting for workers to finish.
receiver:hello 2020-05-18 10:28:41
...
2.work模式
生產者發送消息
@Component
public class WorkSender {
@Resource
private AmqpTemplate rabbitTemplate;
public void send(int i){
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String context = "hello "+ i +" "+date;
//簡單隊列情況下routingkey即爲Q名
this.rabbitTemplate.convertAndSend(Constants.BOOT_QUEUE_WORK,context);
}
}
生產者發送消息以後,在RabbitMQ服務端可以看見這條消息。
消費者接受消息
//消費者1
@Component
@RabbitListener(queues = Constants.BOOT_QUEUE_WORK, containerFactory = "workListenerFactory")
/*
這裏需要注意,加上containerFactory就是能者多勞模式,不加就是輪詢模式
*/
public class WorkReceiver1 {
@RabbitHandler
public void process(String hello) throws Exception {
Thread.sleep(10);
Constants.RECEIVE_1++;
//接收消息
System.out.println("receiver1:" + hello + " " + Constants.RECEIVE_1);
}
}
//消費者2
@Component
@RabbitListener(queues = Constants.BOOT_QUEUE_WORK,containerFactory = "workListenerFactory")
public class WorkReceiver2 {
@RabbitHandler
public void process(String hello) throws Exception {
Constants.RECEIVE_2++;
Thread.sleep(20);
//接收消息
System.out.println("receiver2:" + hello+" "+Constants.RECEIVE_2);
}
}
測試代碼
@Test
void workQueue() throws Exception {
for (int i = 0; i < 100; i++) {
workSender.send(i);
}
}
//輸出
...
//消費者1輸出64條
receiver1:hello 97 2020-05-18 15:30:10 64
//消費者2輸出34條
receiver2:hello 96 2020-05-18 15:30:10 34
...
3.訂閱模式(Publish/subcsribe)
生產者發送消息
@Component
public class PublishSender {
@Resource
private AmqpTemplate rabbitTemplate;
public void send(){
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String context = "publish : "+date;
//簡單隊列情況下routingkey即爲Q名
this.rabbitTemplate.convertAndSend(Constants.BOOT_EXCHANGE_PUBLISH,"",context);
}
}
生產者發送消息以後,在RabbitMQ服務端可以看見這條消息。
消費者接受消息
//消費者1
@Component
@RabbitListener(queues = Constants.A_BOOT_QUEUE_PUBLISH)
public class PublishReceiver1 {
@RabbitHandler
public void process(String hello) throws Exception {
//接收消息
System.out.println("receiver1:" + hello);
}
}
//消費者2
@Component
@RabbitListener(queues = Constants.B_BOOT_QUEUE_PUBLISH)
public class PublishReceiver2 {
@RabbitHandler
public void process(String hello) throws Exception {
//接收消息
System.out.println("receiver2:" + hello);
}
}
//消費者3
@Component
@RabbitListener(queues = Constants.C_BOOT_QUEUE_PUBLISH)
public class PublishReceiver3 {
@RabbitHandler
public void process(String hello) throws Exception {
//接收消息
System.out.println("receiver3:" + hello);
}
}
測試代碼
@Test
void PublishQueue() throws Exception {
publishSender.send();
}
//輸出
...
receiver2:publish : 2020-05-18 16:04:48
receiver1:publish : 2020-05-18 16:04:48
receiver3:publish : 2020-05-18 16:04:48
...
//綁定了交換機的隊列都收到消息
4.topic模式(通配符)
生產者發送消息
@Component
public class TopicSender {
@Resource
private AmqpTemplate rabbitTemplate;
public void send1(){
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String context = "topic1 : "+date;
//簡單隊列情況下routingkey即爲Q名
this.rabbitTemplate.convertAndSend(Constants.BOOT_EXCHANGE_TOPIC,"topic.message.x.y",context);
}
public void send2(){
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String context = "topic2 : "+date;
//簡單隊列情況下routingkey即爲Q名
this.rabbitTemplate.convertAndSend(Constants.BOOT_EXCHANGE_TOPIC,"topic.message.x",context);
}
}
生產者發送消息以後,在RabbitMQ服務端可以看見這條消息。
消費者接受消息
//消費者1
@Component
@RabbitListener(queues = Constants.BOOT_QUEUE_TOPIC_A)
public class TopicReceiver1 {
@RabbitHandler
public void process(String hello) throws Exception {
//接收消息
System.out.println("receiver1:" + hello);
}
}
//消費者2
@Component
@RabbitListener(queues = Constants.BOOT_QUEUE_TOPIC_B)
public class TopicReceiver2 {
@RabbitHandler
public void process(String hello) throws Exception {
//接收消息
System.out.println("receiver2:" + hello);
}
}
測試代碼
/**
* topic模式測試
*/
@Test
void TopicQueue() throws Exception {
topicSender.send1();
topicSender.send2();
}
//輸出
...
receiver2:topic1 : 2020-05-18 16:37:02
receiver1:topic2 : 2020-05-18 16:37:02
receiver2:topic2 : 2020-05-18 16:37:02
...
//第一條消息消費者2接收了,第二條消息兩個消費者都接收了
生產者發送消息以後,在RabbitMQ服務端可以看見這條消息。
消費者接受消息
//消費者1
@Component
@RabbitListener(queues = Constants.BOOT_QUEUE_TOPIC_A)
public class TopicReceiver1 {
@RabbitHandler
public void process(String hello) throws Exception {
//接收消息
System.out.println("receiver1:" + hello);
}
}
//消費者2
@Component
@RabbitListener(queues = Constants.BOOT_QUEUE_TOPIC_B)
public class TopicReceiver2 {
@RabbitHandler
public void process(String hello) throws Exception {
//接收消息
System.out.println("receiver2:" + hello);
}
}
測試代碼
/**
* topic模式測試
*/
@Test
void TopicQueue() throws Exception {
topicSender.send1();
topicSender.send2();
}
//輸出
...
receiver2:topic1 : 2020-05-18 16:37:02
receiver1:topic2 : 2020-05-18 16:37:02
receiver2:topic2 : 2020-05-18 16:37:02
...
//第一條消息消費者2接收了,第二條消息兩個消費者都接收了