文本描述
毛剑
Go 进阶训练营
第6课
案例 - 评论系统架构设计
功能模块
架构设计
存储设计
可用性设计
References
功能模块
bilibili/video/BV1Sa4y1H7cy
架构设计最重要的就是理解整个产品体系在系统中的定位。搞清楚系统背后的背景,才能做出最佳的设计和抽象。不要做需求的翻译机,先理解业务背后的本质,事情的初衷。
评论系统,我们往小里做就是视频评论系统,往大里做就是评论平台,可以接入各种业务形态。
发布评论: 支持回复楼层、楼中楼。
读取评论: 按照时间、热度排序。
删除评论: 用户删除、作者删除。
管理评论: 作者置顶、后台运营管理(搜索、删除、审核等)。
在动手设计前,反复思考,真正编码的时间只有5%。
功能模块
架构设计
存储设计
可用性设计
References
架构设计 - 概览
BFF: comment
复杂评论业务的服务编排,比如访问账号服务进行等级判定,同时需要在 BFF 面向移动端/WEB场景来设计 API,这一层抽象把评论的本身的内容列表处理(加载、分页、排序等)进行了隔离,关注在业务平台化逻辑上。
Service: comment-service
服务层,去平台业务的逻辑,专注在评论功能的 API 实现上,比如发布、读取、删除等,关注在稳定性、可用性上,这样让上游可以灵活组织逻辑把基础能力和业务能力剥离。
Job: comment-job
消息队列的最大用途是消峰处理,
架构设计 - 概览
Admin: comment-admin
管理平台,按照安全等级划分服务,尤其划分运营平台,他们会共享服务层的存储层(MySQL、Redis)。运营体系的数据大量都是检索,我们使用 canal 进行同步到 ES 中,整个数据的展示都是通过 ES,再通过业务主键更新业务数据层,这样运营端的查询压力就下方给了独立的 fulltext search 系统。
Dependency: account-service、filter-service
整个评论服务还会依赖一些外部 gRPC 服务,统一的平台业务逻辑在 comment BFF 层收敛,这里 account-service 主要是账号服务,filter-service 是敏感词过滤服务。
架构设计等同于数据设计,梳理清楚数据的走向和逻辑。尽量避免环形依赖、数据双向请求等。
架构设计 - comment-service
comment-service,专注在评论数据处理(认真想下 Separation of Concerns)。
我们一开始是 comment-service 和 comment 是一层,业务耦合和功能耦合在一起,非常不利于迭代,当然在设计层面可以考虑目录结构进行拆分,但是架构层次来说,迭代隔离也是好的。
读的核心逻辑:
Cache-Aside 模式,先读取缓存,再读取存储。早期 cache rebuild 是做到服务里的,对于重建逻辑,一般会使用 read ahead 的思路,即预读,用户访问了第一页,很有可能访问第二页,所以缓存会超前加载,避免频繁 cache miss。当缓存抖动时候,特别容易引起集群 thundering herd 现象,大量的请求会触发 cache rebuild,因为使用了预加载,容易导致服务 OOM。所以我们看到回源的逻辑里,我们使用了消息队列来进行逻辑异步化,对于当前请求只返回 mysql 中部分数据即止。
架构设计 - comment-service
写的核心逻辑:
我们担心类似“明星出轨”等热点事件的发生,而且写和读相比较,写可以认为是透穿到存储层的,系统的瓶颈往往就来自于存储层,或者有状态层。对于写的设计上,我们认为刚发布的评论有极短的延迟(通常小于几 ms)对用户可见是可接受的,把对存储的直接冲击下放到消息队列,按照消息反压的思路,即如果存储 latency 升高,消费能力就下降,自然消息容易堆积,系统始终以最大化方式消费。
Kafka 是存在 partition 概念的,可以认为是物理上的一个小队列,一个 topic 是由一组 partition 组成的,所以 Kafka 的吞吐模型理解为: 全局并行,局部串行的生产消费方式。对于入队的消息,可以按照 hash(comment_subject) % N(partitions) 的方式进行分发。那么某个 partition 中的 评论主题的数据一定都在一起,这样方便我们串行消费。
同样的,我们处理回源消息也是类似的思路。