直播房间服务基于CQRS的架构演进实践( 二 )


拆分目的是减少B端和C端之间领域穿插,双方更加聚焦各自的业务领域,最终闭环从而提升架构稳定性规避系统性风险,并提升各自业务域内的组织效率 。
业务拆分共识围绕房间实体,业务上有生产和消费的逻辑需要盘点,从BFF/基础房间服务/直播biz服务三层进行BC Domain的拆分 。

  • B/C两端,都具有完整业务领域,领域内有各自相对独立的业务上下文
  • App客户端/Web前端,属于多个领域共用的“视窗”
  • C端有两路数据流,一路是看端领域闭环的数据流,第二路数据流是B端的数据流(开关播上下文、房间状态变化上下文)

直播房间服务基于CQRS的架构演进实践

文章插图
图片
根据GRASP(General Responsibility Assignment Software Pattern)中的信息专家(Information Expert)模式,数据应该放在需要经常使用它的地方,同时某一个功能在哪里实现,取决于数据哪里 。换句话讲,数据+功能=领域 。
架构演进目标房间核心系统耦合了消费端和生产端的逻辑,基于CQRS理论需要将服务和数据库完全解耦,承担高流量的xroom/dananchor服务划分为C端业务域服务,新的B端服务闭环接受内容生产的读写请求和后台管控聚合请求(写多读少) 。
B端核心房间数据变更通过领域事件消息通知给到C端,C端关注数据的最终一致性,期间会有数据对账脚本主动发现数据不一致并自动修复 。
总体方向按照C&B职能原则来拆分,过渡阶段允许历史请求写请求C服务 , C proxy to B service 。
直播房间服务基于CQRS的架构演进实践

文章插图
图片
最终目标是完全解耦,通过领域事件数据流来同步必要信息 。
直播房间服务基于CQRS的架构演进实践

文章插图
图片
没有银弹我们享受到了CQRS带来的便利 , 相反的也要解决引入它带来的“副作用” , 这些副作用在直播领域下 , 表现的最核心无疑是开关播状态的延迟,但是由于用户和主播的天然隔离,反而不需要两边完全实时 。
主播开播后,需要推流等一些列的动作,直到用户可以看到主播的直播画面,这个过程中很自然,符合人的直觉,而在架构层面我们通过引入消息中间件来同步数据 , 本身耗时在毫秒级别,这相比前面的自然过程几乎可以忽略,但是我们的技术架构上服务和数据完全拆开了 。
同时因为引入了更多的数据交互环节,请求拓扑变得更加复杂,每个环节的数据正确性排查变得更困难 。我们通过平台提供的Tracing+Metrics+Logging来进行问题辅助定位 , 双写+对账脚本保障过渡阶段数据最终一致性 , 灰度阶段控制读写流量各自单独放量验证 。
为了应对CQRS架构带来的复杂性确实需要额外引入数据服务脚本等方式去做保障,这部分的思路更偏向于架构设计中的“风险驱动” 。
执行落地过程对于当前比较成熟的业务系统去做拆分,是一件比较有挑战的事情 。我们先从横纵两个角度看下房间服务所在的层级位置 。
 横向技术架构分层
  • 面向用户的终端设备:App粉版、Web端、开播App等
  • CDN -> SLB -> APIGW:内容分发边缘加速,LB层与统一网关
  • BFF:Backend for Frontend,根据终端渠道区分的业务网关入口,eg:app-room / web-room / app-interface
  • Biz Service:业务逻辑服务
  • Domain Service / Fundamental Service:业务域服务/基础服务,eg:Room Domain Service / Account Service
 纵向部署隔离
  • Region:eg sh/bj
  • Zone:eg sh001/sh002/sh003 , 每个Zone单元内的流量应尽可能闭环(读多活写回源 -> 读写多活、BFF failback cross Zone可选策略)
  • Cluster/Group:group1/group2/染色group,不同group可以设置服务发线上的weight权重
  • AppID:每个应用的服务发现naming id

直播房间服务基于CQRS的架构演进实践

文章插图
图片
可以看到房间服务有众多的请求上游,必须在读写切分过程中,保障好数据的一致性(B端业务域内强一致性,C端业务域内最终一致性)和服务的可用性(底层服务抖动会有放大效应) 。


推荐阅读