文章目录
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
使用连接对象前的准备
创建用户和虚拟机,此处用的是admin用户创建的虚拟机
消费模型
直连模型
当多个消费者对应一个生产者时,消息会被平均分配到多个消费者上
任务模型
多个消费者绑定到一个生产者时,会是能者多劳的情况
创建连接对象(代码)
连接对象的工具类
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class RabbitMqUtil {
private static ConnectionFactory connectionFactory;
static {
// 重量级资源 ,让其类加载是执行一次即可
connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.10.166");
connectionFactory.setPort(5672);
// 设置虚拟主机
connectionFactory.setVirtualHost("ems");
// 设置访问虚拟主机的用户名和密码
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
}
// 获取连接对象的方法
public static Connection getConnection() {
try {
// 创建连接
return connectionFactory.newConnection();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 关闭通道和关闭连接的方法
public static void closeConnection(Channel channel, Connection connection) {
try {
if (channel != null) {
channel.close();
}
if (connection != null) {
connection.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
生产消息代码
public static void main(String[] args) throws IOException, TimeoutException {
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
// 通道绑定对应消息队列,生成者和消费者这个方法的参数要严格一致才能消费到
// 参数1 队列名称 ,此处不能决定发往哪个队列,需要下面basicPublish第二个参数决定
// 参数2 用来定义队列特性是否要持久化 true 持久化 false 不持久化,此处只是队列的持久化,并不是消息的持久化,消息的持久化需要设置下面basicPublish方法的第三个参数
// 参数3 exclusive 是否独占队列 true 独占队列 false 不独占队列
// 参数4 autoDelete 是否在消费完成后自动删除,true 自动删除 false 不自动删除,消费者只有关闭连接后队列才会删除
// 参数5 额外附加参数
channel.queueDeclare("topic",true,false,true,null);
// 发布消息 参数1 交换机名 参数2 队列名 参数3传递消息的额外设置 配合queueDeclare方法的地二个参数设置可以设置消息的持久化, 参数4 消息内容
channel.basicPublish("","topic",MessageProperties.PERSISTENT_TEXT_PLAIN,"hellomq2".getBytes());
RabbitMqUtil.closeConnection(channel, connection);
}
管理界面和代码参数的对应关系
消费者代码 - 自动确认消息
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
// 通道绑定对象
channel.queueDeclare("topic",true,false,true,null);
// 消费消息 参数1 队列名称 参数2 开始消息的自动确认机制 参数3 消费时的回调接口
channel.basicConsume("topic",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("======" + new String(body));
}
});
消费者代码-手动确认消息
如果代码没有走到channel.basicAck(envelope.getDeliveryTag(),false);
这一步,则消息会重新回到消息队列中给其他消费者消费,如果消费者在消费过程中异常了,则不会再消费信息,处于停止状态
修改的地方,
- 将
生产者和消费者
的channel.queueDeclare("topic",true,false,false,null);
代码的第四个参数也需要设置为false - 将消费者的
channel.basicConsume
的第二个参数是否自动确认消息设置为false, - 最后将消息手动确认
channel.basicAck(envelope.getDeliveryTag(),false);
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
channel.basicQos(1); // 一次只接受一条未确认的消息,需要配合下面envelope手动确认消息
// 通道绑定对象
channel.queueDeclare("topic",true,false,true,null);
// 消费消息 参数1 队列名称 参数2 开始消息的自动确认机制 参数3 消费时的回调接口
channel.basicConsume("topic",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("======" + new String(body));
// 参数1 :确认队列中哪个具体消息 ,参数2: 是否开启多个消息同时确认,true,可以同时处理信道中的多条消息确认,false 处理完成后确认当前消息消费完成
channel.basicAck(envelope.getDeliveryTag(),false); // 单条消息处里确认
// channel.basicReject(envelope.getDeliveryTag(),false); // 拒绝改消息,不处理该消息,第二个参数为是否重回队列,false为不重新回到队列,true为重新回到队列,消息会再次被消费,一般设置为false
}
});
第三个模型广播
在广播模式下,消息发送流程是这样的:
- 可以有多个消费者。消息会被所有绑定此交换机的消费者消费
- 每个消费者有自己的临时队列
- 消费者的队列都要绑定到交换机上
- 生产者生产消息时,只能发送到交换机上,交换机决定发送到哪
生产者代码
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
// 将通道指定交换机,参数1交换机名称 参数2 交换机类型 fanout 广播类型 参数固定为fanout
channel.exchangeDeclare("logs", "fanout");
// 发送消息
channel.basicPublish("logs", "", null, "fanout type message".getBytes());
// 释放资源
RabbitMqUtil.closeConnection(channel, connection);
消费者代码
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
// 通道绑定交换机
channel.exchangeDeclare("logs","fanout");
// 临时队列
String queue = channel.queueDeclare().getQueue();
// 绑定临时队列
channel.queueBind(queue,"logs","");
// 消费消息 参数1 队列名称 参数2 开始消息的自动确认机制 参数3 消费时的回调接口
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("======" + new String(body));
}
});
基于路由的直连模式
在direct模式下
- 队列与交换机的的绑定,不能是任意绑定了,而是要指定一个RoutingeKey,只有RoutingeKey完全一致的消费者才会消费到消息
生产者代码
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
// 将通道指定交换机,参数1交换机名称 参数2 交换机类型 direct路由类型 direct固定写死
channel.exchangeDeclare("logs_direct", "direct");
// 发送消息
String routingKey = "info";
channel.basicPublish("logs_direct", routingKey, null, "基于direct发布的消息".getBytes());
// 释放资源
RabbitMqUtil.closeConnection(channel, connection);
消费者代码
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
// 通道绑定交换机
channel.exchangeDeclare("logs_direct","direct");
// 临时队列
String queue = channel.queueDeclare().getQueue();
// 绑定临时队列和交换机,可同时绑定多个
channel.queueBind(queue,"logs_direct","info");
channel.queueBind(queue,"logs_direct","warn");
// 消费消息 参数1 队列名称 参数2 开始消息的自动确认机制 参数3 消费时的回调接口
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("======" + new String(body));
}
});
RoutingKey路由之订阅模式topic
topic类型的Exchange与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过topic类型Exchange可以让队列在绑定RoutingKey的时候使用通配符,这种模型RoutingKey一般是由一个或多个单词组成,多个单词之间用"."分割,例如item.insert
通配符
*
匹配任何一个词#
匹配一个或多个词
生产者代码
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
// 将通道指定交换机,参数1交换机名称 参数2 交换机类型 fanout 广播类型
channel.exchangeDeclare("topics", "topic");
// 发送消息
String routingKey = "user.save.findAll";
channel.basicPublish("topics", routingKey, null, "基于topic发布的消息".getBytes());
// 释放资源
RabbitMqUtil.closeConnection(channel, connection);
消费者代码
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
// 通道绑定交换机
String exchange = "topics";
channel.exchangeDeclare(exchange,"topic");
// 临时队列
String queue = channel.queueDeclare().getQueue();
// 绑定临时队列和交换机,可同时绑定多个
channel.queueBind(queue,exchange,"user.*"); // 可以消费到 ,user.*消费不到,只能是user.save.*才能消费到
// 消费消息 参数1 队列名称 参数2 开始消息的自动确认机制 参数3 消费时的回调接口
channel.basicConsume(queue,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("======" + new String(body));
}
});
springboot整合rabbitmq
注意点:如果只有生产者没有消费者,启动项目后在rabbitmq的管理界面是看不到交换机或者队列信息的,只有有消费者的时候才会生成队列信息
需要的依赖
<!--rabbitmq依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
需要的配置
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtual-host: / # 虚拟机
connection-timeout: 60000ms
# 支持发布确认
# publisher-confirms: true
publisher-confirm-type: correlated
# 支持发布返回
publisher-returns: true
cache:
channel:
size: 1
生产者代码
package li;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import rabbitmq.StartApplication;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {StartApplication.class})
public class RabbitMqTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testHello() {
rabbitTemplate.convertAndSend("hello","hello world");
}
@Test
public void testWork() {
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("work","work模式" + i);
}
}
@Test // fanout广播模式
public void testFanout() {
rabbitTemplate.convertAndSend("logs","","fanout的模式发送消息");
}
@Test // route 路由模式
public void testRoute() {
rabbitTemplate.convertAndSend("directs","info","direct的模式发送消息");
}
@Test // topic 动态路由模式
public void testTopic() {
rabbitTemplate.convertAndSend("topics","user.save","user.save发送消息");
}
}
直连模式的消费者代码
@Component
@RabbitListener(queuesToDeclare = @Queue(value = "hello"))
public class HelloConsumer {
@RabbitHandler
public void receive(String message){
System.out.println("接受到的信息" + message);
}
}
work模式
work模式消费者代码,和直连模式相同,只不过是多个直连模式的消费者,默认的分配是平均分配给多个消费者的
package rabbitmq.springboot;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class WorkConsumer {
@RabbitListener(queuesToDeclare = @Queue("work"))
public void receive1(String message){
System.out.println("work message1" + message);
}
@RabbitListener(queuesToDeclare = @Queue("work"))
public void receive2(String message){
System.out.println("work message2" + message);
}
}
fanout 广播模式的消费者代码
package rabbitmq.springboot;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class FanoutConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue, // 绑定临时队列
exchange = @Exchange(value = "logs",type = "fanout") // 绑定的交换机
)
})
public void receive1(String message) {
System.out.println("广播模式消费到的信息1" + message);
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue, // 绑定临时队列
exchange = @Exchange(value = "logs",type = "fanout") // 绑定的交换机
)
})
public void receive2(String message) {
System.out.println("广播模式消费到的信息2" + message);
}
}
route路由模式消费者的代码
package rabbitmq.springboot;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class RouteConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue, // 创建临时队列
exchange = @Exchange(value = "directs", type = "direct"), // 自定交换机名称和类型
key = {"info", "error"}
)
})
public void receive1(String message) {
System.out.println("路由模式接受的信息1" + message);
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue, // 创建临时队列
exchange = @Exchange(value = "directs", type = "direct"), // 自定交换机名称和类型
key = {"info"}
)
})
public void receive2(String message) {
System.out.println("路由模式接受的信息2" + message);
}
}
topic模式消费者代码
package rabbitmq.springboot;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class TopicConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value=@Queue,
exchange = @Exchange(name = "topics",type = "topic"),
key = {"user.save","order.#"}
)
})
public void receive1(String message){
System.out.println("topic模式接受到的信息1" + message);
}
@RabbitListener(bindings = {
@QueueBinding(
value=@Queue,
exchange = @Exchange(name = "topics",type = "topic"),
key = {"product.*","order.#"}
)
})
public void receive2(String message){
System.out.println("topic模式接受到的信息2" + message);
}
}
死信队列
概念
无法被消费的消息,英文缩写:DLX。Dead Letter Exchange(死信交换机
),当消息成为Dead Message后,可以被重新发送另一个交换机。所以交换机必须声明和绑定队列,和routingkey
此前提是要将消息手动确认,且基于点对点模式的,
死信的来源
- 消息TTL过期
- 队列达到最大长度
- 消息被拒绝 channel.basicReject(); 或者channel.basicNack();,且 requeue参数的值为false
代码案例
正常消费者代码
private static final String NORMAL_QUEUE = "normalQueue";
private static final String DEAD_EXCHANGE = "deadExchange";
private static final String NORMAL_EXCHANGE = "normalExchange";
private static final String DEAD_QUEUE = "deadTopic";
public static void main(String[] args) throws IOException {
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
channel.basicQos(1); // 一次只接受一条未确认的消息,需要配合下面envelope手动确认消息
Map<String, Object> arguments = new HashMap<>();
// arguments.put("x-message-ttl", 10000); // 消息过期时间
arguments.put("x-dead-letter-exchange", DEAD_EXCHANGE); // 死信的交换机
arguments.put("x-dead-letter-routing-key", "DEADKEY"); // 死信的RoutingKey
arguments.put("x-max-length", 6); // 队列设置的最大长度,超过长度的信息进入死信队列
// 声明队列
channel.queueDeclare(NORMAL_QUEUE, true, false, false, arguments);
channel.queueDeclare(DEAD_QUEUE, true, false, false, null);
// 声明交换机
channel.exchangeDeclare(NORMAL_EXCHANGE,BuiltinExchangeType.DIRECT);
channel.exchangeDeclare(DEAD_EXCHANGE,BuiltinExchangeType.DIRECT);
// 绑定队列
channel.queueBind(NORMAL_QUEUE,NORMAL_EXCHANGE,"normalkey");
channel.queueBind(DEAD_QUEUE,DEAD_EXCHANGE,"DEADKEY");
// 消费消息 参数1 队列名称 参数2 开始消息的自动确认机制 参数3 消费时的回调接口
channel.basicConsume(NORMAL_QUEUE,false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("======" + new String(body));
// 参数1 :确认队列中哪个具体消息 ,参数2: 是否开启多个消息同时确认
channel.basicAck(envelope.getDeliveryTag(),false); // 手动确认消息
}
});
额外参数的解释
- x-message-ttl:消息过期时间,单位毫秒
- x-dead-letter-exchange:死信队列的交换机
- x-dead-letter-routing-key:死信队列的RoutingKey
- x-max-length:绑定此参数的队列的最大长度
生成者代码
private static final String NORMAL_QUEUE = "normalQueue";
private static final String DEAD_EXCHANGE = "deadExchange";
public static void main(String[] args) throws IOException, TimeoutException {
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
// 声明队列的额外参数
Map<String, Object> arguments = new HashMap<>();
// arguments.put("x-message-ttl", 10000); // 消息过期时间,单位毫秒
arguments.put("x-dead-letter-exchange", DEAD_EXCHANGE); // 死信的交换机
arguments.put("x-dead-letter-routing-key", "DEADKEY"); // 死信的RoutingKey
arguments.put("x-max-length", 6); // 队列设置的最大长度,超过长度的信息进入死信队列
// 声明队列
channel.queueDeclare(NORMAL_QUEUE, true, false, false, arguments);
for (int i = 0; i < 10; i++) {
String body = "asdasda" + i;
channel.basicPublish("", NORMAL_QUEUE, MessageProperties.PERSISTENT_TEXT_PLAIN, body.getBytes());
}
RabbitMqUtil.closeConnection(channel, connection);
死信队列的消费者
private static final String DEAD_QUEUE = "deadTopic";
public static void main(String[] args) throws IOException {
// 创建连接
Connection connection = RabbitMqUtil.getConnection();
// 获取连接通道
Channel channel = connection.createChannel();
channel.basicQos(1); // 一次只接受一条未确认的消息,需要配合下面envelope手动确认消息
// 通道绑定对象
channel.queueDeclare(DEAD_QUEUE,true,false,false,null);
// 消费消息 参数1 队列名称 参数2 开始消息的自动确认机制 参数3 消费时的回调接口
channel.basicConsume(DEAD_QUEUE,false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("======" + new String(body));
// 参数1 :确认队列中哪个具体消息 ,参数2: 是否开启多个消息同时确认
channel.basicAck(envelope.getDeliveryTag(),false); // 手动确认消息
}
});
}
springboot中的死信队列和消费者
声明队列和交换机
package rabbitmq.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class RabbitDirectConfig {
public static final String NORMAL_QUEUE = "normalQueue";
public static final String DEAD_EXCHANGE = "deadExchange";
public static final String NORMAL_EXCHANGE = "normalExchange";
public static final String DEAD_QUEUE = "deadqueue";
@Bean
public Queue directQueue() {
//参数介绍
//1.队列名 2.是否持久化 3.是否独占 4.自动删除 5.其他参数
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-message-ttl", 10000); // 消息过期时间
arguments.put("x-dead-letter-exchange", DEAD_EXCHANGE); // 死信的交换机
arguments.put("x-dead-letter-routing-key", "dead"); // 死信的RoutingKey
return new Queue(NORMAL_QUEUE, false, false, false, arguments);
}
@Bean
public DirectExchange directExchange() {
//参数介绍
//1.交换器名 2.是否持久化 3.自动删除 4.其他参数
return new DirectExchange(NORMAL_EXCHANGE, false, false, null);
}
@Bean
public Binding bingExchange() {
return BindingBuilder.bind(directQueue()) //绑定队列
.to(directExchange()) //队列绑定到哪个交换器
.with("normal"); //绑定路由key,必须指定
}
// 死信队列
@Bean
public Queue deadQueue() {
//参数介绍
//1.队列名 2.是否持久化 3.是否独占 4.自动删除 5.其他参数
return new Queue(DEAD_QUEUE, false, false, false, null);
}
@Bean
public DirectExchange deadExchange() {
//参数介绍
//1.交换器名 2.是否持久化 3.自动删除 4.其他参数
return new DirectExchange(DEAD_EXCHANGE, false, false, null);
}
@Bean
public Binding bingDeadExchange() {
return BindingBuilder.bind(deadQueue()) //绑定队列
.to(deadExchange()) //队列绑定到哪个交换器
.with("dead"); //绑定路由key,必须指定
}
}
消费者代码
如果想让消息进入死信队列把普通队列的消费者代码注释掉即可
package rabbitmq.springboot;
import org.springframework.amqp.rabbit.annotation.Argument;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import rabbitmq.config.RabbitDirectConfig;
@Component
public class WorkConsumer {
@RabbitListener(queuesToDeclare = @Queue(value = RabbitDirectConfig.NORMAL_QUEUE, durable = "false", autoDelete = "false",
arguments = {@Argument(name = "x-message-ttl", value = "10000", type = "java.lang.Integer"),
@Argument(name = "x-dead-letter-exchange", value = RabbitDirectConfig.DEAD_EXCHANGE),
@Argument(name = "x-dead-letter-routing-key", value = "dead")}))
public void receive1(String message) {
System.out.println("work message1" + message);
}
@RabbitListener(queuesToDeclare = @Queue(value = RabbitDirectConfig.DEAD_QUEUE, durable = "false", autoDelete = "false"))
public void receive2(String message) {
System.out.println("work message2" + message);
}
}
生产者代码
生产者只需要指定队列名称
即可
package rabbitmq.controller;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import rabbitmq.config.RabbitDirectConfig;
@RestController
public class PublishMessage {
@Autowired
private RabbitTemplate rabbitTemplate;
@RequestMapping("/receiveMessage")
public void receiveMessage(@RequestBody String str) {
System.out.println("===" + str);
rabbitTemplate.convertAndSend(RabbitDirectConfig.NORMAL_QUEUE, "接收到的信息为" + str);
}
}
几种消费模式总结
- 基于真实队列queue的直连和任务模型消息只会被一个消费者消费,直连会平均分配给多个消费者,任务模型是能者多劳,
区别于下面的广播模式,此处队列有实际的名称,广播只是临时队列
, - 基于交换机exchange分广播fanout、直连direct和主题topic,其实这三个都是基于交换机的广播模式,消息会广播到所有的消费者消费,基于交换机的消费者都会有个
临时队列
, - 基于交换机类型为fanout的只要交换机名称一样,消费者就会收到消息
- 类型为direct的,只有交换机名称和RoutingKey都一致的消费者才会收到消息,交换机机可以类比一级分类,则RoutingKey为更细粒度的二级分类
- 类型为topic的,是在direct的基础上把RoutingKey由固定的值改为了公式,只要满足公式的消费者都会消费消息,比direct更加灵活
总结:如果想让同一消息只让一个消费者消费,则使用基于真实队列queue的模式(此种模式生产者生产消息后消息如果没有被消费掉则一直存在直到被消费,不必先有消费者,即点对点模式
),如果想让同一消息让多个消费者消费,则选择基于交换机exchange的模式,(此种模式必须先有消费者,否则如果没有消费者,消费者生成后不会消费到之前的消息,即发布订阅模式
)
异常的情况
- 如果队列已经存在,再次生产消息时如果渠道的绑定参数和之前的不一致会报错
- 生产者和消费的声明队列的参数要完全一致,包括第五个参数额外参数
- 如果测试时服务器没有安装rabbitmq,
想着连接rabbit失败也不影响项目启动
,可以将@RabbitListener
注解的类全部注释掉