消息中间件安全处理方案

概念

消息如何保证100% 的投递

保障消息的成功发出
保障MQ节点的成功接收
发送端收到MQ节点(Broker) 确定应答
完善的消息进行补偿机制

常见解决方案

1、消息落库,对消息状态进行打标

数据存储在两个数据库(也可以用一个数据库)
BIZ DB 订单数据库
MSG DB 消息数据库
status:0 订单 -》进行中状态
status:1 订单 -》 订单完成状态
status:2 订单 -》 异常状态(尝试数(n)次未接收待订单完成的MQ消息)
Confirm LIstener 消息监听 (接受消息 ,更新消息状态)
MQ Broker
Sender

step1 : 将订单数据和消息数据分别入库 (如果此处失败则进行快速失败操作)
step2 :发送消息改MQ Broker
step3 :生产端接受待MQ Broker 发送过来的消息
step4 :更新消息的状态(更新数据库)
step5 :如果MSG DB中的消息数据状态一直未更新(分布式定时任务根据预先指定的规则抓取 MSG DB中没有确认消息数据) ,执行step6操作
step6 :重新发送消息1 step2、step3重新执行
step7 :重试数次(n次)如果不成功 消息状态改为 2 (由技术人员人工排查这些数据)

消息落库
这种方式在高并发下不适合 ,数据库写入两次

方案二

Upstream sevice 上游服务 生产端
Downstrem service 下游服务 消费端
MQ Broker MQ集群
callback service 回调服务
MSG DB
BIZ DB

step1 : 业务消息入库 发送消息 (订单业务入库 BIZ DB) 发送消息给MQ(step1)
step2 :延迟消息投递检查 (第二条消息)
step3 :消费端接受消息并处理
step4 :Downstrem service 生成新的消息 (发送消息 确认)
step5 :callback service 收到 Downstrem service 发送来的确认消息 (查询消息数据库确认消息是否入库)
step6 :检查 消息数据库 (消息是否入库) 查询到给 订单服务发送消息
RPC Resend command :(没有在消息数据库中查询到消息 ) callback service 发送消息给订单服务 (消息库中没有查询到数据)

1

2、消息的延迟投递,做二次确认,回调检查

消费端的限流

为什么要对消费端限流

假设一个场景,首先,我们 Rabbitmq 服务器积压了有上万条未处理的消息,我们随便打开一个消费者客户端,会出现这样情况: 巨量的消息瞬间全部推送过来,但是我们单个客户端无法同时处理这么多数据!
当数据量特别大的时候,我们对生产端限流肯定是不科学的,因为有时候并发量就是特别大,有时候并发量又特别少,我们无法约束生产端,这是用户的行为。所以我们应该对消费端限流,用于保持消费端的稳定,当消息数量激增的时候很有可能造成资源耗尽,以及影响服务的性能,导致系统的卡顿甚至直接崩溃。

限流的 api 讲解

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

void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;
  • prefetchSize:0,单条消息大小限制,0代表不限制
  • prefetchCount:一次性消费的消息数量。会告诉 RabbitMQ 不要同时给一个消费者推送多于 N 个消息,即一旦有 N 个消息还没有 ack,则该 consumer 将 block 掉,直到有消息 ack。
  • global:true、false 是否将上面设置应用于 channel,简单点说,就是上面限制是 channel 级别的还是 consumer 级别。当我们设置为 false 的时候生效,设置为 true 的时候没有了限流功能,因为 channel 级别尚未实现。
  • 注意:prefetchSize 和 global 这两项,rabbitmq 没有实现,暂且不研究。特别注意一点,prefetchCount 在 no_ask=false 的情况下才生效,即在自动应答的情况下这两个值是不生效的。

.如何对消费端进行限流

第一步,我们既然要使用消费端限流,我们需要关闭自动 ack,将 autoAck 设置为 falsechannel.basicConsume(queueName, false, consumer);
第二步我们来设置具体的限流大小以及数量。channel.basicQos(0, 15, false);
第三步在消费者的 handleDelivery 消费方法中手动 ack,并且设置批量处理 ack 回应为 truechannel.basicAck(envelope.getDeliveryTag(), true);

仔细查看一下 Consumer 的回调方法:

        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            ......
            consumerChannel1.basicAck(envelope.getDeliveryTag(), false);
        }

当我们需要确认一条消息已经被消费时,我们调用的 basicAck 方法的第一个参数是 Delivery Tag。
Delivery Tag 用来标识信道中投递的消息。RabbitMQ 推送消息给 Consumer 时,会附带一个 Delivery Tag,以便 Consumer 可以在消息确认时告诉 RabbitMQ 到底是哪条消息被确认了。
RabbitMQ 保证在每个信道中,每条消息的 Delivery Tag 从 1 开始递增。

RabbitMQ 消息消费并没有超时机制,也就是说,程序不重启,消息就永远是 Unacked 状态。处理运维事件时不要忘了这些 Unacked 状态的消息。当程序关闭时(实际只要 Consumer 关闭就行),这3条消息会恢复为 Ready 状态。

消息的ACK与重回队列

消费端的手工ACK 与 NACK

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

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

消费端的重回队列

  • 消费端的重回队列是为了对没有处理成功的消息,把消息重新递给 broker
  • 但是在我们的实际生产,一般都会关闭重回队列,也就是设置为false
if((Integer)properties.getHeaders().get("num") == 0) {
	//requeue true 是否重回队列,重回队列后会添加到队列的末端 ,重新发送(不建议使用)
	channel.basicNack(envelope.getDeliveryTag(), false, true);
} else {
	//false 表示不批量确认
	channel.basicAck(envelope.getDeliveryTag(), false);
}

TTL消息

  • TTL 是 Time To Live 的缩写,也就是生存时间
  • RabbitMQ支持消息的过期时间,在消息发送时可指定过期时间
  • RabbitMQ支持队列的过期时间,从消息入队列开始计算,只要超过队列的超时时间配置,那么消息会自动的消除

死信队列

利用DLX(死信队列 dead-letter-exchange)当消息在一个队列中变成死信(dead message)之后,他被重新publish到另外一个Exchage,这个 Exchage就是DLX

消息变成死信有一下几种情况

  • 消息被拒绝(basic.reject/basic.ncak) 并且request =false
  • 消息TTL过期
  • 队列到达最大长度
    死信队列在开发中很重要 接收无接受的的队列消息 进行补偿处理

参考

Rabbitmq 消费端限流
RabbitMQ:消息发送确认 与 消息接收确认(ACK)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值