架构设计:让你设计一个消息队列,你会怎么设计它的架构?
要设计自己的消息队列,要围绕生产者、消费者、broker和topic4个方面
本章将设计一个以MySQL为基础的消息队列
topic设计
-
topic是必不可少的,它代表的是不同业务
-
分区也是必不可少的,不划分分区会有并发竞争。比如所有生产者都要竞争同一把锁才能写入到topic,消费者读取数据也必须竞争同一把锁,这样性能很差,所以topic内部肯定要通过分区细分
-
MySQL怎么把topic和表关联起来?
-
一个topic是一个逻辑表,对应的分区就是逻辑表执行分库分表后得到的物理表
- 举例:假如有个叫order的topic,代表我有一个逻辑上叫order的表
- 如果这个topic有3个分区,那么代表我有order0、order1、order2三张物理表
- 这种情况下,每张表都可以用自增主键,自增主键对应kafka中的偏移量
-
-
为什么不能所有topic都用一张逻辑表?
-
不这样做的原因有两方面原因,性能和隔离
- 性能:一张表难以应付高并发场景,分库分表也要分几千张
- 隔离:topic天然就是业务隔离的,不同topic用不同的表,相互之间就不会影响
- 使用不同的表,能够更好的安排不同数据库实例来存储
-
broker与消息存储
接下来就要考虑怎么存储topic和分区。kafka存储分区是尽量分散开来,我们也可以学习这种方式
-
为了保证高可用,同一个topic的不同分区最好分散在不同broker上存储。这样一个broker崩溃,这个topic最多只有一个分区收到影响
-
要使用MySQL实现,那么一个topic的不同分区,不仅要分表,还要分库。也就是说最好存放在不同的主从集群上
-
最好的策略是把分区分散在不同主从集群上,比如有四个分区,四个分区分别在四个不同的主从集群上。
- 优点是分散了流量,还互不影响
-
主从集群本身就类似kafaka的副本效果,不过没有ISR概念
- 主从集群说明分区在从库都有对应的数据。如果是一主两从,说明每个分区都有一个主分区和两个从分区。MySQL的主从机制也方便我们不需要管理主从选举的问题
-
发送消息
解决topic和broker后,就是讨论发送者怎么发送了。要先确定是发送者主动发给broker还是broker主动拉去
- 就生产者来说应该主动推送到broker,因为消息产生速率和broker没关系。broker拉取不好控制频率和数量
批量发送
-
为了优化发送性能,可以支持批量发送。生产者凑够一个批次后再发
-
批次大小生产者决定
-
生产者也要有兜底措施,一段时间内没有凑够也要发送,防止消息长时间停留在生产者内存,出现消息丢失
- kafka有类似机制,通过linger.ms来控制等待时长
直接插入消息
用MySQL实现,消息最终是存在DB里的,所以可以通过broker访问数据库,也可以生产者直接访问数据库
- 如果要追求极致性能可以考虑生产者直接把数据插入到数据库。生产者同进程下引入一个本地依赖处理数据库配置和连接问题。发送消息就是调用本地依赖的方法执行INSERT语句。可以省略一次网络中间通信,可以结合批量插入进一步提高性能
消费消息
可能会有不同业务方消费同一个topic的消息,所以可以引入消费组和消费者的概念
- 一个业务方就是一个消费者组,一个消费者组有多个消费者。kafka中,每个分区有一个消费者,一个消费者可以消费多个分区。可以参照这个方法,每个分区是一张表,这张表对于一个消费者组来说,只能有一个消费者读取
记录偏移量
- 可以用另外一张表来记录,只需要记录消费组名字、分区、已消费偏移量即可。
- 每次消费者提交消息,对于broker来说就是更新这张表的偏移量
直接拉取
- 消费者也可以设计一个本地依赖直连数据库,也可以提高性能
延迟消息
用MySQL为基础很容易实现延迟消息,只要在消息表里加上一个预期发送时间,消费者拉去消息的时候判断一下