三类角色:
Broker(服务端):
- MQ 中最核心的部分,是 MQ 的服务端,核心逻辑几乎全在这里
- 它为生产者和消费者提供 RPC 接口,负责消息的存储、备份和删除,以及消费关系的维护等。
Producer(生产者):
- MQ 的客户端之一,调用 Broker 提供的 RPC 接口发送消息。
Consumer(消费者):
- MQ 的另外一个客户端,调用 Broker 提供的 RPC 接口接收消息,同时完成消费确认。
完整的数据流:
RPC通信
解决的是 Broker 与 Producer 以及 Consumer 之间的通信问题。
- 如果不重复造轮子,直接利用成熟的
RPC
框架Dubbo
或者Thrift
实现即可
- 这样不需要考虑服务注册与发现、负载均衡、通信协议、序列化方式等一系列问题了。
也可以基于 Netty 来做底层通信,用 Zookeeper、Euraka 等来做注册中心
- 然后自定义一套新的通信协议(类似 Kafka)
也可以基于 AMQP 这种标准化的 MQ 协议来做实现(类似 RabbitMQ)。
- 对比直接用 RPC 框架,这种方案的定制化能力和优化空间更大。
高可用设计
高可用主要涉及两方面:
- Broker 服务的高可用、存储方案的高可用。
Broker 服务的高可用
只需要保证 Broker 可水平扩展进行集群部署即可
- 进一步通过服务自动注册与发现、负载均衡、超时重试机制、发送和消费消息时的
ack
机制来保证。存储方案的高可用有两个思路:
参考
Kafka
的分区 + 多副本模式
- 但是需要考虑分布式场景下数据复制和一致性方案(类似
Zab、Raft
等协议),并实现自动故障转移还可以用主流的 DB、分布式文件系统、带持久化能力的 KV 系统,它们都有自己的高可用方案。
存储设计
存储的高性能如何保证?
- 这个问题的决定因素在于存储结构的设计。
目前主流的方案是:
- 追加写日志文件(数据部分) + 索引文件的方式(很多主流的开源 MQ 都是这种方式)
索引设计上可以考虑稠密索引或者稀疏索引,查找消息可以利用跳转表、二份查找等
- 还可以通过操作系统的页缓存、零拷贝等技术来提升磁盘文件的读写性能。
如果不追求很高的性能,也可以考虑现成的分布式文件系统、KV 存储或者数据库方案。
消费关系管理
为了支持发布-订阅的广播模式,Broker 需要知道每个主题都有哪些 Consumer 订阅了
- 基于这个关系进行消息投递。
由于 Broker 是集群部署的,所以消费关系通常维护在公共存储上
- 可以基于 Zookeeper、Apollo 等配置中心来管理以及进行变更通知。
高性能设计
存储的高性能前面已经谈过了,还可以从其他方面进一步优化性能。
- 比如 Reactor 网络 IO 模型、业务线程池的设计、生产端的批量发送、Broker 端的异步刷盘、消费端的批量拉取等等。