品牌广告投放平台的中台化应用与实践
01
02
「复用的不同层次」
依据可复用结构图的复杂性,从低到高依次划分为:代码复用、模块复用、服务复用、产品复用。
1、代码复用,在此处窄化为函数、方法、或类的复用,代码是复用的最低层次,也是服务质量的基石。复用的代码通常无法脱离自身服务提供独立完整的功能,不具有模块的规模
2、模块复用,也叫组件复用,模块是基于功能划分的单位。模块复用比代码复用高一层级,是因为它通常可以提供较为独立的功能。
3、服务复用,这里的服务复用可以分为两种:3.1 将2中可复用的模块直接抽离成微服务,单独提供功能。3.2 将被多场景用到的业务能力,抽离为微服务,做为底层业务能力。
4、产品复用,产品是对外交付的最终的成果,产品复用是项目体系内最大程度的复用。较为实际的一种产品复用是指:可以直接设计出可组装的产品模块,通过对模块的组装生成不同形态的产品。
03
04
「中台化和微服务的关系」
中台是一种企业治理思想和方法论,微服务是技术架构方式。中台化的落地,需要使用微服务架构。微服务化是践行公司中台理念的一种技术实现手段。
05
5.1 研发效率
「系统复杂度」
「开发并发度」
「沟通成本」
5.2 技术架构
「高并发」
「高可用」
「可运维」
5.3 组织结构
06
6.1 团队组织架构,每个微服的owner是否明确
6.2 微服务拆分粒度
这个难点在品牌广告的微服务实践中解决的比较好,团队在 cpt、cpm、cpm_gd 等合约类广告售卖模式上深耕多年,对相关领域的业务模型有深刻的理解。因此,微服务的拆分按照投前、投中、投后划分为7大业务中台,各业务中心所承担的职责按照业务模型进行边界划分,不同服务负责业务实体的不同生命周期,职责明确、边界清晰。
6.3 自动化构建部署以及可测性
微服务化后,服务数量比原来单系统要多的多,在rd联调和qa测试中,不同环境下的多服务部署非常令人头疼。比如要测试某次迭代,需要完整部署七大业务中台和一个业务系统,大约涉及10+模块。同一时间若有其他业务迭代也需要测试,那么同样需要部署一套10+模块的完整环境,且不同产品的测试环境需要互相隔离(例如消息、db等),如果没有自动化构建部署的工具,手动逐个模块部署将耗费大量人力。依托公司的基础设施可以快速搭建一套完整的拓扑环境,支持不同场景的测试需求。
6.4 微服务治理
微服务治理包括服务的变更(包括:发布、升级与版本管理)、服务的发现、服务的健康检查(包括:日志、监控与报警)、服务的故障处理(包括:)等,正如前文所述,整个微服务的治理均托管至部门级的基础设施,在此就不做赘述了。
6.5 微服务和分布式能力紧耦合
上图是微服务体系的逻辑架构,由两部分组成(紫色部分是集成在微服务中的提供分布式能力的共享类库和网络客户端):
内层架构(图中浅蓝色部分),是每个微服务的实现架构;
外层架构(图中黄色部分),是构建强大微服务架构所需要的各种能力,即分布式能力,如服务注册发现、消息中间件、redis等key-value存储、数据库、日志监控追总系统等。
微服务和分布式能力的强耦合会增加运维的复杂性(如提供分布式能力的各客户端版本)和服务本身的体积(某些微服务本身可能规模很小,但是需要引入的外层分布式能力的客户端相比之下就规模惊人了)。由于微服务的解决方案采用的是部门级的基础设施,目前一部分已经做到了无侵入,比如使用凤睛实现了业务无侵入的微服务监控。
6.6 分布式事务
从单体应用改造成微服务化后,势必会引入分布式事务的问题。针对不同的业务场景,主要落地了2种解决方案。
同步方案
如图所示,品牌广告下单过程中的典型业务场景,冻款 + 锁库存,在rpc的调用过程中可能会遇到如下问题:
冻款成功,锁库存失败
冻款成功,锁库存下游成功(黄色检索端服务),上游超时(蓝色资源中心)
冻款和锁库存均成功,业务方失败
冻款+锁库存的操作主要发生在客户下单的时候,从业务方的视角来看是同步调用服务,其结果会影响到主业务服务的抉择。针对此类执行时间确定且较短的业务场景,采用了通用型TCC的思想,落地了分布式解决方案BTCC。下图是BTCC的架构图:
异步方案
如图所示,业务场景是客户在公示期交付标的的保证金(T+1 0点截止),获取竞拍资格,然后T+1的10点可以参与此标的的竞拍。对于业务方来说,需要调用两个服务的rpc接口,冻款 + 确认竞拍资格。但此类业务场景对时间上并无实时要求,对于客户来说T+1的10点才能感知到竞拍资格,因此可以采用异步化的解决方案,本地消息表。同时配上定时任务,监控未被成功处理的消息,重试发送,起到了兜底方案。
6.7 RPC调用替代进程内方法调用
微服务架构改造过程中,在遵循领域模型将现有单体应用按照业务边界拆分为多个微服务时,往往选择用 REST 或者 RPC 等远程调用方式简单替代原有的进程内方法调用。
在微服务之前:应用程序由多个耦合在一起的模块组成,这些模块通过内存空间进行方法调用;
在微服务之后:应用程序由多个耦合在一起的微服务组成,这些微服务通过网络进行远程调用。
抛开调用方式的差异来看微服务化前后的系统架构,会发现两者几乎完全一样。如下图所示:
而微服务版本在某些情况下可能表现的更糟糕:因为调用方式更脆弱(网络远比内存不可靠)。而我们将网络当成 “胶水” 来使用,试图把分散的业务逻辑模块(已经拆分为微服务)按照单体时代的同样方式简单粘在一起,这当然比单体在同一个进程内直接方法调用更加的不可靠。也就是说,简单用远程调用替代进程内方法调用,服务间的强耦合并未得到改善,反而让调用方式变得更加脆弱。
解决方案是引入「事件」(Event),解除不必要的强耦合。结合领域模型的设计理念,将Event 理解为领域中已经发生的事情:通常意味着有行为(Action)已经发生,有状态(Status)已经改变。RPC的调用方式更加像「命令」(Command),用于传递一个要求执行某个动作(Action)的请求,代表将要发生的事情。
下面举一个使用 event 机制的实际原理,同时对比command方式,优势显而易见。
下图是使用RPC调用的方式(以进入竞拍期为例),核心业务流程:创建标的 --> 标的审核 --> 标的报价 --> 标的进入公示期 --> 标的进入竞拍期 --> 标的竞拍中 --> 标的竞拍结束 --> 下单
和RPC调用方式相比,服务之间解耦了,不再有循环依赖和调用的情况,做到了「高内聚,低耦合」的设计理念。同时,基于实体生命周期的监控更加自然,任何环节有问题或者延迟,都会在第一时间发现,服务的稳定性和线上运维效率大幅提升。
GEEK TALK
07
业务组件的沉淀
介绍完微服务化改造后,再来聊聊中台化实践中,对于某个服务而言,如果做到更好的复用性。这里提出「业务组件」的概念,业务组件是指一类能够被复用的独立业务功能,从粒度上看,可以比功能模块更细。比如竞拍中心比价服务中的标的排序策略;资源中心的询量、锁量功能;订单中心的冻款、解冻功能等。下图解释了业务组件的使用,概括来说,业务组件本身不和产品线直接关联,从组件功能本身来定义边界,即做到高内聚,业务组件定义的越抽象,未来复用的可能性就越大。然后在应用层,针对不同产品线装配组件,这个步骤可以抽象为配置化。
对于业务中台来说,需要支持多类产品线的需求,各产品线之间有相似的逻辑也有自己定制化的,而且对于业务模块来说,唯一不变的就是变化。那么在这种多变且需求不可预知的场景下,如何预知哪些业务能力未来可能被复用,需要抽象为业务组件呢?个人认为在实际开发中,不强求一次就能设计出通用的业务组件,而是采用实用主义的态度,即只对那些需要在多个场景中被复用的能力进行抽象下沉,而不需要复用的,就暂时放在各产品线独有的处理逻辑中。具体如下图所示(不同的use case可以看作是不同的产品线逻辑):
按产品线隔离和抽象业务组件各有利弊。前者可以最大程度降低产品线之间的耦合,各产品线的升级迭代相互独立,降低风险。但是没有深入挖掘产品线之间的共性,和中台化的理念背道而驰。业务组件的方式更加贴近中台化理念,最大化通用能力的复用性。但同时风险也更大,一个业务组件的功能升级会影响所有产品线,所以对业务组件进行抽象和定义的合理化程度直接决定了升级代价和风险。从这个意义上来讲,定期的局部模块重构显得尤为重要。
GEEK TALK
08
总结
本文首先介绍了品牌广告投放平台技术架构的历史变迁,从1.0到3.0版本的演进过程和背后的驱动因素。接着结合实际业务,重点介绍了微服务化改造中碰到的难点和应对方式。最后,在践行中台化理念的过程中,探讨了通用能力下沉的时机和方法,同时结合业务组件的概念,使各产品线在流程独立和能力复用中找到平衡。
END