RabbitMQ
一、特点
可靠性
多种机制,允许在性能和可靠性之间进行权衡
例:消息持久化,开启持久化会将消息写入磁盘,而磁盘速度远慢于内存。这会导致消息吞吐量下降,性能受损。
灵活的路由
多种交换机类型,不同的路由匹配规则。
实现自己的交换机类型当作插件使用。
多协议,插件支持
STOMP 文本协议(类似HTTP)
常用,命令和头部是文本,消息体可以是二进制或文本,解析成本较高。
MQTT 二进制协议
适用于带宽低、延迟高、网络不稳定、电力/计算资源受限的远程设备(物联网传感器、移动设备)。
RabbitMQ Streams 二进制协议
高吞吐量、低延迟的流式数据,适用于处理日志、遥测数据等大规模实时数据流场景。
HTTP/WebSocket
允许浏览器等客户端通过 WebSocket 传输 STOMP 或 MQTT 等协议
AMQP 0-9-1
定义了强大的消息语义(如 Exchange、Queue、Binding)
AMQP 1.0
国际标准,适用于不同消息中间件之间互相操作
可视化工具
部署mq后,打开服务器ip+配置的端口号,就是可视化面板
mq集群
联合模型
二、作用
解耦
两个功能的通信通过mq传递,当两个功能不再关联,不需要删除代码
异步
用户支付成功后需要加积分、赠送权益、发送微信通知,正常流程需要三者顺序进行,消耗三个功能的响应时间,使用mq同时进行,只消耗一个功能的时间。
削峰
一段时间内请求暴增,所有sql都挤进mysql执行,mysql处理不过来就会崩溃,进而导致系统瘫痪。
mq短时间能积压一部分数据,Qos限流机制给消费者消费,
12306点击购买后提示“购票流程已进入队列”,mq就起到削峰的作用。
三、简单原理
消息流程
生产者连接到MQ的Broker,创建Connection,开启channel;生产者发送消息并指定消息的RoutingKey,Exchange接收到消息根据Binding的路由规则转发给Queue,若Queue内有消息Broker会根据设定Push给监听的消费者
组成
Virtual Host
虚拟容器,出于多用户和安全设计,划分出多个vhost,如果有多个用户连接mq,每个用户创建独自的exchange/queue等,起到隔离的作用
Broker
rabbitmq的服务器
Exchange
direct 直连/精确模式:RoutingKey与BindingKey完全匹配
topic 主题/模糊模式:模糊匹配,通配符符合就放入该队列,交给Costumer消费者消费
fanout 订阅发布/广播模式:忽略RoutingKey,进入Exchange绑定的所有Queue
Headers:忽略RoutingKey,根据消息Heade属性匹配,极少使用
Quene
Costumer
Pull:消费者定时去队列中拿,有延迟性
Push:队列有消息,Broker就推送给消费者
实现流程
定义名称并配置交换机new TopicExchange和队列new Queue,BindingBuilder.bind绑定队列到交换机,rabbitAdmin初始化所有exchange和queue,生产方rabbitTemplate.convertAndSend指定exchange和RoutingKey,消费方@RabbitListener指定监听Queue名
四、机制
消息持久化
交换机和队列持久化默认是true可以不设置
持久化交换机:声明交换机时设置durable=true
持久化队列:声明队列时设置durable=true
发送持久化消息:发送消息时,将delivery_mode属性设置为2
消息应答
自动应答:队列将消息发给消费者后立即删除,不考虑消费者是否真正处理成功。
简单但不可靠,适合允许丢消息、日志收集等场景
// autoAck = true,消费后自动确认
channel.basicConsume("queue_name", true, (consumerTag, delivery) -> {
String message = new String(delivery.getBody());
System.out.println("收到消息: " + message);
// 方法执行完,RabbitMQ 自动认为消息已处理
}, consumerTag -> {});
手动应答:可靠但需要手动处理
三种应答方法
basicAck 确认成功 deliveryTag:消息标签 multiple:是否批量确认
basicNack 拒绝(可批量) deliveryTag:消息标签 multiple:是否批量 requeue:是否重新入队
basicReject 拒绝(单条) deliveryTag:消息标签 requeue:是否重新入队
使用手动应答时,如果忘记调用basicAck,消息会一直处于Unacked状态,积压过多会导致RabbitMQ内存爆满。
// autoAck = false,需要手动确认
channel.basicConsume("queue_name", false, (consumerTag, delivery) -> {
try {
String message = new String(delivery.getBody());
System.out.println("处理消息: " + message);
// 业务处理成功,手动确认
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
// multiple = false ↑ 只确认当前这条
} catch (Exception e) {
// 处理失败,拒绝并重新入队
channel.basicNack(delivery.getEnvelope().getDeliveryTag(), false, true);
// multiple requeue ↑
}
}, consumerTag -> {});
消息分发机制
任务分配(Work Queue):多个消费者监听同一个队列
轮询分发:RabbitMQ默认,不考虑消费速度,按顺序平均分配。可能导致忙的忙死,闲的闲死。
公平分发:能者多劳,消费者处理并确认完一条后,才会收到下一条,处理快的能分到更多消息。通过配置 channel.basicQos(1) 开启,必须配合手动应答(autoAck=false)。
消息路由:Exchange四种路由方式
发布确认机制
confirm机制
生产者发送到Exchange的消息,如果顺利抵达会触发Exchange的confirm()回调。
Exchange的消息如果没有匹配到绑定的Queue会回调returnedMessage(),能路由到无回调。
ACK事务机制
application.yml中配置rabbitmq:
publisher-confirms: correlated
publisher-returns: true
template:
mandatory: true
死信队列
死信:无法被消费的消息
消费者处理异常且没有后续处理的消息会进入死信队列,成为死信 → 死信交换机 → 死信队列 → 监控/处理
死信的来源:
1.消息被拒绝(basic.reject/basic.nack),且 requeue = false(代表不重新回到队列)
2.消息超过TTL过期
3.积压的消息超过队列最大长度,先入队的消息会被丢弃变为死信
@Configuration
public class DeadLetterConfig {
// 死信交换机
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange("dlx_exchange");
}
// 死信队列
@Bean
public Queue dlxQueue() {
return new Queue("dlx_queue");
}
@Bean
public Binding dlxBinding() {
return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with("dead_routing_key");
}
// 声明正常队列时绑定死信交换机
@Bean
public Queue normalQueue() {
return QueueBuilder.durable("normal_queue")
.deadLetterExchange("dlx_exchange")
.deadLetterRoutingKey("dead_routing_key")
.ttl(30000) // 消息 TTL
.maxLength(1000) // 队列最大长度
.build();
}
}
延迟队列
延迟队列:到达设置的延迟时间再推给消费者处理
利用 TTL + 死信实现延迟消费的核心思路:消息先在一个没有消费者的队列中过期,过期后自动进入死信队列,再由真正的消费者从死信队列消费,从而实现延迟执行的效果。
如果一条消息设置了TTL或者进入了设置TTL属性的队列,这条消息在TTL内没有被消费会成为死信。如果同时配置了队列的TTL和消息的TTL,使用较小的值。在队列上设置TTL,消息一过期就被队列丢弃,针对每条消息设置TTL,消息是否过期是在投递到消费者前判定的,积压在队列中的消息即使过期还能存活一阵。
@Configuration
public class DelayQueueSpringConfig {
@Bean
public DirectExchange delayExchange() {
return new DirectExchange("order.delay.exchange");
}
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange("order.dlx.exchange");
}
@Bean
public Queue dlxQueue() {
return new Queue("order.dlx.queue");
}
@Bean
public Binding dlxBinding() {
return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with("order.timeout");
}
@Bean
public Queue delayQueue() {
return QueueBuilder.durable("order.delay.queue")
.deadLetterExchange("order.dlx.exchange")
.deadLetterRoutingKey("order.timeout")
.ttl(30 * 60 * 1000) // 30分钟
.build();
}
@Bean
public Binding delayBinding() {
return BindingBuilder.bind(delayQueue()).to(delayExchange()).with("order.delay");
}
}
发送消息
@Service
public class DelayMessageService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendDelayMessage(String orderId) {
rabbitTemplate.convertAndSend("order.delay.exchange", "order.delay", orderId);
}
}
消费消息
@Component
public class TimeoutOrderListener {
@RabbitListener(queues = "order.dlx.queue")
public void processTimeoutOrder(String orderId) {
System.out.println("订单超时检查: " + orderId);
// 检查并处理...
}
}
四、实践问题
消息丢失
消息从生产者到消费者,经过两次网络传输,所以以下三种情况会出现消息丢失
1、生产者向mq服务器(Broker)发送消息,mq服务器宕机或重启会丢失数据
解决方法:confirm机制
2、消息存储在队列中,如果队列没有对消息持久化,mq服务器宕机或重启会丢失数据
解决方法:消息持久化
3、消费者从MQ服务器获取队列中存储的数据消费,消费者程序出错或者宕机,消息没有被成功处理就从队列删除,数据丢失。
解决方法:ACK事务机制
消息积压
队列长度限制
队列溢出,造成内存泄漏,当前进程会被杀死
消息处理失败
消费失败的消息进入死信队列,被存入数据库,人为干预
channel.basicConsume("dlx_queue", false, (consumerTag, delivery) -> {
String deadMsg = new String(delivery.getBody());
Map<String, Object> headers = delivery.getProperties().getHeaders();
// 获取死信原因信息(RabbitMQ 自动添加的头信息)
String reason = (String) headers.get("x-death");
Long originalExpiration = (Long) headers.get("x-first-death-exchange");
System.err.println("收到死信消息: " + deadMsg);
System.err.println("死信原因: " + reason);
// 发送警告/存入数据库待人工处理
sendAlert(deadMsg);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}, consumerTag -> {});
参考:
https://rabbitmq.mr-ping.com/
https://developer.aliyun.com/article/769883
https://developer.aliyun.com/article/769882
https://www.cnblogs.com/antLaddie/p/15958830.html
https://www.cnblogs.com/antLaddie/p/17318213.html

浙公网安备 33010602011771号