以下问题中的回答基于 RocketMQ 来回答.
-
为什么要用 MQ
异步、解耦、削峰
(然后具体针对业务说下即可)
eg.
业务场景: 保单创建后需要通知财务系统、通知客户系统、邮件系统、短信系统
异步: 如果都同步调用,那么耗时会增加。通过 MQ 异步的方式,可以加快响应。
解耦: 通过引入 MQ,无需直连各系统,就算各系统出现问题,也不会影响保单的创建.
削峰: 保单创建并发高,是微服务系统.其他系统还未改造成微服务系统,通过引入 MQ,各下游系统可按照自己的速率消费消息。不至于被压垮 -
各MQ都有什么优缺点
ActiveMQ、RocketMQ、Kafka 对比.
eg.ActiveMQ RocketMQ Kafka Client SDK Java, .NET, C++ etc. Java, C++, Go Java, Scala etc. Protocol and Specification Push model, support OpenWire, STOMP, AMQP, MQTT, JMS Pull model, support TCP, JMS, OpenMessaging Pull model, support TCP Ordered Message Exclusive Consumer or Exclusive Queues can ensure ordering Ensure strict ordering of messages,and can scale out gracefully Ensure ordering of messages within a partition Scheduled Message Supported Supported Not Supported Batched Message Not Supported Supported, with sync mode to avoid message loss Supported, with async producer BroadCast Message Supported Supported Not Supported Message Filter Supported Supported, property filter expressions based on SQL92 Supported, you can use Kafka Streams to filter messages Server Triggered Redelivery Not Supported Supported Not Supported Message Storage Supports very fast persistence using JDBC along with a high performance journal,such as levelDB, kahaDB High performance and low latency file storage High performance file storage Message Retroactive Supported Supported timestamp and offset two indicates Supported offset indicate Message Priority Supported Not Supported Not Supported High Availability and Failover Supported, depending on storage,if using kahadb it requires a ZooKeeper server Supported, Master-Slave model, without another kit Supported, requires a ZooKeeper server Message Track Not Supported Supported Not Supported Configuration The default configuration is low level, user need to optimize the configuration parameters Work out of box,user only need to pay attention to a few configurations Kafka uses key-value pairs format for configuration. These values can be supplied either from a file or programmatically. Management and Operation Tools Supported Supported, rich web and terminal command to expose core metrics Supported, use terminal command to expose core metrics eg. 选择 RocketMQ,理由是 RocketMQ 在保证了可靠性的前提下,队列特性足够丰富,可运维程度比较高。其快速横向扩展的能力,也能保证未来几年我们对其性能的要求。另外RocketMQ基于Java语言开发,也降低了我们后续对其进行扩展和二次开发的难度
-
引入 MQ 后会带来哪些问题,如何解决这些问题.[带出下面的几个问题]
系统复杂性提高
系统可用性降低
一致性问题 -
MQ 如何保证高可用
以 RocketMQ 为例.
支持主从模式(支持异步复制模式和同步双写模式):
一主一从(主节点挂了的时候,从节点可自动顶上,无需人工干预)
一主多从(主节点挂了的时候,需人工干预)
多主多从(自己项目用的二主二从)
(通过配置相同的 brokername 来确定是同一集群,通过配置不同的 brokerId 来确定是主节点还是从节点,主节点的 brokerId 为0,从节点的 brokerId 大于0;主用 Broker 提供读写服务,而备用 Broker 只提供读服务) -
如何保证消费幂等
mq 不保证消息一定只发送一次。所以消息的去重需要在消费端自己保证。一般有如下几种方案:- 消息中带有业务的唯一 id 主键,基于数据库的主键去重
- 消息中带有业务的唯一 ID 主键,基于 redis 的 setIfabsent 判断是否重复
-
如何保证消息顺序
RocketMQ 可以严格的保证消息有序。但这个顺序,不是全局顺序,只是分区(queue)顺序。要全局顺序只能一个分区。之所以出现你这个场景看起来不是顺序的,是因为发送消息的时候,消息发送默认是会采用轮询的方式发送到不通的 queue(分区)
所以如果需要实现顺序消息的话,需要满足两个条件:- 生产消息要有序:需要将相关消息发送到同一个 queue 。需要重写
MessageQueueSelector
。// eg.将相同订单号的不同流程消息发送到同一个queue. SendResult sendResult = producer.send(msg, new MessageQueueSelector() { @Override public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) { Long id = (Long) arg; long index = id % mqs.size(); return mqs.get((int)index); } }, orderList.get(i).getOrderId());//订单id ```
- 消费消息要有序:保证消费同一 queue,需要使用
MessageListenerOrderly
consumer.registerMessageListener(new MessageListenerOrderly() { Random random = new Random(); @Override public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) { context.setAutoCommit(true); System.out.print(Thread.currentThread().getName() + " Receive New Messages: " ); for (MessageExt msg: msgs) { System.out.println(msg + ", content:" + new String(msg.getBody())); } try { //模拟业务逻辑处理中... TimeUnit.SECONDS.sleep(random.nextInt(10)); } catch (Exception e) { e.printStackTrace(); } return ConsumeOrderlyStatus.SUCCESS; } );
- 生产消息要有序:需要将相关消息发送到同一个 queue 。需要重写
-
部署结构如何
主要有如下几个组件:
Name Server: Name Server 是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。
Broker:Broker 部署相对复杂,Broker 分为 Master 与 Slave,一个 Master 可以对应多个 Slave,但是一个 Slave 只能对应一个 Master,Master 与 Slave 的对应关系通过指定相同的 BrokerName,不同的BrokerId 来定义,BrokerId 为0表示 Master,非0表示 Slave。Master 也可以部署多个。每个 Broker 与 Name Server 集群中的所有节点建立长连接,定时注册 Topic 信息到所有 Name Server。
Producer:Producer 与 Name Server 集群中的其中一个节点(随机选择)建立长连接,定期从 Name Server 取 Topic 路由信息,并向提供Topic服务的Master建立长连接,且定时向 Master 发送心跳。 Producer 完全无状态,可集群部署。
Consumer:Consumer 与 Name Server 集群中的其中一个节点(随机选择)建立长连接,定期从 Name Server 取Topic路由信息,并向提供 Topic 服务的 Master 、Slave 建立长连接,且定时向 Master、Slave 发送心跳。Consumer 既可以从 Master 订阅消息,也可以从 Slave 订阅消息,订阅规则由 Broker 配置决定。 -
如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路
需要考虑实现消息队列的关键点:
MQ 的可伸缩性设计
MQ 的高可用设计
MQ 的可靠性设计(持久化)、等等…
eg.
可伸缩性:首先这个 mq 得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下 kafka 的设计理念,broker -> topic -> partition,每个 partition 放一个机器,就存一部分数据。如果现在资源不够了,简单啊,给 topic 增加 partition,然后做数据迁移,增加机器,不就可以存放更多数据,提供更高的吞吐量了?
可靠性设计(持久化):其次你得考虑一下这个mq的数据要不要落地磁盘吧?那肯定要了,落磁盘,才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是 kafka 的思路。
高可用设计:其次你考虑一下你的mq的可用性啊?这个事儿,具体参考我们之前可用性那个环节讲解的kafka的高可用保障机制。多副本 -> leader & follower -> broker 挂了重新选举leader即可对外服务。
数据0丢失:能不能支持数据0丢失啊?可以的,参考我们之前说的那个 kafka 数据零丢失方案(同步双写) -
RocketMQ 底层数据结构
如上图所示,RocketMQ 采取了一种数据与索引分离的存储方法。有效降低文件资源、IO资源,内存资源的损耗。即便是阿里这种海量数据,高并发场景也能够有效降低端到端延迟,并具备较强的横向扩展能力。 -
RocketMQ 为什么这么快,能承受十万级别的并发
参考阿里中间件团队博客的文章:万亿级数据洪峰下的分布式消息引擎、Apache RocketMQ 背后的设计思路与最佳实践 -
消息积压了怎么处理
- 检查消费者状态是否正常,不正常的话则先修复消费者状态
- 迅速创建一批临时的新 topic,并增加消费者消费这些新 topic;将积压的 topic 中的内容消费重新发送到新的临时 topic 中
- 处理后需要增加完善的监控通知机制以及消费者能力横向拓展的机制
待续