消息中间件面试题31道RabbitMQ+ActiveMQ+Kafka

Java架构师追风 2019-07-26

消息系统 java 服务器 cluster 集群 消息中间件 node rabbitmq exchange Consumer 面试题 ActiveMQ kafka

前言

文章开始前,我们先了解一下什么是消息中间件?

什么是中间件?

非底层操作系统软件,非业务应用软件,不是直接给最终用户使用的,不能直接给客户带来价值的软件统称为中间件。

什么是消息中间件?

是关注于数据的发送和接收,利用高效可靠的异步消息传递机制集成分布式系统

图示:
image

消息中间件RabbitMQ+ActiveMQ+Kafka的对比

image

接下来就是消息中间件面试题RabbitMQ+ActiveMQ+Kafka

RabbitMQ消息中间件系列

1:RabbitMQ 中的 broker 是指什么?cluster 又是指什么?

答:broker 是指一个或多个 erlang node 的逻辑分组,且 node 上运行着 RabbitMQ 应用程序。cluster 是在 broker 的基础之上,增加了 node 之间共享元数据的约束。

2:什么是元数据?元数据分为哪些类型?包括哪些内容?与 cluster 相关的元数据有哪些?元数据是如何保存的?元数据在 cluster 中是如何分布的?

答:在非 cluster 模式下,元数据主要分为 Queue 元数据(queue 名字和属性等)、Exchange 元数据(exchange 名字、类型和属性等)、Binding 元数据(存放路由关系的查找表)、Vhost 元数据(vhost 范围内针对前三者的名字空间约束和安全属性设置)。在cluster 模式下,还包括 cluster 中 node 位置信息和 node 关系信息。元数据按照 erlang node 的类型确定是仅保存于 RAM 中,还是同时保存在 RAM 和 disk 上。元数据在cluster 中是全 node 分布的。

3:RAM node 和 disk node 的区别?

答:RAM node 仅将 fabric(即 queue、exchange 和 binding 等 RabbitMQ 基础构件)相关元数据保存到内存中,但 disk node 会在内存和磁盘中均进行存储。RAM node 上唯一会存储到磁盘上的元数据是 cluster 中使用的 disk node 的地址。要求在 RabbitMQ cluster 中至少存在一个 disk node 。

4:RabbitMQ 上的一个 queue 中存放的 message 是否有数量限制?

答:可以认为是无限制,因为限制取决于机器的内存,但是消息过多会导致处理效率的下降。

5:RabbitMQ 概念里的 channel、exchange 和 queue 这些东东是逻辑概念,还是对应着进程实体?这些东东分别起什么作用?

答:queue 具有自己的 erlang 进程;exchange 内部实现为保存 binding 关系的查找表; channel 是实际进行路由工作的实体,即负责按照 routing_key 将 message 投递给queue 。由 AMQP 协议描述可知,channel 是真实 TCP 连接之上的虚拟连接,所有AMQP 命令都是通过 channel 发送的,且每一个 channel 有唯一的 ID。一个 channel 只能被单独一个操作系统线程使用,故投递到特定 channel 上的 message 是有顺序的。但一个操作系统线程上允许使用多个 channel 。channel 号为 0 的 channel 用于处理所有对于当前 connection 全局有效的帧,而 1-65535 号 channel 用于处理和特定 channel 相关的帧。

其中每一个 channel 运行在一个独立的线程上,多线程共享同一个 socket。

6:vhost 是什么?起什么作用?

答:vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的

queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。

7:为什么 heavy RPC 的使用场景下不建议采用 disk node ?

答:heavy RPC 是指在业务逻辑中高频调用 RabbitMQ 提供的 RPC 机制,导致不断创建、销毁 reply queue ,进而造成 disk node 的性能问题(因为会针对元数据不断写盘)。所以在使用 RPC 机制时需要考虑自身的业务场景。

8:向不存在的 exchange 发 publish 消息会发生什么?向不存在的 queue 执行consume 动作会发生什么?

答:都会收到 Channel.Close 信令告之不存在(内含原因 404 NOT_FOUND)。

9:RabbitMQ 允许发送的 message 最大可达多大?

答:根据 AMQP 协议规定,消息体的大小由 64-bit 的值来指定,所以你就可以知道到底能发多大的数据了。

10:什么情况下 producer 不主动创建 queue 是安全的?

答:1.message是允许丢失的;2.实现了针对未处理消息的republish功能(例如采用Publisher Confirm 机制)。

11:“dead letter”queue 的用途?

答:当消息被 RabbitMQ server 投递到 consumer 后,但 consumer 却通过 Basic.Reject 进行了拒绝时(同时设置 requeue=false),那么该消息会被放入“dead letter”queue 中。该 queue 可用于排查 message 被 reject 或 undeliver 的原因。

12:为什么说保证 message 被可靠持久化的条件是 queue 和 exchange 具有durable 属性,同时 message 具有 persistent 属性才行?

答:binding 关系可以表示为 exchange – binding – queue 。从文档中我们知道,若要求投递的 message 能够不丢失,要求 message 本身设置 persistent 属性,要求 exchange 和 queue 都设置 durable 属性。其实这问题可以这么想,若 exchange 或 queue 未设置durable 属性,则在其 crash 之后就会无法恢复,那么即使 message 设置了 persistent 属性,仍然存在 message 虽然能恢复但却无处容身的问题;同理,若 message 本身未设置persistent 属性,则 message 的持久化更无从谈起。

13:什么情况下会出现 blackholed 问题?

答:blackholed 问题是指,向 exchange 投递了 message ,而由于各种原因导致该message 丢失,但发送者却不知道。可导致 blackholed 的情况:1.向未绑定 queue 的exchange 发送 message;2.exchange 以 binding_key key_A 绑定了 queue queue_A,但向该 exchange 发送 message 使用的 routing_key 却是 key_B。

14:如何防止出现 blackholed 问题?

答:没有特别好的办法,只能在具体实践中通过各种方式保证相关 fabric 的存在。另外, 如果在执行 Basic.Publish 时设置 mandatory=true ,则在遇到可能出现 blackholed 情况时,服务器会通过返回 Basic.Return 告之当前 message 无法被正确投递(内含原因 312 NO_ROUTE)。

15:Consumer Cancellation Notification 机制用于什么场景?

答:用于保证当镜像 queue 中 master 挂掉时,连接到 slave 上的 consumer 可以收到自身 consume 被取消的通知,进而可以重新执行 consume 动作从新选出的 master 出获得消息。若不采用该机制,连接到 slave 上的 consumer 将不会感知 master 挂掉这个事情,导致后续无法再收到新 master 广播出来的 message 。另外,因为在镜像 queue 模式下,存在将 message 进行 requeue 的可能,所以实现 consumer 的逻辑时需要能够正确处理出现重复 message 的情况。
image

ActiveMQ消息中间件系列

1.什么是 ActiveMQ?

activeMQ 是一种开源的,实现了 JMS1.1 规范的,面向消息(MOM)的中间件,为应用程序提供高效的、可扩展的、稳定的和安全的企业级消息通信

2.ActiveMQ 服务器宕机怎么办?

这得从 ActiveMQ 的储存机制说起。在通常的情况下,非持久化消息是存储在内存中的,持久化消息是存储在文件中的,它们的最大限制在配置文件的节点中配置。但是,在非持久化消息堆积到一定程度,内存告急的时候,ActiveMQ 会将内存中的非持久化消息写入临时文件中,以腾出内存。虽然都保存到了文件里,但它和持久化消息的区别是,重启后持久化消息会从文件中恢复,非持久化的临时文件会直接删除。

那如果文件增大到达了配置中的最大限制的时候会发生什么?我做了以下实验:

设置 2G 左右的持久化文件限制,大量生产持久化消息直到文件达到最大限制,此时生产者阻塞,但消费者可正常连接并消费消息,等消息消费掉一部分,文件删除又腾出空间之后,生产者又可继续发送消息, 服务自动恢复正常。

设置 2G 左右的临时文件限制,大量生产非持久化消息并写入临时文件,在达到最大限制时,生产者阻塞,消费者可正常连接但不能消费消息,或者原本慢速消费的消费者,消费突然停止。整个系统可连接, 但是无法提供服务,就这样挂了。

具体原因不详,解决方案:尽量不要用非持久化消息,非要用的话,将临时文件限制尽可能的调大。

3.丢消息怎么办?

这得从 java 的 java.net.SocketException 异常说起。简单点说就是当网络发送方发送一堆数据,然后调用 close 关闭连接之后。这些发送的数据都在接收者的缓存里,接收者如果调用 read 方法仍旧能从缓存中读取这些数据,尽管对方已经关闭了连接。但是当接收者尝试发送数据时,由于此时连接已关闭,所以会发生异常,这个很好理解。不过需要注意的是,当发生 SocketException 后,原本缓存区中数据也作废了,此时接收者再次调用 read 方法去读取缓存中的数据,就会报 Software caused connection abort: recv failed 错误。

通过抓包得知,ActiveMQ 会每隔 10 秒发送一个心跳包,这个心跳包是服务器发送给客户端的,用来判断客户端死没死。如果你看过上面第一条,就会知道非持久化消息堆积到一定程度会写到文件里,这个写的过程会阻塞所有动作,而且会持续 20 到 30 秒,并且随着内存的增大而增大。当客户端发完消息调用connection.close()时,会期待服务器对于关闭连接的回答,如果超过 15 秒没回答就直接调用 socket 层的 close 关闭 tcp 连接了。这时客户端发出的消息其实还在服务器的缓存里等待处理,不过由于服务器心跳包的设置,导致发生了 java.net.SocketException 异常,把缓存里的数据作废了,没处理的消息全部丢失。

解决方案:用持久化消息,或者非持久化消息及时处理不要堆积,或者启动事务,启动事务后,

commit()方法会负责任的等待服务器的返回,也就不会关闭连接导致消息丢失了。

4.持久化消息非常慢。

默认的情况下,非持久化的消息是异步发送的,持久化的消息是同步发送的,遇到慢一点的硬盘,发送消息的速度是无法忍受的。但是在开启事务的情况下,消息都是异步发送的,效率会有 2 个数量级的提升。所以在发送持久化消息时,请务必开启事务模式。其实发送非持久化消息时也建议开启事务,因为根本不会影响性能。

5.消息的不均匀消费。

有时在发送一些消息之后,开启 2 个消费者去处理消息。会发现一个消费者处理了所有的消息,另一个消费者根本没收到消息。原因在于 ActiveMQ 的 prefetch 机制。当消费者去获取消息时,不会一条一条去获取,而是一次性获取一批,默认是 1000 条。这些预获取的消息,在还没确认消费之前,在管理控制台还是可以看见这些消息的,但是不会再分配给其他消费者,此时这些消息的状态应该算作“已分配未消 费”,如果消息最后被消费,则会在服务器端被删除,如果消费者崩溃,则这些消息会被重新分配给新的消费者。但是如果消费者既不消费确认,又不崩溃,那这些消息就永远躺在消费者的缓存区里无法处理。更通常的情况是,消费这些消息非常耗时,你开了 10 个消费者去处理,结果发现只有一台机器吭哧吭哧

处理,另外 9 台啥事不干。

解决方案:将 prefetch 设为 1,每次处理 1 条消息,处理完再去取,这样也慢不了多少。

6.死信队列。

如果你想在消息处理失败后,不被服务器删除,还能被其他消费者处理或重试,可以关闭AUTO_ACKNOWLEDGE,将 ack 交由程序自己处理。那如果使用了 AUTO_ACKNOWLEDGE,消息是什么时候被确认的,还有没有阻止消息确认的方法?有!

消费消息有 2 种方法,一种是调用 consumer.receive()方法,该方法将阻塞直到获得并返回一条消息。这种情况下,消息返回给方法调用者之后就自动被确认了。另一种方法是采用 listener 回调函数,在有消息到达时,会调用 listener 接口的 onMessage 方法。在这种情况下,在 onMessage 方法执行完毕后, 消息才会被确认,此时只要在方法中抛出异常,该消息就不会被确认。那么问题来了,如果一条消息不能被处理,会被退回服务器重新分配,如果只有一个消费者,该消息又会重新被获取,重新抛异常。就算有多个消费者,往往在一个服务器上不能处理的消息,在另外的服务器上依然不能被处理。难道就这么退回

–获取–报错死循环了吗?

在重试 6 次后,ActiveMQ 认为这条消息是“有毒”的,将会把消息丢到死信队列里。如果你的消息不见了,去 ActiveMQ.DLQ 里找找,说不定就躺在那里。
image

Kafka消息中间件系列

1.Kafka 的设计时什么样的呢?

Kafka 将消息以 topic 为单位进行归纳

将向 Kafka topic 发布消息的程序成为 producers.

将预订 topics 并消费消息的程序成为 consumer.

Kafka 以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个 broker. producers 通过网络将消息发送到 Kafka 集群,集群向消费者提供消息

2.数据传输的事物定义有哪三种?

数据传输的事务定义通常有以下三种级别:

(1)最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输

(2)最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输.

(3)精确的一次(Exactly once):不会漏传输也不会重复传输,每个消息都传输被一次而且仅仅被传输一次,这是大家所期望的

3.Kafka 判断一个节点是否还活着有那两个条件?

(1)节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连接

(2)如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久

4.producer 是否直接将数据发送到 broker 的 leader(主节点)?

producer 直接将数据发送到 broker 的 leader(主节点),不需要在多个节点进行分发,为了帮助 producer 做到这点,所有的 Kafka 节点都可以及时的告知:哪些节点是活动的,目标topic 目标分区的 leader 在哪。这样 producer 就可以直接将消息发送到目的地了

5、Kafa consumer 是否可以消费指定分区消息?

Kafaconsumer 消费消息时,向 broker 发出"fetch"请求去消费特定分区的消息,consumer 指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer 拥有了 offset 的控制权,可以向后回滚去重新消费之前的消息,这是很有意义的

6、Kafka 消息是采用 Pull 模式,还是 Push 模式?

Kafka 最初考虑的问题是,customer 应该从 brokes 拉取消息还是 brokers 将消息推送到consumer,也就是 pull 还 push。在这方面,Kafka 遵循了一种大部分消息系统共同的传统的设计:producer 将消息推送到 broker,consumer 从 broker 拉取消息

一些消息系统比如 Scribe 和 ApacheFlume 采用了 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 阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发

7.Kafka 的消费者如何消费数据

消费者每次消费数据的时候,消费者都会记录消费的物理偏移量(offset)的位置等到下次消费时,他会接着上次位置继续消费

8.消费者负载均衡策略

一个消费者组中的一个分片对应一个消费者成员,他能保证每个消费者成员都能访问,如果组中成员太多会有空闲的成员

9.数据有序

一个消费者组里它的内部是有序的消费者组与消费者组之间是无序的

10.kafaka 生产数据时数据的分组策略

生产者决定数据产生到集群的哪个 partition 中每一条消息都是以(key,value)格式

Key 是由生产者发送数据传入

所以生产者(key)决定了数据产生到集群的哪个 partition

喜欢文章记得点个赞,感谢支持!

登录 后评论
下一篇
云攻略小攻
595人浏览
2019-10-11
相关推荐
分布式之消息队列复习精讲
770人浏览
2018-05-16 13:46:00
消息队列深入解析
1332人浏览
2018-04-21 20:12:44
ActiveMQ消息队列
4908人浏览
2016-09-03 12:01:40
消息中间件收录集
841人浏览
2017-01-26 10:42:00
0
0
1
681