Go 进阶训练营 – 评论系统架构设计二:详细设计

2022-10-31 17:43:23 浏览数 (1)

详细设计

comment-service

专注在评论数据处理(认真想下 Separation of Concerns 关注点分离,职责更清晰)。我们一开始是 comment-service 和 comment 是一层,业务耦合和功能耦合在一起,非常不利于迭代,当然在设计层面可以考虑目录结构进行拆分,但是架构层次来说,迭代隔离也是好的。

读的核心逻辑

Cache-Aside 模式,先读取缓存,再读取存储。

Cache Miss 处理流程
  1. 发送构建缓存的消息,利用kafka job异步构建评论当前页 后面几页的缓存。
    • 架构图里的回源逻辑
    • 预读,缓存超前加载,避免频繁 cache miss
    • 利用 kafka 串行消费的特点,同一个缓存key在job中会先判断是否已构建缓存,避免重复构建。并行的话容易导致OOM。
  2. 当前请求从DB查询当前页,量不大。
    • 避免缓存抖动时,特别容易引起集群 thundering herd 现象,大量的请求会触发 cache rebuild,因为使用了预加载,容易导致服务 OOM。
    • thundering herd:惊群现象,指多个进程同时获取同一个资源产生的问题,例如这里对DB的压力以及查询出来的大量数据导致应用OOM。

缓存对象不大时,或者构建缓存对象比较容易时,优先采用直接删除缓存,反正构建缓存的逻辑是有的,这样就不用再去写更新缓存的逻辑了。

写的核心逻辑

系统的瓶颈往往就来自于存储层。对于写的设计上,我们认为刚发布的评论有极短的延迟(通常小于几 ms)对用户可见是可接受的,把对存储的直接冲击下放到消息队列,按照消息反压的思路,即如果存储延迟升高,消费能力就下降,自然消息容易堆积,系统始终以最大化方式消费。

Kafka 是存在 partition 概念的,可以认为是物理上的一个小队列,一个 topic 是由一组 partition 组成的,所以 Kafka 的吞吐模型理解为: 全局并行,局部串行的生产消费方式。对于入队的消息,可以按照 hash(comment_subject) % N(partitions) 的方式进行分发。那么某个 partition 中的 评论主题的数据一定都在一起,这样方便我们串行消费。同样的,我们处理回源消息也是类似的思路。

一般不轻易对DB进行分库分表,而是才有成本更低的方式:利用架构设计解决。B站 DAU 上亿,评论系统就一个 MySQL 照样没问题。

comment-admin

mysql binlog 中的数据被 canal 中间件流式消费,获取到业务的原始 CUD 操作,需要回放录入到 es 中,但是 es 中的数据最终是面向运营体系提供服务能力,需要检索的数据维度比较多,在入 es 前需要做一个异构的 joiner,把单表变宽预处理好 join 逻辑,然后导入到 es 中。

有的字段是使用时才去查询的,例如用户昵称,由于比用户ID大,索引成本高,所以es只存了用户ID,列表展示时才去查用户服务进行转换。

那哪些字段需要导入时查询外部服务,存入es?哪些字段在实际使用时,再调用外部服务查询?主要考虑索引成本,仅展示的字段不需要索引,可直接存入es。某些字段需要索引,但字段大,索引成本高,且es里已存在可以映射的字段,这种情况下可以考虑使用时再去查询。

一般来说,运营后台的检索条件都是组合的,使用 es 的好处是避免依赖 mysql 来做多条件组合检索,同时 mysql 毕竟是 oltp 面向线上联机事务处理的。通过冗余数据的方式,使用其他引擎来实现。B站内部运营体系基本都是基于 es 来完成的。

es 一般会存储检索、展示、primary key 等数据,当我们操作编辑的时候,找到记录的 primary key,最后交由 comment-admin 进行运营测的 CUD 操作。

comment-interface

comment 作为 BFF,是面向端,面向平台,面向业务组合的服务。所以平台扩展的能力,我们都在 comment 服务来实现,方便统一和准入平台,以统一的接口形式提供平台化的能力。

  • 依赖其他 gRPC 服务,整合统一平台测的逻辑(比如发布评论用户等级限定)。
  • 直接向端上提供接口,提供数据的读写接口,甚至可以整合端上,提供统一的端上 SDK。
  • 需要对非核心依赖的 gRPC 服务进行降级,当这些服务不稳定时。

Post Views: 4

0 人点赞