1、mq如何选型
MQ | 描述 |
---|---|
RabbitMQ | erlang开发,对消息堆积的支持并不好,当大量消息积压的时候,会导致 RabbitMQ 的性能急剧下降。每秒钟可以处理几万到十几万条消息。 |
RocketMQ | java开发,面向互联网集群化功能丰富,对在线业务的响应时延做了很多的优化,大多数情况下可以做到毫秒级的响应,每秒钟大概能处理几十万条消息。 |
Kafka | Scala开发,面向日志功能丰富,性能最高。当你的业务场景中,每秒钟消息数量没有那么多的时候,Kafka 的时延反而会比较高。所以,Kafka 不太适合在线业务场景。 |
ActiveMQ | java开发,简单,稳定,性能不如前面三个。小型系统用也ok,但是不推荐。推荐用互联网主流的。 |
2、为啥要使用?
消息队列作为高并发系统的核心组件之一,能够帮助业务系统解构提升开发效率和系统稳定性。主要具有以下优势:
- 削峰填谷:主要解决瞬时写压力大于应用服务能力导致消息丢失、系统奔溃等问题
- 系统解耦:解决不同重要程度、不同能力级别系统之间依赖导致一死全死
- 提升性能:当存在一对多调用时,可以发一条消息给消息系统,让消息系统通知相关系统
- 蓄流压测:线上有些链路不好压测,可以通过堆积一定量消息再放开来压测
3、RocketMQ由哪些角色组成
- Nameserver 无状态,动态列表;这也是和zookeeper的重要区别之一。zookeeper是有状态的。
- Producer 消息生产者,负责发消息到Broker。
- Broker 就是MQ本身,负责收发消息、持久化消息等。
- Consumer 消息消费者,负责从Broker上拉取消息进行消费,消费完进行ack。
4、RocketMQ Broker中的消息被消费后会立即删除吗?
不会,每条消息都会持久化到CommitLog中,每个Consumer连接到Broker后会维持消费进度信息,当有消息消费后只是当前Consumer的消费进度(CommitLog的offset)更新了。
5、那么消息会堆积吗?什么时候清理过期消息?
4.6版本默认48小时后会删除不再使用的CommitLog文件
- 检查这个文件最后访问时间
- 判断是否大于过期时间
- 指定时间删除,默认凌晨4点
6、RocketMQ消费模式有几种?
消费模型由Consumer决定,消费维度为Topic。
1、集群消费
- 一条消息只会被同Group中的一个Consumer消费
- 多个Group同时消费一个Topic时,每个Group都会有一个Consumer消费到数据
2、广播消费
消息将对一 个Consumer Group 下的各个 Consumer 实例都消费一遍。即即使这些 Consumer 属于同一个Consumer Group ,消息也会被 Consumer Group 中的每个 Consumer 都消费一次。
7、消费消息是push还是pull?
RocketMQ没有真正意义的push,都是pull,虽然有push类,但实际底层实现采用的是长轮询机制,即拉取方式
broker端属性 longPollingEnable 标记是否开启长轮询。默认开启
8、broker如何处理拉取请求的?
Consumer首次请求Broker
Broker中是否有符合条件的消息
有 ->
响应Consumer
等待下次Consumer的请求
没有->
- DefaultMessageStore#ReputMessageService#run方法
- PullRequestHoldService来Hold连接,每个5s执行一次检查pullRequestTable有没有消息,有的话立即推送
- 每隔1ms检查commitLog中是否有新消息,有的话写入到pullRequestTable
- 当有新消息的时候返回请求挂起consumer的请求,即不断开连接,也不返回数据
- 使用consumer的offset,
9、RocketMQ如何做负载均衡?
通过Topic在多Broker中分布式存储实现。
9.1 producer端
发送端指定message queue发送消息到相应的broker,来达到写入时的负载均衡
- 提升写入吞吐量,当多个producer同时向一个broker写入数据的时候,性能会下降
- 消息分布在多broker中,为负载消费做准备
默认策略是随机选择:
- producer维护一个index
- 每次取节点会自增
- index向所有broker个数取余
- 自带容错策略
其他实现:
- SelectMessageQueueByHash:hash的是传入的args
- SelectMessageQueueByRandom
- SelectMessageQueueByMachineRoom 没有实现
9.2 consumer端
采用的是平均分配算法来进行负载均衡。
其他负载均衡算法
- 平均分配策略(默认)(AllocateMessageQueueAveragely)
- 环形分配策略(AllocateMessageQueueAveragelyByCircle)
- 手动配置分配策略(AllocateMessageQueueByConfig)
- 机房分配策略(AllocateMessageQueueByMachineRoom)
- 一致性哈希分配策略(AllocateMessageQueueConsistentHash)
- 靠近机房策略(AllocateMachineRoomNearby)
10、消息重复消费
影响消息正常发送和消费的重要原因是网络的不确定性。
引起重复消费的原因
ACK:正常情况下在consumer真正消费完消息后应该发送ack,通知broker该消息已正常消费,从queue中剔除;当ack因为网络原因无法发送到broker,broker会认为词条消息没有被消费,此后会开启消息重投机制把消息再次投递到consumer
消费模式:在CLUSTERING模式下,消息在broker中会保证相同group的consumer消费一次,但是针对不同group的consumer会推送多次
解决方案
- 数据库表:处理消息前,使用消息主键在表中带有约束的字段中insert
- Map:单机时可以使用map ConcurrentHashMap-> putIfAbsent guava cache
- Redis:分布式锁搞起来。
11、RocketMQ如何保证消息不丢失
11.1、Producer端如何保证消息不丢失
- 采取send()同步发消息,发送结果是同步感知的。
- 发送失败后可以重试,设置重试次数。默认3次。
- 集群部署,比如发送失败了的原因可能是当前Broker宕机了,重试的时候会发送到其他Broker上。
11.2 Broker端如何保证消息不丢失
- 修改刷盘策略为同步刷盘。默认情况下是异步刷盘的。
- 集群部署,主从模式,高可用。
11.3 Consumer端如何保证消息不丢失
1.完全消费正常后在进行手动ack确认。
12 RocketMQ在分布式事务支持这块机制的底层原理?
12.1概念
- Half(Prepare) Message——半消息(预处理消息):半消息是一种特殊的消息类型,该状态的消息暂时不能被Consumer消费。当一条事务消息被成功投递到Broker上,但是Broker并没有接收到Producer发出的二次确认时,该事务消息就处于"暂时不可被消费"状态,该状态的事务消息被称为半消息。
- Message Status Check——消息状态回查:由于网络抖动、Producer重启等原因,可能导致Producer向Broker发送的二次确认消息没有成功送达。如果Broker检测到某条事务消息长时间处于半消息状态,则会主动向Producer端发起回查操作,查询该事务消息在Producer端的事务状态(Commit 或 Rollback)。可以看出,Message Status Check主要用来解决分布式事务中的超时问题。
12.2 执行流程
下面对具体流程进行分析:
- Step1:Producer向Broker端发送Half Message;
- Step2:Broker ACK,Half Message发送成功;
- Step3:Producer执行本地事务;
- Step4:本地事务完毕,根据事务的状态,Producer向Broker发送二次确认消息,确认该Half Message的Commit或者Rollback状态。Broker收到二次确认消息后,对于Commit状态,则直接发送到Consumer端执行消费逻辑,而对于Rollback则直接标记为失败,一段时间后清除,并不会发给Consumer。正常情况下,到此分布式事务已经完成,剩下要处理的就是超时问题,即一段时间后Broker仍没有收到Producer的二次确认消息;
- Step5:针对超时状态,Broker主动向Producer发起消息回查;
- Step6:Producer处理回查消息,返回对应的本地事务的执行结果;
- Step7:Broker针对回查消息的结果,执行Commit或Rollback操作,同Step4。
13 Broker把自己的信息注册到哪个NameServer上?
这么问明显在坑你,因为Broker会向所有的NameServer上注册自己的信息,而不是某一个,是每一个,全部!
14 Rebalance
14.1 定义
- 将一个Topic下的多个队列(或称之为分区),在同一个消费者组(consumer group)下的多个消费者实例(consumer instance)之间进行重新分配。
- Rebalance机制本意是为了提升消息的并行处理能力。例如,一个Topic下5个队列,在只有1个消费者的情况下,那么这个消费者将负责处理这5个队列的消息。如果此时我们增加一个消费者,那么可以给其中一个消费者分配2个队列,给另一个分配3个队列,从而提升消息的并行处理能力
14.2 Rebalance限制:
由于一个队列最多分配给一个消费者,因此当某个消费者组下的消费者实例数量大于队列的数量时,多余的消费者实例将分配不到任何队列。
14.3 Rebalance危害:
除了以上限制,更加严重的是,在发生Rebalance时,存在着一些危害,如下所述:
- 消费暂停:考虑在只有Consumer 1的情况下,其负责消费所有5个队列;在新增Consumer 2,触发Rebalance时,需要分配2个队列给其消费。那么Consumer1就需要停止这2个队列的消费,等到这两个队列分配给Consumer 2后,这两个队列才能继续被消费。
- 重复消费:Consumer 2 在消费分配给自己的2个队列时,必须接着从Consumer1之前已经消费到的offset继续开始消费。然而默认情况下,offset是异步提交的,如consumer 1当前消费到offset为10,但是异步提交给broker的offset为8;那么如果consumer2从8的offset开始消费,那么就会有2条消息重复。也就是说,Consumer 2 并不会等待Consumer1提交完offset后,再进行Rebalance,因此提交间隔越长,可能造成的重复消费就越多。
- 消费突刺:由于rebalance可能导致重复消费,如果需要重复消费的消息过多;或者因为rebalance暂停时间过长,导致积压了部分消息。那么都有可能导致在rebalance结束之后瞬间可能需要消费很多消息。
14.4 Broker端Rebalance协调机制
A、队列信息变化
- broker宕机
- broker升级等运维操作
- 队列扩容/缩容
B、消费者组信息变化
- 日常发布过程中的停止与启动
- 消费者异常宕机
- 网络异常导致消费者与Broker断开连接
- 主动进行消费者数量扩容/缩容
- Topic订阅信息发生变化