写点什么

自研集群 +MySQL 架构设计文档模板

用户头像
9527
关注
发布于: 2021 年 05 月 09 日

前言

在公司良好的发展情况下,游戏业务也是发展很快,跟游戏相关的系统也是越来越多。由于游戏业务发展带来系统数量和大小的增长,导致了系统间协作的效率开始变得低效,开发人员的工作量不断增大。为了解决现有的系统之间协作的低效问题和减低开发人员的工作量。公司打算自研游戏业务线的消息队列中间件系统,用于指导消息队列后续的开发,测试以及运维。


技术背景:

  • 中间件团队规模在 6 人左右

  • 中间件团队熟悉 Java 语言,但是有一个 C/C++技术高手

  • 采用 Linux 开发平台

  • 采用 MySQL 数据库

  • 目前整个业务系统是单机房部署

  • 公司刚刚被阿里巴巴收购,阿里的架构以及技术栈可以使用


1. 业务背景

现有问题

随着公司的不断发展壮大,公司的游戏业务也随之发展的很快。因此跟游戏相关的系统也是越来越多。由于游戏业务发展带来系统数量和大小的增长,导致了系统间协作的效率开始变得低效,开发人员的工作量不断增大。


例如在游戏的新版本发布的时候,首先游戏厂家更新游戏版本,然后公司的运营人员获取最新的游戏包,进行版本信息的更新,然后上传游戏包到包管理系统,接下来进行打包测试。当测试通过之后,运营子系统通知论坛进行发布预热。当测试通过,运营管理子系统要通知包管理系统进行打包。由于公司是一个游戏对接平台, 游戏对接有非常多的渠道,通常测试通过之后,运营人员需要打上百个不同的游戏渠道包,这极大的降低了游戏打包的效率。然后当游戏准点发布的时候,运营子系统还需要通知 App,Web 等站点进行更新,这里每一个更新操作,都需要去跟踪记录,一旦失败还需要重试或者创建失败任务记录,再等待进行处理。这样的整个新版本发布的过程由太多的环节和依赖,非常容易出错,也增加了工作人员的负担,影响了系统的稳定性和体验度。


现有游戏版本发布架构举例


可以看到各个子系统之间的依赖都是通过接口暴露,当运营子系统需要通知其它的子系统的时候,必须按照子系统的接口一个一个进行调用,还要考虑失败重试的机制。整个大的系统环境下,子系统的交互和依赖很多,效率很低。


目前现有架构存在的几个明显的问题:

  • 协作问题: 不同子系统之间依赖不同的 API 接口和协议进行通信,在当运营子系统需要通知其它的子系统的时候,必须按照子系统的接口一个一个进行调用,还要考虑失败重试的机制

  • 重复问题: 由于现有的游戏系统对接很多的不同渠道,每次发布新版本需要对每一个不同的渠道进行打包,很多重复性的操作问题

  • 效率问题:每个子系统提供的接口参数和实现都有一些细微的差别,导致每次都需要重新设计接口和联调接口,开发团队和测试团队花费了许多重复工作量。

基于以上的问题,团队决定引入消息队列,来进行系统解耦,把同步调用改为异步通知,完成标准化和统一话,来提高子系统之间的协作效率,降低开发团队和测试团队的重复工作量。

需求分析

5W 需求分析

  • Who: 消息队列系统主要是游戏平台的各个子系统来使用,子系统发送或者接受消息

  • When: 当运营子系统需要发送异步通知的时候,使用消息队列

  • What: 需要开发自己的自研集群+MySQL 消息系统

  • Where: 开发/测试/生产环境都需要部署

  • Why: 系统解耦,统一标准,将同步调用改为异步通知。

1H 业务流程

自研集群+MySQL 消息队列系统有两大核心功能:

  • 运营子系统发送消息给消息队列

  • 其余子系统从消息队列获取消息

2. 约束和限制

项目进度:

  1. Dev 环境的开发: 20xx 年 1 月~20xx 年 4 月

  2. QA 环境的部署: 20xx 年 5 月~20xx 年 6 月

  3. Prd 环境的部署: 20xx 年 7 月~20xx 年 8 月

  4. 现有架构的迁移:20xx 年 9 月~20xx 年 12 月

项目成本:

  1. 成本不能超过 200 万

数据库:

  1. 消息存储数据库采用 MySQL 主备双机

  2. SDK 轮询服务器进行消息写入

  3. SDK 轮询服务器进行消息读取

消息队列开发语言:

  1. Java 语言编写消息队列服务器

消息格式:

  1. Netty 自定义消息格式

  2. 支持 HTTP 接口


3. 总体架构

消息队列总体架构图

架构关键设计点:

  • 采用 Java 语言编写消息队列服务器

  • 消息存储采用 MySQL,表名就是消息队列的名字,里面存储方式为 key-value pair

  • 存储引擎采用 InnoDB

  • 采用数据分散集群的架构,集群中的服务器进行分组,每个分组存储一部分消息数据。

  • 每个分组包含一台主 MySQL 和一台备 MySQL,分组内主备数据复制,分组间数据不同步。

  • 正常情况下,分组内的主服务器对外提供消息写入和消息读取服务,备服务器不对外提供服务;主服务器宕机的情况下,备服务器对外提供消息读取的服务。

  • 客户端采取轮询的策略写入和读取消息。轮询时间大致为每秒一次。

消息队列架构图

MySQL 存储架构图


4. 详细设计

4.1 核心功能

客户端

  • 客户端采用 Java SDK 语言开发,基于 Netty 实现与服务端交互

  • 客户端与服务端采用 TCP 连接,采用 JSON 传递数据

  • 为了兼容非 Java 系统,客户端同时提供 HTTP 协议借口


服务器端

  • 服务器端基于 Netty 开发,采用 Reactor 网络模型

  • 两台服务器组成一个 sharding, 整个系统可以多个 sharding,每个 sharding 包括一主一从两台服务器

  • 主服务提供消息读写操作,从服务器只提供消息读取操作

  • 服务器基于 ZooKeeper 进行主从切换


存储端

  • MySQL 服务器采用主从同步

  • 每个消息队列对应一张表

  • 每一个消息表最多存储 30 天的消息,过期的转入线下备份

  • 直接用 MySQL 的主从复制来实现数据复制


消息队列

  • 消息发送流程

  1. 消息队列系统设计两个角色: 生产者和消费者,每个角色都有唯一的名称

  2. 消息队列系统提供 SDK 供各个业务子系统调用,SDK 从配置中读取所有消息队列系统的服务器信息,SDK 采取轮询算法发起消息写入请求给服务器

  3. 如果某个主服务器无响应或者返回错误,SDK 将发起请求发送到下一台主服务器,相当于在客户端实现了分片功能

  • 消息读取流程

  1. 消息队列系统提供 SDK 供各业务系统调用,SDK 从配置中读取所有消息队列系统的服务器信息,轮流向所有服务器发起消息读取请求

  2. 消息队列服务器需要记录每个消费者的消费状态,即当前消费者已经读取到了哪条消息,当收到消息读取请求时,返回下一条未被读取的消息给消费者

  3. 默认情况下主服务器提供读写服务,当主服务器挂掉后,从服务器提供读消息服务


服务器主从切换

  1. 同一组的主从服务器配置相同的 group 名称,在 Zookeeper 建立对应的 PERSISENT 节点

  2. 主从服务器启动后,在 Zookeeper 对应的 group 节点下建立 EPHEMERAL 节点,名称分别为 master 和 slave

  3. 从服务器 watch 主服务器的 master 节点状态,当 master 节点超时被删除后,从服务器接管读消息,收到客户端 DSK 的读消息后返回消息,收到客户端 SDK 的写请求直接拒绝

高可用设计

  • 消息发送可靠性

业务服务器中嵌入消息队列系统提供的 SDK,SDK 支持轮询发送消息,当某个分组的主服务器无法发送消息时,SDK 挑选下一个分组主服务器重发消息,依次尝试所有主服务器直到发送成功;如果全部主服务器都无法发送,SDK 可以缓存消息,也可以直接丢弃消息,具体策略可以在启动 SDK 的时候通过配置指定。

如果 SDK 缓存了一些消息未发送,此时恰好业务服务器又重启,则所有缓存的消息将永久丢失,这种情况 SDK 不做处理,业务方需要针对某些非常关键的消息自己实现永久存储的功能。


  • 消息存储可靠性

消息存储在 MySQL 中,每个分组有一主一备两台 MySQL 服务器,MySQL 服务器之间复制消息以保证消息存储高可用。如果主备间出现复制延迟,恰好此时 MySQL 主服务器宕机导致数据无法恢复,则部分消息会永久丢失,这种情况不做针对性设计,DBA 需要对主备间的复制延迟进行监控,当复制延迟超过 30 秒的时候需要及时告警并进行处理。


  • 消息读取可靠性

每个分组有一主一备两台服务器,主服务器支持发送和读取消息,备服务器只支持读取消息,当主服务器正常的时候备服务器不对外提供服务,只有备服务器判断主服务器故障的时候才对外提供消息读取服务。

主备服务器的角色和分组信息通过配置指定,通过 ZooKeeper 进行状态判断和决策。主备服务器启动的时候分别连接到 ZooKeeper,在 /MQ/Server/[group] 目录下建立 EPHEMERAL 节点,假设分组名称为 group1,则主服务器节点为 /MQ/Server/group1/master,备服务器的节点为 /MQ/Server/group1/slave。节点的超时时间可以配置,默认为 10 秒。


高性能设计

根据分析,目前的系统并不需要太高性能,并且性能要求不是业务的核心要求,主要是针对内部的各个子系统之间的协调和协作,自研集群+MySQL 存储能够满足现有要求。


可扩展设计

按照需求进行主从服务器 pair 的增加即可


安全设计

消息队列系统需要提供权限控制功能,权限控制包括两部分:身份识别和队列权限控制。

  • 身份识别

消息队列系统给业务子系统分配身份标识和接入 key,SDK 首先需要建立连接并进行身份校验,消息队列服务器会中断校验不通过的连接。因此,任何业务子系统如果想接入消息队列系统,都必须首先申请身份标识和接入 key,通过这种方式来防止恶意系统任意接入。

  • 队列权限

某些队列信息可能比较敏感,只允许部分子系统发送或者读取,消息队列系统将队列权限保存在配置文件中,当收到发送或者读取消息的请求时,首先需要根据业务子系统的身份标识以及配置的权限信息来判断业务子系统是否有权限,如果没有权限则拒绝服务。


其他设计

  • 消息队列系统需要接入公司已有的运维平台,通过运维平台发布和部署。

  • 消息队列系统需要输出日志给公司已有的监控平台,通过监控平台监控消息队列系统的健康状态,包括发送消息的数量、发送消息的大小、积压消息的数量等,详细监控指标在后续设计方案中列出。


5. 部署方案

消息队列系统的服务器和数据库服务器采取混布的方式部署,即:一台服务器上,部署同一分组的主服务器和主 MySQL,或者备服务器和备 MySQL。因为消息队列服务器主要是 CPU 密集型,而 MySQL 是磁盘密集型的,所以两者混布互相影响的几率不大。

硬件的基本要求:32 核 48G 内存 512G SSD 硬盘,考虑到消息队列系统动态扩容的需求不高,且对性能要求较高,因此需要使用物理服务器,不采用虚拟机。


6. 演进规划


整个消息队列系统分三期实现:

第一期:实现消息发送、权限控制功能,预计时间 3 个月。

第二期:实现消息读取功能,预计时间 1 个月。

第三期:实现主备基于 ZooKeeper 切换的功能,预计时间 2 周。

开发到生产环境的实现规划:

  1. Dev 环境的开发: 20xx 年 1 月~20xx 年 5 月

  2. QA 环境的部署: 20xx 年 6 月~20xx 年 7 月

  3. Prd 环境的部署: 20xx 年 7 月~20xx 年 8 月

  4. 现有架构的迁移:20xx 年 9 月~20xx 年 12 月


发布于: 2021 年 05 月 09 日阅读数: 61
用户头像

9527

关注

还未添加个人签名 2020.04.22 加入

还未添加个人简介

评论

发布
暂无评论
自研集群+MySQL架构设计文档模板