写点什么

基于 MySQL 存储的自研消息队列架构设计文档

用户头像
Geek_2e7dd7
关注
发布于: 2021 年 04 月 26 日

前言

适用于游戏业务线,基于 MySQL 的自研消息队列架构设计文档。指导开发,测试和运维。


词汇表

MySQL:广泛使用的关系型数据库


1. 业务背景

游戏业务发展迅速,各种子系统越来越多。出现了如下问题:

1.系统间的写作变得越来越复杂,沟通 &工作效率下降。

2.模块间耦合程度加深,不利于各个应用独立演化。

3.调用链路变长,响应时间增加,用户感知的延迟上升。

为了解决这些问题,引入消息队列将模块、子系统之间解耦,优化接口响应时间,隔离依赖系统失败,提高系统演化速度。以更好地支持业务发展。

引入消息队列后,上游服务 A,B,C 只与消息队列通信,下游服务 D,E,F 只与消息队列通信。上下游服务间不产生直接依赖。


2. 约束和限制

1.必须在 2021.06.30 号完成

2.成本不能超过 1000 万

3.数据库采用 MySQL

4.运维要融入现有的运维系统

5.采用 Java 语言开发


3. 总体架构


采用发布订阅模式工作,每个 topic 可以同时被多个消费组(consumer group)消费,每个消费组都有自己的消费偏移量。在一个消费组中,topic 的一条消息只能被一个消费者消费。topic 内部维护每个消息的偏移,按照消息到达消息队列的时间顺序编号。

生产者 &消费者与 proxy 通信。proxy 从 catalog 服务将 topic 名字映射成 broker 列表,再去连接 broker。broker 负责从 catalog 查找消费组的偏移,查找 topic 到 MySQL 表的对应关系。

broker:

无状态,部署在 k8s 容器里。

接收 proxy 发来的消息写入 MySQL,接收消费者的请求,从 MySQL 读出消息发送给 proxy。

broker 没有主备关系,都是对等的。

控制台:Web 应用,可以接收运维的控制命令,比如新建 topic,列出所有的 topic,为 topic 设置分片(sharding)数量,配置消息保存的时间。

catalog:存储 topic 到 MySQL 表的映射,topic 到 broker 列表的映射。proxy 从这里获得 broker 列表,再去链接 broker。broker 从这里获得 topic 每个 shard 对应的 MySQL 表,以发送后续的读写请求会。

proxy:生产者 &消费者与 proxy 通信。proxy 负责把请求转发给具体的 broker 处理,再把相应发送回去,起到负载均衡的作用。在消费者发送请求时向 catalog 标记最远的消费偏移。

对于每个 topic,共享 catalog 服务实例,有独立的 broker 实例+proxy 实例。MySQL 服务器是公用的,每个 topic 有自己的一组 MySQL 表。topic 可以有多个 shard,每个 shard 对应一张 MySQL 表。每个 shard 会被分配给一个 broker 进行读写操作。

3.1 架构分析

3.1.1 高可用

broker 本身无状态,挂掉可以由 k8s 拉起新的容器。生产者需要检查消息发送结果,如果不成功需要重试。消费者在消费失败时可以重试。

proxy 无状态,proxy 在第一次收到对某个 topic 的请求时去 catalog 查找元数据。之后缓存在内存里。如果处理请求的过程中 proxy 挂掉,由调用方即生产者、消费者重试。

MySQL 使用主备方式保证高可用。

catalog 无状态,映射关系也会持久化到 MySQL 里。

3.1.2 高性能

网络传输高性能:

生产者可以 buffer 一批数据再发送,等待响应再发下一批。

组件间消息采用二进制编码,最大程度节省网络带宽。

MySQL 表采用自增主键+固定长度列作为表结构,消息内容作为二进制直接存入 MySQL。消息队列内部不对消息体进行解析。因结构简单,可获得很高的读写性能。

3.1.3 可扩展

存储扩容即 MySQL 扩容,加机器部署新的实例即可。broker 处理能力出现瓶颈,扩容 broker 数量。proxy 同理。catalog 负载过高也可简单扩大容器数量。

由于消息队列不对消息内容做任何解析,也不做任何假设,该系统可以承接任何类型的消息,也能适应消息结构演化。

3.1.4 可维护

提供控制台,各组件的运行情况均可配置。提供报警功能。k8s 可自动重启 &扩容,简化运维工作。

3.1.5 易使用

生产消费者只需要知道一个 topic 名字和 proxy 地址。其余细节都隐藏在消息队列内部。

3.1.6 可观测

消息队列提供指标面板,限制 topic 相关的消息量,存储量,组件 cpu 使用率等指标。


4. 详细设计

4.1 核心功能

4.1.1 消息发送



4.1.2 消费消息


4.1.3

brokers 决定向那个 table 写 msg,从哪个 table 读 msg。

topic 创建时制定了分片数量,每个分片会创建一个 MySQL 表,例如 topic 名字为 new-message-queue,

有 3 个分片,分片 table 名字可以叫 new-message-queue-shard1,new-message-queue-shard2,new-message-queue-shard3.

brokers 收到 msg 后,计算 hash 值,再对 shard 数取模决定发送给那个 table。

brokers 收到消息 shard+offset 后,用 shard 找到对应 table,再从中读取一条记录。

4.1.2 消费者需要先跟 proxy 通信,获得 topic 对应的 shard 列表,每个 shard 最新的消费偏移,再发送消费请求给 proxy

4.2 关键设计

1)可靠的发送

提供 SDK,封装重试逻辑,以在连接的 proxy 宕机时随机从其他的 proxy 选择一个发送请求。如果达到重试次数上限或者尝试了所有的 proxy 均不成功,大日志,丢弃消息,向调用者返回这一状态。SDK 使用者需要对这种情况做处理,比如保存消息到一个持久化存储,一段时间后重试发送。

2)可靠的存储

消息存在 MySQL 表里,保证可靠存储即需要保证 MySQL 服务高可用。使用主备模式提供高可用保证。需要运维团队参与保障。

3)可靠的消费

消费便宜会同步进持久化存储,消费者宕机回复后可以从 proxy 获取上一次最近的消费进度。消费者可以使用 SDK 从多个 shard 同时消费消息。


4.3 设计规范

proxy、catalog、broker、控制台使用 Java + Spring boot 开发

MySQL 使用 Innodb 存储引擎

消息使用 Protobuf 序列化

MySQL 存储消息的二进制形式,不解析内容

5. 质量设计

5.1.1 可测试性

proxy、catalog、broker 均为无状态服务,可以独立进行测试

5.1.2 成本

proxy,catalog,broker 均使用 k8s 托管,可动态扩容缩容,节省成本

5.1.3 可观测性 &运维成本

每个服务内部对关键指标打点,指标接入报警系统,建立指标大盘

6. 演进规划

6.1 消息队列一期

完成 proxy,broker,catalog,控制台基本功能,联调成功。建立报警和指标大盘。

6.2 消息队列二期

接入两个业务场景,协助业务将原有服务拆分,迁移到消息队列上

用户头像

Geek_2e7dd7

关注

还未添加个人签名 2018.11.08 加入

还未添加个人简介

评论

发布
暂无评论
基于MySQL存储的自研消息队列架构设计文档