1、RabbitMQ如何保证稳定性
就是保证消息不会丢失
消息持久化;ACK确认机制;设置集群镜像模式;消息补偿机制,通过将 channel 设置为 confirm(确认)模式。
第一种:消息持久化
RabbitMQ的消息是默认放在内存的,如果不特别声明消息持久到磁盘,当节点关掉或者crash(碰撞)掉,消息就会丢失。
那么要把数据持久到磁盘就要满足三个条件,缺一不可
- Exchange(互换) 设置持久化
- Queue(队列)() 设置持久化
- Message(信息)持久化发送:发送消息设置发送模式deliveryMode=2,代表持久化消息
第二种:ACK确认机制
消费端消费完成要通知服务端,服务端才把消息从内存删除,一个消费者出了问题,没有同步消息给服务端,还有其他的消费端去消费,保证了消息不丢的case
第三种:设置集群镜像模式
普通模式:必须消息是持久的,默认是集群模式,某个节点挂了,消息不可用了,业务瘫痪了,此时只能等待节点恢复重启使用;
镜像模式:把需要的队列做成镜像队列,存在于多个节点,属于RabbitMQ的HA方案
2、kafka为什么设计为拉模式
优点:
- 一些消息系统比如Scribe和Apache Flume采用了push模式,将消息推送到下游的consumer。这样做有好处也有坏处:由broker决定消息推送的速率,对于不同消费速率的consumer就不太好处理了。消息系统都致力于让consumer以最大的速率最快速的消费消息,但不幸的是,push模式下,当broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了。最终Kafka还是选取了传统的pull模式。
- Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉取数据。Push模式必须在不知道下游consumer消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免consumer崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull模式下,consumer就可以根据自己的消费能力去决定这些策略。
缺点:
- Pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到新消息到t达。为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发送)。
3、如果让你设计一个消息队列,该如何进行架构设计?
简单理解就是一个消息转发器,把一次RPC做成两次RPC,发送者把消息投递到broker,broker再将消息转发一手到接收端。
总结起来就是两次RPC加一次转储,如果要做消费确认,则是三次RPC。
为了实现上述消息队列的基础功能:
- 消息的传输
- 存储
- 消费
就需要涉及到如下三个方面的设计:
- 通信协议
- 存储选择
- 消费关系维护
通讯协议
消息Message:既是信息的载体,消息发送者需要知道如何构造消息,消息接收者需要知道如何解析消息,它们需要按照一种统一的格式描述消息,这种统一的格式称之为消息协议。
传统的通信协议标准有XMPP和AMQP协议等,现在更多的消息队列从性能的角度出发使用自己设计实现的通信协议。
Kafka的Producer、Broker和Consumer之间采用的是一套自行设计的基于TCP层的协议。Kafka的这套协议完全是为了Kafka自身的业务需求而定制的
存储选型
对于分布式系统,存储的选择有以下几种
- 内存
- 本地文件系统
- 分布式文件系统
- nosql
- DB
从速度上内存显然是最快的,对于允许消息丢失,消息堆积能力要求不高的场景(例如日志),内存会是比较好的选择。
DB则是最简单的实现可靠存储的方案,很适合用在可靠性要求很高,最终一致性的场景(例如交易消息),对于不需要100%保证数据完整性的场景,要求性能和消息堆积的场景,hbase也是一个很好的选择。
理论上,从速度来看,文件系统>分布式KV(持久化)>分布式文件系统>数据库,而可靠性却截然相反。
还是要从支持的业务场景出发作出最合理的选择,如果你们的消息队列是用来支持支付/交易等对可靠性要求非常高,但对性能和量的要求没有这么高,而且没有时间精力专门做文件存储系统的研究,DB是最好的选择。
对于不需要100%保证数据完整性的场景,要求性能和消息堆积的场景,hbase也是一个很好的选择,典型的比如 kafka的消息落地可以使用hadoop。
消费关系处理
抛开现象看本质,无外乎是单播与广播的区别。
为了实现广播功能,我们必须要维护消费关系,通常消息队列本身不维护消费订阅关系,可以利用zookeeper等成熟的系统维护消费关系,在消费关系发生变化时下发通知。
消息队列需要支持高级特性
- 消息的顺序
- 投递可靠性保证
- 消息持久化
- 支持不同消息模型
- 多实例集群功能
- 事务特性等
4、如何保证消息的顺序性?
1、rabbitmq ,一个 queue,多个 consumer,这就会乱,保证一个 queue一个 consumer就好了
2、kafka,一个 topic,一个 partition,一个 consumer,内部单线程消费,因为partition的消息是保证顺序的。保证全局顺序会牺牲吞吐量。
5、如何保证消息不被重复消费?(幂等)
1、消费完把自己消费过的消息offset提交一下,告知broker已经消费过该消息。
2、上面可能导致宕机情况,也就是实际消费过了但offset提交不成功,这时候需要保证消费端的幂等
幂等可以从几个方面去保证:
1、新增时需要查询是否存在,如已经存在就更新就可以了
2、使用数据库的唯一索引来限制
3、把id储存到redis,每次去查询是否已经存在,存在说明已经消费。
6、如何保证消息的可靠性传输?(防丢失)
RabbitMQ
1、生产端:开启confirm机制
2、broker:开启持久化
3、消费端:关闭 RabbitMQ 的自动ack,确保处理完再ack
Kafka
1、生产端: 设置acks=all,写入所有 replica 之后,才能认为是写成功了, retries=MAX,写入失败就不断重试
2、 broker:每个 partition 必须有至少 2 个副本,要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系
3、消费端:关闭自动提交 offset,确保处理完再提交。
7、如何保证消息队列的高可用?
RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式。
Kafka :天然的分布式消息队列,提供了 HA 机制,由多个 broker 组成, topic 可以划分为多个 partition,每个 partition 可以存在于不同的 broker 上。
8、为什么选择RabbitMQ、而不选kafka?
在实际生产应用中,通常会使用kafka作为消息传输的数据管道,rabbitmq作为交易数据作为数据传输管道,主要的取舍因素则是是否存在丢数据的可能;
rabbitmq在金融场景中经常使用,具有较高的严谨性,数据丢失的可能性更小,同事具备更高的实时性;而kafka优势主要体现在吞吐量上,虽然可以通过策略实现数据不丢失,但从严谨性角度来讲,大不如rabbitmq;而且由于kafka保证每条消息最少送达一次,有较小的概率会出现数据重复发送的情况;
RabbitMQ该怎么用(精细严谨,顺序,实时,可靠)
- RabbitMQ的消息应当尽可能的小,并且只用来处理实时且要高可靠性的消息。
- 消费者和生产者的能力尽量对等,否则消息堆积会严重影响RabbitMQ的性能。
- 集群部署,使用热备,保证消息的可靠性。
Kafka该怎么用(粗糙,但可以存的多,吞吐量高)
- 应当有一个非常好的运维监控系统,不单单要监控Kafka本身,还要监控Zookeeper。
- 对消息顺序不依赖,且不是那么实时的系统。
- 对消息丢失并不那么敏感的系统。
9、RabbitMQ的消息事务,可靠消息最终一致性怎么实现,什么场景?
场景:
BD系统下个c端业务订单,把订单信息通过消息队列发给SB系统下履约单(用于采购)。两边的订单要么都创建成功,要么都创建失败
实现:
以保证消息可靠性为基调去做到消息事务。
image.png
当主动方接收到消息存储结果后,开始执行本地的业务操作,根据本地事务提交的结果,调用消息服务的接口。这里分为两种状态:
- 如果本地事务执行成功,就调用消息服务确认消息状态,更新为 待发送。
- 如果本地事务执行失败,就调用消息服务删除消息(一般是逻辑删除,更新消息状态为 已回滚)
保证消息发送和本地事务同时成功同时失败。这里还是有两种情况:
- 如果更新消息状态失败,则应当抛出异常回滚事务,不投递消息到MQ中。
- 如果投递MQ失败,(需要捕获异常),需要主动抛出异常触发本地事务回滚。
1.2要同时成功同时失败
如:消费失败、未收到消息投递(传说中的丢消息)等,该如何处理呢?
重复投递,保证幂等性
10、什么情况下消息会进入死信队列:
1、消息被拒绝并且不再重新投递 requeue=false
2、消息超期
3、队列超载
网友评论