RabbitMQ(幂等性/顺序性/可靠性)
2021-02-06 15:46:51 61 举报
AI智能生成
登录查看完整内容
RabbitMQ
作者其他创作
大纲/内容
RabbitMQ
RabbitMQ是一款开源的,使用Erlang语言编写的,基于AMQP协议的消息中间件
基本概念
特点
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、 安全
AMQP协议对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次
常用组件
Connection
连接,应用程序与Server的网络连接,TCP连接
Channel
信道,消息读写等操作在信道中进行
Message
消息,应用程序和服务器之间传送的数据
Virtual Host
虚拟主机,用于逻辑隔离。一个虚拟主机里面可以有若干个Exchange和Queue
Exchange
交换器,接收消息,按照路由规则将消息路由到一个或者多个队列
Binding
绑定,交换器和消息队列之间的虚拟连接,绑定中可以包含一个或者多个RoutingKey
RoutingKey
路由键,生产者将消息发送给交换器时会发一个RoutingKey,用来指定路由规则
Queue
消息队列,用来保存消息,供消费者消费
Broker
标识消息队列服务器实体
交换器类型
Direct Exchange
完全匹配,消息中的路由键(routing key)和Binding中的binding key一致
Topic Exchange
模糊匹配,两个通配符:\"#\"和\"*\",#匹配0个或多个单词,*只匹配一个单词
Fanout Exchange
广播模式,不处理路由键,把所有发送到交换器的消息路由到所有绑定的队列中
Headers Exchange
忽略路由规则,根据消息中的headers属性来匹配,性能较低(不常用)
MQ使用场景及优缺点
优点
解耦(最终一致性)
异步(提升效率)
削峰(量力而行)
限流
RabbitMQ提供了一种qos(服务质量保证)功能。即在非自动确认消息的前提下,如果一定数目的消息未被确认前,不进行消费新的消息
缺点
系统的可用性降低
引入外部依赖越多,系统越容易挂掉MQ挂了,也会导致整个系统不可用
系统的复杂性提高
如何保证消息没有重复消费?
幂等性
如何保证消息传递的顺序?
顺序性
如何保证消息不丢失?
可靠性
数据一致性的问题
A系统发送完消息直接返回成功,但是BCD系统之中若有系统写库失败,则会产生数据不一致的问题
使用全局唯一ID+指纹码(全局唯一ID:雪花算法生成的业务表的主键。指纹码:时间戳、UUID、订单号)
并发量不高的情况下可以在数据库维护一张消费记录表,并发量很高可将全局ID写入redis,利用其原子操作setnx
接收到消息后执行setnx,如果执行成功则表示没有处理过,可以消费,相反如果执行失败就表示该消息已经被消费了
在 MQ 里面创建多个queue,使用hash算法将需要排序的数据有顺序的放入同一个queue,每个queue对应一个consumer
或者就一个queue,对应一个consumer,这个consumer内部用内存队列排队,然后分发给不同的worker来处理
消息丢失场景
生产者发送消息到MQ
网络原因
代码/配置
MQ中存储的消息丢失
消息未完全持久化
消费者从MQ拉取消息
消费端接收到相关消息之后,消费端还没来得及处理消息,消费端机器就宕机了
如何避免消息丢失
生产者丢消息
事务机制(基于AMQP协议)
吞吐量下降(同步),不推荐
confirm机制(生产者确认机制)
异步回调,效率高
MQ丢消息
开启RabbitMQ持久化
必须同时设置
创建queue时设置持久化
发送消息时设置持久化
使用镜像集群模式保证高可用
rabbitmq有很好的管理控制台,在后台新增一个镜像集群模式的策略
指定同步节点的时候要求数据同步到所有节点(性能受极大影响)镜像策略:指定最多同步N台机器、只同步到符合指定名称的机器
再次创建queue的时候应用这个策略就会自动将数据同步到其他的节点上
消费者丢消息
关闭消费者的自动ack机制,采用手动ack形式
消费者处理完消息后手动ack通知MQ删除消息
常见问题
死信队列(DLX)
消息变成死信的原因
消息被拒绝(basic.reject/ basic.nack)并且不再重新投递 requeue=false
TTL(time-to-live) 消息超时未消费
队列达到最大长度
死信队列的设置
提前设置好死信队列的 exchange 和 queue,然后进行绑定
在普通队列上加一个参数: argument.put(\"x-dead-letter-exchange\
这样消息在过期或者队列达到最大长度时,消息就会直接路由到死信队列
延时队列怎么实现?
不推荐
使用定时任务
非常浪费服务器性能,不建议
使用Java自带的delayQueue
这种实现方式是数据保存在内存中,可能面临数据丢失的情况
无法支持分布式系统,不能做集群化处理且不易维护
推荐
使用rabbitmq的消息过期时间(TTL)和死信队列(DLX)来模拟出延时队列
还可以用RabbitMQ的插件 rabbitmq-delayed-message-exchange 插件来实现延时队列达到可投递时间时并将其通过 x-delayed-type 类型标记的交换机类型投递至目标队列
重复排队怎么解决?
利用redis的原子递增来实现,使用用户名作为key,每次递增1,返回值大于1则抛异常
0 条评论
回复 删除
下一页