RabbitMQ交换机、高级特性和死信队列

本文详细介绍了RabbitMQ的交换机类型,包括Direct、Topic和Fanout,以及交换机的属性。讨论了RabbitMQ的高级特性,如100%投递成功、幂等性和Confirm确认消息机制。同时,重点讲解了死信队列(DLX)的设置和应用,阐述了消息变为死信的原因以及如何利用死信队列进行消息处理。
摘要由CSDN通过智能技术生成

Exchange交换机

接受消息,并根据路由键转发消息到所有绑定的队列

交换机

交换机属性

Name 交换机名称

Type 交换机类型 direct 、topic fanout,headers

Durability 是否需要持久化,true为持久化

Auto Delete 当最后一个绑定到Exchange上的队列删除后,自动删除该Exchange

Internal 当前Exchange是否用于 RabbitMQ内部使用,默认为false

Arguments 扩展参数,用于扩展AMQP协议自制定制化使用

交换机类型

Direct Exchange

所有发送到Direct Exchange 的消息被转发到RouteKey中指定的Queue

注意:Direct模式可以使用RabbitMQ自带的Exchange:default Exchange ,所有不需要将Exchange进行任何绑定(binding)操作,消息传递时,RouteKey必须完全匹配才回被队列接收,否则消息会被抛弃

img

Topic Exchange

  • 所有发送到Topic Exchange的消息被转发到所有管线RouteKey中指定Topic的Queue上

  • Exchange将RouteKey和某Topic进行模糊匹配,此时队列需要绑定一个Topic

    注意:可以使用通配符进行模糊匹配

    符号 “#” 匹配一个或多个词

    符号 “" 匹配不多不少一个词 例如:“log.#” 能够匹配到 “log.info.oa” "log.” 只会匹配到 “log.error”

Topic Exchange结构图

Fanout Exchange

  • 不处理路由键,只需要简单的将队里绑定到交换机上
  • 发送到交换机的消息都会被转发到与该交换机绑定的所有队列上
  • Fanout交换机转发消息是最快的

Fanout Exchange结构图

Bingding —— 绑定

  • Exchange和Exchange、Queue之间的连接关系
  • Bingding可以包含RoutingKey或者参数

Queue——消息队列

  • 消息队列,实际存储消息数据
  • Durability:是否持久化,Durable:是 ,Transient:否
  • Auto delete:如选yes,代表当最后一个监听被移除之后,该Queue会自动被删除。

Message——消息

  • 服务器与应用程序之间传送的数据
  • 本质上就是一段数据,由Properties和Payload(Body)组成
  • 常用属性:delivery mode、headers(自定义属性)
其他属性

content_type 类型

content_encoding 编码

priority 优先级

correlation_id 消息唯一id

reply_to 重回队列用,指定消息失败后回到哪个队列

expiration 消息过期时间

message_id

timestamp 时间戳

type、user_id、app_id、cluster_id

Virtual Host虚拟主机

  • 虚拟地址,用于进行逻辑隔离,最上层的消息路由
  • 一个Virtual Host里面可以有若干个Exchange和Queue
  • 同一个Virtual Host里面不能有相同名称的Exchange或Queue

参考

https://juejin.im/post/5d455cc95188255d6461b1a3

RabbitMQ 高级特性

RabbitMQ主要有以下高级特性

  • 可靠性投递(100%投递成功)
  • 幂等性(解决重复消费)
  • confirm机制、return机制
  • 自定义消费者
  • 消息的Ack和重回队列
  • 消息限流
  • TTL消息(生存周期)
  • 死信队列

100%投递成功(可靠性投递)

生产端的可靠性投递

  • 保证消息成功发送
  • 保障MQ节点成功接收
  • 发送端收到MQ的Ack
  • 完善的消息进行补偿机制(失败情况的处理)

解决方案

  • 方法一: 消息入库、对消息状态标记
  • 方法二: 消息的延迟投递、二次确认、回调检查
方法一 消息入库、对消息状态标记

1583029777947

发送消息前,先存到数据库,来一条消息,状态标记为0

接着发送消息 到 MQ Broker,如果MQ Broker收到成功,就会返回一个 Broker confirm

接着在数据库里标记这条消息状态为 1

以上是发送成功的情况

对于中间有可能发送失败的情况:

  • 消息发送到MQ Broker 一直没有confirm消息发送失败,此时数据库消息状态会一直为0

需要有一个分布式定时任务,扫描状态为0的消息,重新进行发送

重新发送也有可能失败,设置一个发送失败的限制,超出后标记为2(人工处理,排除故障)

方法二: 消息的延迟投递、二次确认、回调检查

1583030463933

先写数据库到 BIZ(业务数据库)

接着发送两条消息: 真正要发送的内容,和 检查消息(是延迟检查真正要发送的内容是否成功)

消费端收到消息后,会返回一个 确认消息

幂等性

对某个操作多次操作,无论操作多少次,结果都是相同的

(并发情况下,每次都相同)

幂等性保障

在海量订单产生的业务高峰,如何避免消息的重复消费?

消费端实现幂等性,就意味着消息永远不会消费多次

业界主流幂等性操作:

  • 唯一ID + 指纹码 机制

唯一ID + 指纹码 机制,利用数据库主键去重

select count(1) from t_order where id= 唯一ID + 指纹码

好处是实现简单,、

坏处:高并发下有数据库写入的性能瓶颈

可以跟进ID进行分库分表进行路由算法

  • 利用Redis的原子性去实现

需要考虑的问题:

是否需要进行数据落库,如果落库的话,关键解决的问题是数据库和缓存如何做到原子性

如果不进行落库,那么都存储到缓存中,如何设置定时同步的策略?

Confirm 确认消息

确认机制流程图

1583058760593

step1: 开启确认模式 channel.confirmSelect()

step2: 在channel添加监听 addConfirmListener

Return消息机制

return listener 用于处理不可路由的消息

路由的队列没有,或者出现故障,消息无法到达队列,专门处理这种

关键配置

Mandatory 如果为true,则监听器接收到路由不可达的消息,然后进行后续处理;如果为false则broker自动删除这些消息。

1583060728341

消费端自定义监听

在之前的代码中我们一般就是在代码中写while循环,进行consumer.nexDelivery方法进行获取下一条消息,然后进行消费处理。

但是在工作中,我们一般使用自定义的Consumer更加的方便,解耦性更加的强。

1583108938424

继承DefaultConsumer,重写 handleDelivery

消费端限流

为什么要进行消费者端限流?

假设有个场景,RabbitMQ服务器上堆积上万条未处理的消息,我们随便打开一个消费者客户端会出现下面情况:巨量的消息同时推送过来,但是我们单个消费者客户端无法同时处理这么多数据,服务器可能卡死

什么是消费端限流

RabbitMQ提供了一种qos(服务质量保证)功能,即在非自动确认消息的情况下,如果一定数量的消息(通过基于consumer或者channel设置qos值)未被确认前,不消费新的消息

// 单条消息的大小限制,一般设为0或不设置,不限制大小
int prefecthSize = 0;
// 告诉RabbitMQ不要同时给消费端推送n条消息,一旦有n个消息还没ack,则该consumer将block掉,直到有ack;注意在自动应答下不生效
int prefecthCount = 1;
// 表示是否应用于channel上,即是channel级别还是consumer级别
boolean global = false;
channel.basicQos(prefecthSize, prefecthCount, global);

对于prefetchSize 和 global

在no_ask=false的情况下生效,即在自动应答的情况下这两个值是不生效的

消费端ACK与重回队列

消费端进行消费的时候,如果由于业务异常导致失败了,返回 NACK 达到最大重试次数,此时我们可以进行日志的记录,然后手动 ACK 回去,最后对这个记录进行补偿。

或者由于服务器宕机等严重问题,导致 ACK 和 NACK 都没有,那我们就需要手工进行 ACK 保障消费端消费成功,再通过补偿机制补偿。

消费端的重回队列 消费端的重回队列是为了对没有处理成功的消息,把消息重新递给 broker。但是在我们的实际生产,一般都会关闭重回队列

TTL队列/消息

TTL消息 TTL 是 time to live 的缩写,也就是生存时间。

rabbitMQ 支持在消息发送的时候指定过期时间。

rabbitMQ 支持队列的过期时间,从消息入队列开始计算,只要超过配置的队列超时时间,那么消息会自动删除。

参考

https://juejin.im/post/5c540e80518825622f12cd32

RabbitMQ死信队列

死信队列: DLX,dead-letter-exchange

利用 dlx,当消息在一个队列中变成死信 (dead message) 之后,它能被重新 publish 到另一个 exchange,这个 exchange 就是 dlx

消息变成死信的原因有:

1.消息被拒绝 (basic.reject / basic.nack) 并且 reQueue=false

2.消息 TTL 过期

3.队列达到最大长度了

dlx 也是一个正常的 exchange,和一般的 exchange 没什么区别,它能在任何队列上被指定,实际上就是设置一个属性。

当这个队列中有死信时,rabbitMQ 就会自动的将这个消息重新发布到设置的 exchange 上去,进而被路由到另一个队列。

可以监听这个队列中消息做相应的处理,这个特性可以弥补 rabbitMQ3.0 以前支持的 immediate 参数功能。

死信队列设置

首先要设置死信队列的 exchange 和 queue,然后进行绑定:

  exchange: dlx.exchange

  queue: dlx.queue

  routingkey:  #

然后进行正常声明交换机、队列、绑定,只不过需要在队列加上一个参数即可: argument.put("x-dead-letter-exchange", "dlx.exchange");

		Map<String,Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange","dlx.exchange");
        //这个arguments属性,要设置到声明队列上
        channel.exchangeDeclare(exchangeName,"topic",true,false,null);
        channel.queueDeclare(queueName,true,false,false,arguments);
        channel.queueBind(queueName,exchangeName,routingKey);

注意,arguments一定是传入队列声明里,不能在交换机声明里

这样消息在过期、reQueue、队列在达到最大长度时,消息就可以直接路由到死信队列

参考

链接:https://juejin.im/post/5c540fdde51d457ff9779e02

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值