写点什么

RocketMQ 面试常问问题及答案,多益网络 java 面试题

  • 2021 年 11 月 27 日
  • 本文字数:5939 字

    阅读完需:约 19 分钟



1. 灵活可扩展性?


RocketMQ 天然支持集群,其核心四组件(Name Server、Broker、Producer、Consumer)每一个都可以在没有单点故障的情况下进行水平扩展。?


2. 海量消息堆积能力?


RocketMQ 采用零拷贝原理实现超大的消息的堆积能力,据说单机已可以支持亿级消息堆积,而且在堆积了这么多消息后依然保持写入低延迟。?


3. 支持顺序消息?


可以保证消息消费者按照消息发送的顺序对消息进行消费。顺序消息分为全局有序和局部有序,一般推荐使用局部有序,即生产者通过将某一类消息按顺序发送至同一个队列来实现。?


4. 多种消息过滤方式?


消息过滤分为在服务器端过滤和在消费端过滤。服务器端过滤时可以按照消息消费者的要求做过滤,优点是减少不必要消息传输,缺点是增加了消息服务器的负担,实现相对复杂。消费端过滤则完全由具体应用自定义实现,这种方式更加灵活,缺点是很多无用的消息会传输给消息消费者。?


5. 支持事务消息?


RocketMQ 除了支持普通消息,顺序消息之外还支持事务消息,这个特性对于分布式事务来说提供了又一种解决思路。?


6. 回溯消费?


回溯消费是指消费者已经消费成功的消息,由于业务上需求需要重新消费,RocketMQ 支持按照时间回溯消费,时间维度精确到毫秒,可以向前回溯,也可以向后回溯。


3. RocketMQ、RabbitMQ、Kafaka 的区别?


kafka:


1、开发语言: Scala 开发


2、性能、吞吐量: 吞吐量所有 MQ 里最优秀,QPS 十万级、性能毫秒级、支持集群部署


3、功能: 功能单一


4、缺点: 丢数据, 因为数据先写入磁盘缓冲区,未直接落盘。机器故障会造成数据丢失


5、应用场景: 适当丢失数据没有关系、吞吐量要求高、不需要太多的高级功能的场景,比如大数据场景。


RabbitMQ:


1、开发语言: Erlang 开发


2、性能、吞吐量: 吞吐量比较低,QPS 几万级、性能 u 秒级、主从架构


3、功能: 功能单一


4、缺点: Erlang 小众语言开发,吞吐量低,集群扩展麻烦


5、应用场景: 中小公司对并发和吞吐量要求不高的场景。


RocketMQ:


1、开发语言: java 开发


2、性能、吞吐量: 吞吐量高,QPS 十万级、性能毫秒级、支持集群部署


3、功能: 支持各种高级功能,比如说延迟消息、事务消息、消息回溯、死信队列、消息积压等等


4、缺点: 官方文档相对简单可能是 RocketMQ 目前唯一的缺点


5、应用场景: 适当丢失数据没有关系、吞吐量要求高、不需要太多的高级功能的场景,比如大数据场景。

Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?

| 特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |


| --- | --- | --- | --- | --- |


| 单机吞吐量 | 万级,比 RocketMQ、Kafka 低一个数量级 | 同 ActiveMQ | 10 万级,支撑高吞吐 | 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |


| topic 数量对吞吐量的影响 | ? | ? | topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic | topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源 |


| 时效性 | ms 级 | 微秒级,这是 RabbitMQ 的一大特点,延迟最低 | ms 级 | 延迟在 ms 级以内 |


| 可用性 | 高,基于主从架构实现高可用 | 同 ActiveMQ | 非常高,分布式架构 | 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |


| 消息可靠性 | 有较低的概率丢失数据 | 基本不丢 | 经过参数优化配置,可以做到 0 丢失 | 同 RocketMQ |


| 功能支持 | MQ 领域的功能极其完备 | 基于 erlang 开发,并发能力很强,性能极好,延时很低 | MQ 功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 |


综上,各种对比之后,有如下总结:


一般的业务系统要引入 MQ,最早大家都用 ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃,所以大家还是算了吧,我个人不推荐用这个了;


后来大家开始用 RabbitMQ,但是确实 erlang 语言阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是确实人家是开源的,比较稳定的支持,活跃度也高;


不过现在确实越来越多的公司会去用 RocketMQ,确实很不错,毕竟是阿里出品,但社区可能有突然黄掉的风险(目前 RocketMQ 已捐给?Apache,但 GitHub 上的活跃度其实不算高)对自己公司技术实力有绝对自信的,推荐用 RocketMQ,否则回去老老实实用 RabbitMQ 吧,人家有活跃的开源社区,绝对不会黄。


所以中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择。


如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。


3、为什么要使用 MQ?




因为项目比较大,做了分布式系统,所有远程服务调用请求都是同步执行经常出问题,所以引入了 mq


作用描述解耦系统耦合度降低,没有强依赖关系异步不需要同步执行的远程调用可以有效提高响应时间削峰请求达到峰值后,后端 service 还可以保持固定消费速率消费,不会被压垮


4、RocketMQ 由哪些角色组成,每个角色作用和特点是什么?




角色作用 Nameserver 无状态,动态列表;这也是和 zookeeper 的重要区别之一。zookeeper 是有状态的。Producer 消息生产者,负责发消息到 Broker。Broker 就是 MQ 本身,负责收发消息、持久化消息等。Consumer 消息消费者,负责从 Broker 上拉取消息进行消费,消费完进行 ack。


5、RocketMQ 中的 Topic 和 JMS 的 queue 有什么区别?




queue 就是来源于数据结构的 FIFO 队列。而 Topic 是个抽象的概念,每个 Topic 底层对应 N 个 queue,而数据也真实存在 queue 上的。


6、RocketMQ Broker 中的消息被消费后会立即删除吗?




不会,每条消息都会持久化到 CommitLog 中,每个 Consumer 连接到 Broker 后会维持消费进度信息,当有消息消费后只是当前 Consumer 的消费进度(CommitLog 的 offset)更新了。

追问:那么消息会堆积吗?什么时候清理过期消息?

4.6 版本默认 48 小时后会删除不再使用的 CommitLog 文件


  • 检查这个文件最后访问时间

  • 判断是否大于过期时间

  • 指定时间删除,默认凌晨 4 点


源码如下:


/**


  • {@link org.apache.rocketmq.store.DefaultMessageStore.CleanCommitLogService#isTimeToDelete()}


*/


private boolean isTimeToDelete() {


// when = "04";


String when = DefaultMessageStore.this.getMessageStoreConfig().getDeleteWhen();


// 是 04 点,就返回 true


if (UtilAll.isItTimeToDo(when)) {


return true;


}


// 不是 04 点,返回 false


return false;


}


/**


  • {@link org.apache.rocketmq.store.DefaultMessageStore.CleanCommitLogService#deleteExpiredFiles()}


*/


private void deleteExpiredFiles() {


// isTimeToDelete()这个方法是判断是不是凌晨四点,是的话就执行删除逻辑。


if (isTimeToDelete()) {


// 默认是 72,但是 broker 配置文件默认改成了 48,所以新版本都是 48。


long fileReservedTime = 48 * 60 * 60 * 1000;


deleteCount = DefaultMessageStore.this.commitLog.deleteExpiredFile(72 * 60 * 60 * 1000, xx, xx, xx);


}


}


/**


  • {@link org.apache.rocketmq.store.CommitLog#deleteExpiredFile()}


*/


public int deleteExpiredFile(xxx) {


// 这个方法的主逻辑就是遍历查找最后更改时间+过期时间,小于当前系统时间的话就删了(也就是小于 48 小时)。


return this.mappedFileQueue.deleteExpiredFileByTime(72 * 60 * 60 * 1000, xx, xx, xx);


}


7、RocketMQ 消费模式有几种?




消费模型由 Consumer 决定,消费维度为 Topic。


  • 集群消费


1.一条消息只会被同 Group 中的一个 Consumer 消费


2.多个 Group 同时消费一个 Topic 时,每个 Group 都会有一个 Consumer 消费到数据


  • 广播消费


消息将对一 个 Consumer Group 下的各个 Consumer 实例都消费一遍。即即使这些 Consumer 属于同一个 Consumer Group ,消息也会被 Consumer Group 中的每个 Consumer 都消费一次。


8、消费消息是 push 还是 pull?




RocketMQ 没有真正意义的 push,都是 pull,虽然有 push 类,但实际底层实现采用的是长轮询机制,即拉取方式


broker 端属性 longPollingEnable 标记是否开启长轮询。默认开启


源码如下:


// {@link org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl#pullMessage()}


// 看到没,这是一只披着羊皮的狼,名字叫 PushConsumerImpl,实际干的确是 pull 的活。


// 拉取消息,结果放到 pullCallback 里


this.pullAPIWrapper.pullKernelImpl(pullCallback);

追问:为什么要主动拉取消息而不使用事件监听方式?

事件驱动方式是建立好长连接,由事件(发送数据)的方式来实时推送。


如果 broker 主动推送消息的话有可能 push 速度快,消费速度慢的情况,那么就会造成消息在 consumer 端堆积过多,同时又不能被其他 consumer 消费的情况。而 pull 的方式可以根据当前自身情况来 pull,不会造成过多的压力而造成瓶颈。所以采取了 pull 的方式。


9、broker 如何处理拉取请求的?




Consumer 首次请求 Broker


  • Broker 中是否有符合条件的消息

  • 有 ->

  • 响应 Consumer

  • 等待下次 Consumer 的请求

  • 没有

  • DefaultMessageStore#ReputMessageService#run 方法

  • PullRequestHoldService 来 Hold 连接,每个 5s 执行一次检查 pullRequestTable 有没有消息,有的话立即推送

  • 每隔 1ms 检查 commitLog 中是否有新消息,有的话写入到 pullRequestTable

  • 当有新消息的时候返回请求

  • 挂起 consumer 的请求,即不断开连接,也不返回数据

  • 使用 consumer 的 offset,


10、RocketMQ 如何做负载均衡?




通过 Topic 在多 Broker 中分布式存储实现。

producer 端

发送端指定 message queue 发送消息到相应的 broker,来达到写入时的负载均衡


  • 提升写入吞吐量,当多个 producer 同时向一个 broker 写入数据的时候,性能会下降

  • 消息分布在多 broker 中,为负载消费做准备


默认策略是随机选择:


  • producer 维护一个 index

  • 每次取节点会自增

  • index 向所有 broker 个数取余

  • 自带容错策略


其他实现:


  • SelectMessageQueueByHash

  • hash 的是传入的 args

  • SelectMessageQueueByRandom

  • SelectMessageQueueByMachineRoom 没有实现


也可以自定义实现 MessageQueueSelector 接口中的 select 方法


MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);

consumer 端

采用的是平均分配算法来进行负载均衡。


其他负载均衡算法


平均分配策略(默认)(AllocateMessageQueueAveragely) 环形分配策略(AllocateMessageQueueAveragelyByCircle) 手动配置分配策略(AllocateMessageQueueByConfig) 机房分配策略(AllocateMessageQueueByMachineRoom) 一致性哈希分配策略(AllocateMessageQueueConsistentHash) 靠近机房策略(AllocateMachineRoomNearby)

追问:当消费负载均衡 consumer 和 queue 不对等的时候会发生什么?

Consumer 和 queue 会优先平均分配,如果 Consumer 少于 queue 的个数,则会存在部分 Consumer 消费多个 queue 的情况,如果 Consumer 等于 queue 的个数,那就是一个 Consumer 消费一个 queue,如果 Consumer 个数大于 queue 的个数,那么会有部分 Consumer 空余出来,白白的浪费了。


11、消息重复消费




影响消息正常发送和消费的重要原因是网络的不确定性。


引起重复消费的原因


  • ACK


正常情况下在 consumer 真正消费完消息后应该发送 ack,通知 broker 该消息已正常消费,从 queue 中剔除


当 ack 因为网络原因无法发送到 broker,broker 会认为词条消息没有被消费,此后会开启消息重投机制把消息再次投递到 consumer


  • 消费模式


在 CLUSTERING 模式下,消息在 broker 中会保证相同 group 的 consumer 消费一次,但是针对不同 group 的 consumer 会推送多次


解决方案


  • 数据库表


处理消息前,使用消息主键在表中带有约束的字段中 insert


  • Map


单机时可以使用 map?ConcurrentHashMap?-> putIfAbsent guava cache


  • Redis


分布式锁搞起来。


12、如何让 RocketMQ 保证消息的顺序消费




你们线上业务用消息中间件的时候,是否需要保证消息的顺序性?


如果不需要保证消息顺序,为什么不需要?假如我有一个场景要保证消息的顺序,你们应该如何保证?


首先多个 queue 只能保证单个 queue 里的顺序,queue 是典型的 FIFO,天然顺序。多个 queue 同时消费是无法绝对保证消息的有序性的。所以总结如下:


同一 topic,同一个 QUEUE,发消息的时候一个线程去发送消息,消费的时候 一个线程去消费一个 queue 里的消息。

追问:怎么保证消息发到同一个 queue?

Rocket MQ 给我们提供了 MessageQueueSelector 接口,可以自己重写里面的接口,实现自己的算法,举个最简单的例子:判断i % 2 == 0,那就都放到 queue1 里,否则放到 queue2 里。


for (int i = 0; i < 5; i++) {


Message message = new Message("orderTopic", ("hello!" + i).getBytes());


producer.send(


// 要发的那条消息


message,


// queue 选择器 ,向 topic 中的哪个 queue 去写消息


new MessageQueueSelector() {


// 手动 选择一个 queue


@Override


public MessageQueue select(


// 当前 topic 里面包含的所有 queue


List<MessageQueue> mqs,


// 具体要发的那条消息


Message msg,


// 对应到 send() 里的 args,也就是 2000 前面的那个 0


Object arg) {


// 向固定的一个 queue 里写消息,比如这里就是向第一个 queue 里写消息


if (Integer.parseInt(arg.toString()) % 2 == 0) {


return mqs.get(0);


} else {


return mqs.get(1);


}


}


},


// 自定义参数:0


// 2000 代表 2000 毫秒超时时间


i, 2000);


}


13、RocketMQ 如何保证消息不丢失




首先在如下三个部分都可能会出现丢失消息的情况:


  • Producer 端

  • Broker 端

  • Consumer 端

最后如何让自己一步步成为技术专家

说句实话,如果一个打工人不想提升自己,那便没有工作的意义,毕竟大家也没有到养老的年龄。


当你的技术在一步步贴近阿里 p7 水平的时候,毫无疑问你的薪资肯定会涨,同时你能学到更多更深的技术,交结到更厉害的大牛。


推荐一份 Java 架构之路必备的学习笔记,内容相当全面!!!



成年人的世界没有容易二字,前段时间刷抖音看到一个程序员连着加班两星期到半夜 2 点的视频。在这个行业若想要拿高薪除了提高硬实力别无他法。


你知道吗?现在有的应届生实习薪资都已经赶超开发 5 年的程序员了,实习薪资 26K,30K,你没有紧迫感吗?做了这么多年还不如一个应届生,真的非常尴尬!


进了这个行业就不要把没时间学习当借口,这个行业就是要不断学习,不然就只能被裁员。所以,抓紧时间投资自己,多学点技术,眼前困难,往后轻松!


【关注】+【转发】+【点赞】支持我!创作不易!


**本文已被[CODING 开源项目:【一线大厂 Java 面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://docs


《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享


.qq.com/doc/DSmxTbFJ1cmN1R2dB)收录**

评论

发布
暂无评论
RocketMQ 面试常问问题及答案,多益网络java面试题