消息队列常见面试题
参考:
什么是消息队列
用来存放要传输的消息的一个队列
- 把数据放到消息队列叫做生产者
- 从消息队列里边取数据叫做消费者
消息队列有哪些模型
点对点
消息生产者向消息队列中发送了一个消息之后,只能被一个消费者消费一次。消费者主动拉取数据,消息被消费后从队列中删除
发布/订阅
消息生产者向频道发送一个消息之后,多个消费者可以从该频道订阅到这条消息并消费。有推送和拉取两种方式,消费者消费数据之后不会清除消息,每条消息有一定的存活时间,过期自动删除。推送的方式的缺点是每个消费者的消费能力可能不一样,造成消费者闲置或者处理不过来。
消息队列的优点和使用场景
异步处理
发送者将消息发送给消息队列之后,不需要同步等待消息接收者处理完毕,而是立即返回进行其它操作。消息接收者从消息队列中订阅消息之后异步处理。
例如在注册流程中通常需要发送验证邮件来确保注册用户身份的合法性,可以使用消息队列使发送验证邮件的操作异步处理,用户在填写完注册信息之后就可以完成注册,而将发送验证邮件这一消息发送到消息队列中。
只有在业务流程允许异步处理的情况下才能这么做,例如上面的注册流程中,如果要求用户对验证邮件进行点击之后才能完成注册的话,就不能再使用消息队列。
流量削锋
在高并发的场景下,如果短时间有大量的请求到达会压垮服务器。
可以将请求发送到消息队列中,服务器按照其处理能力从消息队列中订阅消息进行处理。
应用解耦
通过使用消息队列,一个模块只需要向消息队列中发送消息,其它模块可以选择性地从消息队列中订阅消息从而完成调用。
模块之间不直接进行调用,所以模块之间耦合度就会很低,那么修改一个模块或者新增一个模块对其它模块的影响会很小,从而实现可扩展性。
消费者怎么得到消息队列的数据?
消费者怎么从消息队列里边得到数据?有两种办法:
- 生产者将数据放到消息队列中,消息队列有数据了,主动推送给消费者(俗称push),push (推)模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的。
- 消费者不断去轮询消息队列,看看有没有新的数据,如果有就消费(俗称pull),pull 模式不足之处是,如果 kafka 没有数据,消费者可能会陷入循环中,一直返回空数据。针对这一点,Kafka 的消费者在消费数据时会传入一个时长参数 timeout,如果当前没有数据可供消费,consumer 会等待一段时间之后再返回,这段时长即为 timeout。
消息队列的可靠性
发送端的可靠性
发送端完成操作后一定能将消息成功发送到消息队列中。
实现方法:
在本地数据库建一张消息表,将消息数据与业务数据保存在同一数据库实例里,这样就可以利用本地数据库的事务机制。事务提交成功后,将消息表中的消息转移到消息队列中,若转移消息成功则删除消息表中的数据,否则继续重传。
接收端的可靠性
接收端能够从消息队列成功消费一次消息。
两种实现方法:
- 保证接收端处理消息的业务逻辑具有幂等性:只要具有幂等性,那么消费多少次消息,最后处理的结果都是一样的。
- 保证消息具有唯一编号,并使用一张日志表来记录已经消费的消息编号
消息队列可能面对的问题
1. 高可用性,
无论是我们使用消息队列来做解耦、异步还是削峰,消息队列肯定不能是单机的。试着想一下,如果是单机的消息队列,万一这台机器挂了,那我们整个系统几乎就是不可用了。所以,当我们项目中使用消息队列,都是得集群/分布式的。要做集群/分布式就必然希望该消息队列能够提供现成的支持,而不是自己写代码手动去实现。
2 数据丢失问题
我们将数据写到消息队列上,系统B和C还没来得及取消息队列的数据,就挂掉了。如果没有做任何的措施,我们的数据就丢了。同样地,消息队列中的数据也需要存在别的地方,这样才尽可能减少数据的丢失。但是存在哪,同步存储还是异步存储又是一个问题。