消息: 消息体, topic
routing key, binding key ,exchanger
多个消费者可以订阅同一个队列 ,消息会被分摊
交换器的模式有
direct
topic
fanout( 类似广播,不看routing key)
比较下相似的direct,topic, topic可以用通配符*,比如 *.petrol.*
【不重复消费】
db操作的话可以用:唯一索引
另外,用一个第三方介质比如redis 存 ( 全局ID, messageId) , 进行判重
【rabbitmq可靠性】
1.生产者端: confirm机制
还有个事务
-
生产端发送消息到broker后,监听从rabbitmq返回的ack确认信息,以触发confirmCallback回调
- 如果rabiitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。
spring: rabbitmq: publisher-returns: true publisher-confirm-type: correlated
@Component public class ConfirmCallbackService implements RabbitTemplate.ConfirmCallback { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { if (ack) { //接收成功 } else { //接收失败 } } }
rabbitTemplate.setConfirmCallback(confirmCallbackService); //发送消息 rabbitTemplate.convertAndSend("confirmTestExchange1", "", "hello,ConfirmCallback你好");
2. 消费者端:消息确认 basicAck机制 ,死信队列
basicAck:
手动签收消息,给broker回复
@rabbitlistener 修饰的方法中,使用channel.basicAck( tag , true) ,
@Component @RabbitListener(queues = "confirm_test_queue") public class ReceiverMessage { @RabbitHandler public void processHandler(String msg, Channel channel, Message message) throws IOException { long deliveryTag = message.getMessageProperties().getDeliveryTag(); try { System.out.println("消息内容===>" + new String(message.getBody())); //TODO 具体业务逻辑
//手动签收[参数1:消息投递序号,参数2:批量签收] channel.basicAck(deliveryTag, true); } catch (Exception e) { //拒绝签收[参数1:消息投递序号,参数2:批量拒绝,参数3:是否重新加入队列] channel.basicNack(deliveryTag, true, true); } } }
3. rabbitmq自身
【集群】
普通集群: 同一个queue,只有一个实例队列,其他节点只有这个queue的元数据(一些配置信息,可以找到实例)
镜像集群: 同一个queue,每个节点都有这个queue的所有数据 ,缺点是网络和磁盘开销大
持久化
消费端限流小结
- 在rabbit:listener-container 中配置 prefetch属性设置消费端一次拉取多少消息
- 消费端的确认模式一定为手动确认。acknowledge="manual"
【消息积压】
1. 设置消息 TTL
队列或者发消息时 设置,消息超时加入死信队列处理或者丢弃
2. 增加机器,多消费
3. 死信交换器
DLX 是一个普通的交换器,可以在任何队列上设置,
当死信消息出现时,RabbitMQ 自动将这个 消息重新发布到设置的 DLX 上,从而被路由到另一个队列,即 死信队列
消息成为死信的三种情况:
-
队列消息长度到达限制;
-
消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
-
原队列存在消息过期设置,消息到达超时时间未被消费;
【消息有序性】
方案:
1 .同一组操作的消息发送到同一个queue, 且这个queue对应一个消费者,而queue中消息是有序的,这样就可以了
如果用多个队列进行消费,应该考虑引入一些机制
2. 延迟处理并利用数据库或缓存存储中间状态
- 实现原理:为每个流程(比如订单)设置特定的状态记录在数据库中或 Redis 缓存中,收到每条消息后验证上一个状态是否已完成。未完成的消息延迟消费,以便前序状态先完成。