1. 引言
近年来,达人分销模式推动电商销售额呈现爆发式增长,其核心在于连接品牌商家与具有影响力的达人,通过内容与商品撮合进行精准推广触达消费者,实现流量变现和GMV快速增长。B站达人带货业务发展快速,从系统平台层面来看,面临诸多挑战:
- 历史系统腐化影响:复杂业务场景支撑如(直播/视频/图文跨场景不同资源位的带货分销玩法)及智能托管模式探索受到一定程度限制
- 协同成本相对较高:历史模型分散且不稳定,业务变更会频繁引发上下游协同,迭代效率往往不达预期
- 平台扩展出现瓶颈:现有系统存储耦合等技术债难以支撑亿级数据量读写,无法匹配当前业务规模化发展
因此,在带货业务持续保持高速增长的阶段,构建高效稳定且具有较高扩展性的达人分销系统尤为重要。本文将详细介绍UP主带货分销平台现状,基于领域驱动设计(DDD)思想,如何对现有系统进行平台化重构,实现分销系统整体架构的升级。
2. 现状&目标
2.1 现有系统痛点
B站带货分销业务涉及直播、视频、图文等场域,当前已覆盖大部分资源位场景。业务快速发展的同时也历经团队组织的调整,形成了如下图所示的系统调用链现状。以用户最基础的操作场景为例,UP主在分销平台选择不同内容不同资源位进行带货,由原来单一视频场景扩展到图文、直播带货场景后,需要关联多个应用多种数据模型,超过数十张数据表的读写,很小业务迭代和变更都会涉及不同应用间的联动开发,同时会引发下游不同团队(如引擎、数据等)协同变更支持,额外增加用户端带货服务体验治理难度。
实际上的情况远比上图所示链路复杂,从整体看,核心问题主要有以下几个方面。
-
架构层面扩展性低,烟囱式系统导致3套独立数据模型、2种开发语言并存,维护成本和迭代升级瓶颈大,复杂业务的支撑变的越来越困难。
-
服务耦合度高,系统历史债重,多个业务深度耦合,逻辑分散在多个模块,部分接口交织,改动会直接影响底层调用及下游依赖(引擎/算法),系统稳定性无法闭环。
-
存储瓶颈严重,当前存储单表模式,核心数据表量级和日均慢查数均已远远超出上限,面对开闭环分销业务快速发展,难以承载接口性能和未来带货业务增量需求。
2.2 平台化目标
为从根本上改变系统现状,解决以上痛点,需要重构系统架构,推行带货分销系统平台化升级,首先确立了几个重要目标。
- 提高系统扩展性和可维护性:建立稳定的带货分销业务数据模型,抽象服务和流程,方案可覆盖现有直播、视频、图文场域20多种带货场景。
- 独立链路核心业务:通过DDD划分领域,根据领域边界拆分业务域,升级系统架构,实现核心业务内聚,关联域解耦。
- 提升系统稳定性:通过存储隔离及分库分表,升级中间件,统一业务框架和研发约定,实时感知观测系统异常。
面对系统多年历史的积累,黑盒面大,如何整体推动是个比较大的问题。根据系统预演和目标优先级,制定了分阶段的演进策略,如下所示,后文将会进一步介绍平台化实践的一些关键过程和关键问题。
第一步:建立统一模型和落地onebp方案,拆分解耦并迁移视频带货业务
第二步:迁移图文/直播带货应用至新模型,完成域内业务与服务统一
第三步:域外统一切至带货分销新模型,一套模型N种业务场景,实现上下游一致表达
3. 业务建模与设计
分销带货业务域相对不太聚焦,包括不仅限货品管理、分销关系、人货场撮合、归因跟踪、分销结算等。从全局视角看带货分销业务,主要参与角色由商家、服务商、UP主、消费者等,活动在不同场域,当前业务阶段UP主是最核心的分销者,如下图所标示区域。
本次平台化升级一方面需要完全覆盖重构的几个核心目标,另一方面需要充分结合业务现状和团队开发人员的经验情况,如何有效结合业务现状和长期演进进行建模是比较关键的问题。领域驱动设计(DDD)作为微服务的主流设计方法,是通过领域模型捕捉领域知识,使用领域模型驱动设计,主要目标是解决复杂业务系统中的设计难题,提高系统的适应性、可维护性和开发效率,与当前业务阶段匹配。基于整体考虑,采用该设计思想,去推进整体平台化实践,是比较有效的方式,但整个实施路径不严格按照DDD方案论推进,优点在于:
-
能够帮助团队统一语言,捕捉复杂业务逻辑,可以很好解决当前分销业务规则混乱以及场域多变依赖子域复杂的现状。
-
通过领域划分、聚合和限界上下文的设计,可以解耦系统,提高可维护性和扩展性
-
结合实际考虑聚合事务边界,降低建模复杂度,兼顾团队在经验上的缺乏和ROI,避免贫血模型或过度设计的反模式,很容易地实现架构演进
3.1 边界划分
从业务视角出发,按照上文中提到的各角色的用例动线,通过分析分销业务流程和所有用例(局部用例图如下图所示)去识别业务域,进而划分子域,定义模型关系。
识别出系统中的主要业务域,并划分出核心域(关键业务)、支撑域(辅助业务)和通用域(共享业务)如下图。
3.2 领域模型
领域模型反映了业务领域中的实体、值对象、聚合、工厂和存储库等。在分销业务中,通过构建抽象模型,相对稳定描述和解决业务问题。对于UP分销任务,关联了内容载体(视频/直播/图文等)和层级以及投放资源位信息,同时分销者(UP主)多分销任务可以被一个分销计划管理,内容层级、资源位和商品(含虚拟品)核心信息构成了投放的最小单元,该模型可以表达如下。
说明:
- 内容:内容载体(稿件、图文、专栏、直播等)
- 资源位:每个内容载体有多个资源位,如视频有浮层、弹幕、框下、评论、图文等
- 商品:每个资源位可以挂多个商品(开环商品、闭环商品、虚拟品等)
- 分销任务:带货分销单元,可关联内容资源位和多个商品
- 分销计划:由若干分销任务构成,用于任务单元管理层级的扩展
任务单元对象关系如下,需要说明的是,样式Object按静态类处理,用于模型的映射,支撑组件样式在消费者的展示。
3.2 事件驱动
领域事件通过将业务动作显式化,促进系统的高内聚、低耦合,是构建复杂业务系统的有效模式。在分销域中,当分销任务聚合根状态及核心信息发生变化时生成事件,保障任务事件与业务逻辑的紧密关联性。在设计时,考虑了全局事件,比如创建分销任务的事件定义如下
该事件通用消息报文定义如下:
3.3 状态机
带货场域分销状态主要在于分销任务状态的流转,如下图所示,分销带货任务从草稿态开始到任务终态(包含删除和审核不通过两种状态)。
业务阶段状态码(终态、可更改态、未完成态)分别定义如下:
swift
// 完成态
public static final List<Integer> END_STATUS = Collections.unmodifiableList(Lists.newArrayList(
DELETED.code, REJECT.code
));
// 未完成态
public static final List<Integer> NOT_FINISHED_STATUS = Collections.unmodifiableList(Lists.newArrayList(
DRAFT.code, AUDITING.code, REJECT.code, LAUNCHING.code
));
// 可更改态
public static final List<Integer> CAN_UPDATE_STATUS = Collections.unmodifiableList(Lists.newArrayList(
DRAFT.getCode(), LAUNCHING.getCode(), AUDITING.getCode()
));
3.4 存储设计
3.4.1 分库分表方案
在完成业务建模后,需要考虑存储的整体设计。上文现状部分已提到过单表数据量过亿、数据模型不能完全表达分销业务(和其他业务存储耦合)、慢查询数量日均十万级的情况,系统基本已无扩展性。总结下来,可采用的方案主要包括分库分表、分区表、冷热分离和分布式数据库等4种方案,从实际的业务发展来看,QPS/TPS在评论等场景中持续增长,为有效应对单表亿级数据的存储与性能挑战,同时为后续业务扩展预留弹性空间,选择分库分表来做基础存储设计,那么问题来了,分库分表策略是什么?
通常分库分表可以分为水平分片和垂直分片,不过通常分库分表更多指水平分片,也就是将数据按某种规则分布到不同的库或表中,常见的分片规则比如哈希取模、范围分片、一致性哈希、按日期或时间分片、枚举分片、地理位置分片、复合分片等。回到业务用例场景,需要支持分销者id(account_id)和分销任务id(task_id)多维度的查询,复合分片能很好解决,假如按4库16张表为例,按照如下规则,那么根据task_id和account_id都能满足大多数业务用例的查询了。
3.4.2 中间件选型
确定分片策略后,进一步考虑分库分表中间件方案,这里给出了业内常见中间件的对比分析,可参见下表。需要说明的是,这里没有提及TDDL和DRDS等商业或非开源方案,涉及资源依赖等问题。表中Akso-Proxy是B站分库分表中间件的一种解决方案,目前对于不规则的分片策略暂未支持。在选型的考虑上有两个点相对比较重要:
- 能支持复合分片的方案诉求,也即支持指定库/表查询(HINT),支持多字段分库分表
- 轻量级低成本接入,能匹配项目节奏及部门当前基建的应用维护现状
Sharding-JDBC是相对轻量级java框架,使用客户端直连数据库,无需额外部署和依赖,可被视为增强版JDBC驱动。目前在部门交易域使用比较广泛,也集成到脚手架,运维升级可以统一管理,成本基本可忽略,结合优势和不足的权衡,Sharding-JDBC是当前阶段最为合适的选择。
在实践过程中,定义了分库分表策略,继承AbstractShardingAlgorithm,依赖关系如下,实现中间中ComplexKeysShardingAlgorithm中的interface方法doSharding,在 doSharding 方法中,根据分片键计算目标数据库和数据表,核心接口定义与基础实现如下。
arduino
public class DatabaseShardingAlgorithm extends AbstractShardingAlgorithm {
@Override
public String doSharding(String shardingKey) {
// 根据分片键计算目标数据库
int hash = shardingKey.hashCode();
int databaseIndex = Math.abs(hash % N); // 假设有N个数据库
return "db_" + databaseIndex;
}
}
4. 架构模式
软件架构从单机、集中式到分布式微服务架构经历了三个阶段的演进,每个阶段都以提高系统响应为目标,通过分离复杂度来实现业务优化。传统的软件架构大多都是三层架构,如下图所示,解决了程序内代码间调用复杂、代码职责不清的问题。但依然属于逻辑分层概念,核心问题就是技术建模和业务需求存在视角差异,随着项目的迭代演进,各层级与各模块之间可能存在交叉引用。从分销系统现状来看,属于典型的传统三层架构模式。
DDD(领域驱动设计) 是一种处理高度复杂领域的设计思想,可以有效分离技术实现的复杂性,围绕业务概念构建领域模型来控制业务复杂度,非常适合微服务架构的演进。其分层架构设计包括用户接口层、应用层、领域层和基础层,如下所示。每层都扮演着特定的角色,通过严格的分层原则实现松耦合,解决了三层架构中核心业务逻辑混乱、代码改动相互影响大的问题,大大简化持续性升级和维护。相比较六边形架构、洋葱架构、CQRS架构等,其实现复杂度相对较低,作为当前业务架构解决方案更有效。
4.1 分层架构
如何从三层架构向DDD四层架构演进,需要根据建模阶段,重新归类要素、重新划分层次、重新确定交互规则和职责边界,主要集中在业务逻辑层和数据访问层的划分,在实践过程中分销服务分层演进过程和常见演进方式类似,如下图。
在设计视角上,根据业务功能归属,提前做好不同业务领域的拆分。在技术实现上,通过实现与接口分离,在 application 与 infrastructure 之间新增 domain 领域层,让domain层尽量独立,不耦合与任何模块,下图描述的是分销平台化从三层架构现状升级到四层DDD架构的实践情况。
四层架构在平台化项目工程中具体描述如下表所示
具体的依赖调用关系以分销任务单元的生命周期为例,如下图所示。这里需要指出的是,domain领域层非必须依赖调用层,通常在业务服务表达中,简单业务非核心领域的调用可以直接跨过,这样简化了非核心领域的调用层级,灵活性高。
4.2 规范约定
确定分层架构后,需要进行相关技术层面的代码约定和规范,主要是异常处理方案和错误码规范,这些作为系统平台化可观测能力的基础条件。
对于异常处理方案,每一层(包括系统内/系统间)仅捕获当前层处理的异常,跨系统间不抛出异常,默认使用通用返回对象+错误码的形式管理。
对于错误码规范,按照8位约定,基础枚举定义如下。在规范要求中,需要统一遵循i18n,命名规范统一约束成 {平台域/业务域}-{领域}-{系统名缩写}-{错误类型}-{错误码数字编号}。举个例子,在分销任务业务用例中,敏感词稿件评论分销任务创建失败,那么错误码则设定为:81211001,这样就对上下游和域内错误异常进行了全局的定义,可快速识别平台异常根因。
5. 系统迁移策略
5.1 切流方案
切流和数据迁移这两个部分是大项目中的关键,风险很高。切流指的是将流量从旧系统切换到新系统,而数据迁移则是将旧数据转移到新系统中。这两者都可能遇到很多问题,比如数据不一致、服务中断、兼容性等问题,对于耦合依赖重的系统,更需要考虑切流与迁移成本以及整体项目节奏和进度,这里面涉及众多关键问题。
5.1.1 双向同步
在分销系统中,涉及相当多的上下游,比如引擎、算法、数据以及其他业务方依赖,实际过程中不可能做到各方节奏上同步性,因此多套老模型老库表的业务数据和新模型必须保持高度的一致,否则就会出现线上故障。当部分切流后,命中的用户会请求到新系统,数据会直接写新,同时异构写老,要做到对依赖方无影响;同时存在较多场景需要读取和变更该用户的存量数据,需要将老模型数据同步异构至新模型库表,当然后者在切流100%后就不再需要了。所以从整体看,数据流是双向同步的,这也决定了平台化升级切流和数据同步就具备较高的复杂性。
历史业务数据区分度较低,改动也将会影响其他的业务方,如何进行双向同步?同时还要保障成本最低、风险最小?这里面有几个关键性切流设计:
-
分销任务单元的变更仅在一端发生:一旦数据同步期间发生意外,以发生变更侧的状态为准,而变更侧定义为数据产生端。这里在老库表新增新模型的唯一id,在新库表新增ref_id,如果unique_id或ref_id非0,则表示当前的记录是从对侧同步过来的。
-
变更操作按新系统是否存在数据记录来判断:灰度期请求入口均在老系统,就可能存在两套id,变更操作请求的处理,若新系统能查到,则创建正常处理,否则拒绝。
-
binlog循坏问题检查:数据变更是不是因同步而改变,新增辅助flag字段,用于检查同步流向。
5.1.2 实施方案
这里选择视频带货服务和平台化升级后的新服务来说明整个切流方案的实施过程(图文带货和直播带货独立服务均采用类似方案),其中mas代表视频分销应用,cbp代表新应用。
第一步,视为初始状态,业务依旧100%在老系统。这里会有有两个前置处理:
-
mas数据库同步cbp,也即完成mas数据的存量快照搬迁和mas数据的增量同步
-
cbp准备好,完成CbpToMas的部署和开关配置,不接入实际流量
第二步,开始切流阶段,白名单的用户走新系统,非白名单用户走老系统。其中,编辑/读取任务,取决于任务初始是在mas还是在cbp创建的 ,cbp里创建/编辑的任务会同步到mas。
第三步,切流100%阶段,所有用户创建任务都切到cbp,后续mas系统不再创建任务,因此mas的binlog里应该不再有INSERT,而编辑/读任务切流还根据任务的初始创建系统走。观察稳定后,会有个后置处理,翻转refId,让所有的请求均走新服务。
第四步,依赖方迁移,依赖老系统接口业务方,可以直接切换cbp域名完成低成本切换,完成新老服务平缓切换。
5.1.3 回滚预案
分销平台化升级切流环节涉及数十服务和不同模块,外部系统依赖性强,需要制定应急回滚预案,确保系统的高可用性,尽可能降低对用户对业务的负面影响,这里采用了系列前置动作。
-
异常感知:切流过程,出现异常,在数据层和服务层通过对账和流量回放等方式自动触发异常告警
-
动态配置:异常一旦发生,切流不符预期,可动态调整切流比例,配置化快速回滚,流量重新回到老系统,控制风险;
-
修复工具:当切流产生异常数据,提供快速修复工具,进行数据补偿处理
-
回滚预演:上线前多次注入切流异常,进行预演验证,实际上10min内即可完成数据与应用的回滚处理,结合小流量切流比例,影响相当小
5.2 数据一致性保障
切流和数据同步过程对数据一致性要求比较高,一旦新老模型数据不一致,就会产生系统故障,这对模型对账也提出了更高的要求:
- 完整性:确保所有数据(全量或增量)均从旧系统迁移到新系统,无遗漏。
- 一致性:新老系统的数据内容(字段值、格式、关联关系等)完全一致。
- 业务正确性:迁移后的数据能支持业务逻辑的正常运行
除了离线全量对账作为基础,同时也设计了增量全套实时对账流程,如下所示。
过程中的数据同步问题及时监控和告警,在上线前不断演练,不断修正历史数据和不一致性问题,约99%潜在问题都能被发现和处理,如下实时告警。
另外结合回滚预案提到的恢复工具和回滚机制,充分控制切流迁移风险,最终保障项目稳定切流,完成了亿级数据全量迁移,期间域内和关联上下游未出现故障问题,系统切流过程高可用质量超出预期。
6. 总结与展望
6.1 阶段总结
本文全面介绍了UP主带货分销系统平台化升级的实践过程,最终完成了业务技术重构和代码结构优化。在整个推进过程中,充分考虑了业务发展及系统当前/未来的基础平台能力,如下图所示,提出了平台化升级的长期目标。
通过结合DDD基本设计思想,实现业务域划分和分销领域模型和事件的定义,然后在存储和架构分层上,提出了符合业务和团队现状的实践方案,最后设计了稳定且低成本的切流迁移方案,完成了平台阶段性升级目标,并达成如下预期成效:
- 系统扩展性和可维护性增强,核心模型可覆盖视频/图文/直播现有20多种带货场景,新场景扩展接入从1-2周降至天级,维护成本大大降低
- 核心业务域完成部分拆分,实现了广告/主站/结算/数据/引擎等众多支撑域的解耦
- 系统稳定性提升,新服务异常感知提升至分钟级,慢sql几乎未出现,核心模型接口读写RT下降4倍左右(如任务单元创建从200ms+下降至40ms+)
这里需要提出的是,过程中引入DDD相对传统架构而言,可以帮助从业务角度更合理地划分系统和业务边界,找到并改进现有架构中的问题。但在初期的架构实现上存在更高的实现成本和复杂度,因此需要做到在合适的场景中选择合适的方案,结合实际情况会更为有效。另外平台化升级不仅是技术层面的重构迭代,更是组织模式与业务思维向前走了一大步,整个项目团队积累了一定的实践经验。在系统稳定性提升、复杂业务场景快速接入、资源利用率优化等方面也达成了阶段性成果。另外,也还有未完成的部分在途持续推进,比如直播场域带货分销模型迁移、商品等支撑域解耦等。
6.2 未来展望
从业务域应用架构现状来看,如下图所示,系统应用调用关系尚较为复杂,长期的治理和平台化的目标是保持一致的。后续随着系统平台化各域的深入演进,将会进一步推动各子域和上下游,从分销全局视角获得最优解。
而分销业务域的平台应用架构将也会不断升级,从现有的20多个应用整合缩简至几大核心应用,整体演进规划如下图所示,可以看到还有相当多的挑战,需要在实践中逐一去解。未来平台化技术深入方向也将会以用户价值为核心,持续提升用户体验,支撑产品化能力,深化生态合作,充分优化组织的迭代流程,不断探索平台智能化(AI托管等),确保在快速增长和变化的业务探索中保持分销平台的领先性。
-End-
作者丨宇陌、Bingo、Alex、忍者等