淘宝商品详情平台化思考与实践
该文章来自阿里巴巴技术协会(ATA)精选集,(部份整理自《Detail2.0总体方案-20140818》)
1. 现状背景
Detail当前的问题可参见《Detail2.0介绍》(2014年7月),本文不赘述。而Detail新平台的目标是提升协作效率/稳定性/扩展性,倡导商品详情业务归一,能横向复用在其它Detail也能运用在非Detail场景。从产品到研发等各维度均展开梳理和重构,采取模块化、SDK/API等方式来定义协作和扩展机制,并提供合成和分组两种部署模式,以应对创新业务快速多变的需求。并在稳定性和性能方面进行统一封装管理和工具的插件式注入,使之常态化和标准化。
2. 名词定义
什么是模块
模块是具备可部署、可管理、可重用、可组合、无状态等特性,遵循统一规范且提供对外接口的软件单元。
什么是平台
平台是一套完整的技术体系和架构,具备可重用、可维护、可扩展、可协作等特性,能规范研发阶段的各项工作,最终支撑物种多样化、个性化等业务目标。
模块与平台
可以发现模块的特性与平台所追求的特性是一致的。模块是微观的具体实现单位,而平台则是宏观上的体系架构。模块化是构建平台的基本元素和方法,故两者并不冲突。以模块化理念解决当前面临的各项问题,对系统进行模块改造,再对模块库进行组织、调度,并提升除研发外其它各维度的能力以实现最终平台化。
3. 设计理念
(仅针对当前场景,普适原则不赘述)
分而治之
是解决复杂问题的有效方式,模块化便是具体的实现方法。模块更具可重用性、可维护性,控制复杂度、易于理解等,特别是共建(多团队协作)和需求多变的场景下。而当今Java平台,无论OSGi或Jigsaw等都还在发展中(细节不赘述) ,但模块化理念和模式是通用的,只要对系统进行良好的设计和拆分,无论采用何种模块化方法都能受益。所以,自主设计一套轻量级模块规范是本次平台化的切入点。
框架治理
事实证明,以口述或文档等方式制定的技术规范都是弱规范,会被日积月累的无序行为致使架构积重难返。所以推崇通过框架定义标准,使研发过程和实现得以规范化。但框架除了按规范去约束,也要允许在同样规范下开放。对无序行为的治理,就如同大禹治水,并不能完全的堵也要疏。所以,模块框架是规范的具体体现,框架会很轻很薄的对基础部份规范,但框架更多的是支撑开放和扩展。
模块粒度
在本方案的模块认定中,一个模块是无关逻辑封装粒度、表现形式的,而是只要可被重用、接受模块框架管理的便是模块。不关心内部逻辑、代码分层等,只关心真正对外暴露的部份是否遵循模块规范。
平台定位
相对IC/TC/UIC等,商品详情绝非一个同类型的基础平台,因其并非位于淘宝架构的底层,且没有直接属于自身的数据,而是基于后端服务通过“逻辑再封装(注)”及“编排各服务“实现的上层业务系统。从更优雅长远的角度,就只应承担后端逻辑服务的“编排者”一职,不能有具体的业务逻辑实现。
注:商品详情的业务逻辑主要靠后端服务化接口提供,但因后端接口封装度较低等历史原因,仍有约10%以上(比率视各后端接口而不同)的逻辑“残留”在Detail上。虽然Detail系统内部也可分出业务逻辑层、数据访问层等,但从淘宝大架构视图分析,Detail仅是各业务系统和基础平台的表现层之一。
4. 平台化
4.1.前后端分离
Detail因历史问题,有大量业务逻辑散落在包括JS代码和VM模板在内的前端层。使得前端层复杂而又臃肿,可维护性差;也使得后端对逻辑的控制能力减弱,可重用性低;同时前后端在协作时因边界模糊和过程依赖,使得研发效率降低。为解决这些问题,商品详情平台化项目首先便进行了前后端分离(或称解耦)。
针对浏览型系统,可将逻辑划分为:后端的业务逻辑、前端的展现和交互逻辑(统称体验逻辑)。业务逻辑产出业务数据,展现逻辑负责模板渲染、交互逻辑响应用户行为。前后端分离就是要将业务和体验逻辑解耦,将业务逻辑完全下沉由后端控制,提升可重用性,将展现和交互的表达交给前端层负责,各司其职。
前后端分离只将逻辑解耦,但前后端并没有因此而失去互动,后端的业务数据接口便成为前后端的通信管道。所以平台化项目接着便对接口进行了规范性定义,包括:模型定义(入参和结果模型等)、安全机制(授权与鉴权等)、调用协议(HTTP/RPC)、返回格式(JSON/JSONP等),并支持且推荐Java本地的原生方式。
商品详情前后端分离之后,后端在业务规则和功能领域便更具操控性,而前端也能专注于用户体验,且前后端在协作时只需遵循统一规范,即可并行展开各自的逻辑实现,如下图,职责边界清晰,有助效率提升。
前后端分离也请关注Midway
4.2.模块改造
平台化项目中设计实现的模块规范和框架,用于将整块架构的应用(monolithic)拆解为模块服务级应用。模块框架是通过对源类的轻量级再封装以实现统一管理,包括管理类的生命周期/输入/输出/调用模式/稳定性等。不侵入逻辑不关心类的分层和设计模式等,事实上不依赖模块框架,如将框架废弃源类仍能运行。
核心构成
-
AbstractModule 是所有模块的基类,每个模块加载到独立的Classloader以避免冲突
-
通过LifecycleListener 可以监听模块生命周期
-
ExtensionPoint 是可扩展入口,以注解(@annotation)方式声明
-
Extension 是扩展实现的基类
代码实现
-
模块基本定义
public final class ExampleModule extends AbstractModule /*implements LifecycleListener*/ {
@Override
protected ExampleResult execute(RunData data, ModuleContext context) throws ModuleException {
//...
}
}
-
模块最少描述
#module.properties 需与模块同层
Module-Class=ExampleModule@1.0.0
Module-Requires=XxxModule@1.0.1,YyyModule,ZzzModule
#or Module-Local=XxxModule@1.0.1,YyyModule,ZzzModule
Module-Description=anything
生命周期定义
生命周期监听(optional)
public interface LifecycleListener extends EventListener {
public void changed(Event event);
}
模块容器
模块容器主要将原本已通过模块框架暴露的本地接口,发布为服务化接口,提供了丰富的远程调用协议,并对服务化后的接口进行安全包装,支持服务化后的批量处理等功能,即未采用模块容器的模块只支持本地调用。除服务化一系列的支持外,模块容器也提供了模块管理的钩子,但模块管理这部份暂时尚未实现。
配套工具
针对模块的生命周期和版本进行管理,以及开发阶段的IDE插件等工具。还在开发中。
插件体系
将日志工具(如slf4j)、稳定性工具(如Switch)、参数校验、模块流程编排等常用的类库,以插件形式融入模块框架,保持各模块在开发时能使用一致,以降低开发成本和提高可维护性。类库在统一定义的接口下适配成插件,目的是保障如果需要替换或升级一款类库后,模块内的代码不用任何改动。现日志、开关框架已完成插件化。
(近期针对模块化进行部份改造优化,后续会针对模块规范和框架实现再单独详细介绍)
4.3.代码重构
详情项目基于上述模块规范,再对业务逻辑重新梳理、对原有代码彻底重构,并按共建型、模块化、可扩展的设计思路重新分层。在新的层次中,每一层内的每一点在逻辑实现后,凡向上暴露接口时均以模块方式表达,上层通过模块描述发现下层能力。整个平台运行在模块容器上,模块容器运行在应用服务器之上。
4.4.稳定性
淘宝商品详情前辈们在多年稳定性实践中积累了丰富的经验,并沉淀了包括静态化/异步化/单元化等全局架构,平台化项目在继续受益延用的同时也在代码层做了统一性和标准性的优化,目标稳定性治理常态化。
-
模块的安装卸载,保障模块级稳定性,使得最少有一层保护
-
模块被手工或自动卸载后,可降级其它许可版本或触发容灾
-
模块内有否实现容灾会在模块安装前被校验,使之成为必然
-
在批量执行模块时(常见场景),模块框架可并发执行并为每个模块设定不同的超时时长以提升性能
-
因各层通过模块框架组织,而模块框架支持稳定性框架以插件的形式嵌入,使得各模块能统一使用
-
新的平台分层里,上层调用二方服务必需通过Adapter层(此层不开放共建,主要是将外部服务进行符合模块规范的适配并未有多余的逻辑)。在适配层进行对外部服务的统一稳定性治理,包括开关降级/容灾/超时控制等。使之前但凡要调用外部服务就直接使用就造成需多次稳定性治理的情况得以改善,降低维护成本。Adpater层内各模块或其它各层的模块,也能针对来源设定不同的限流阀值
4.5.测试体系
Detail受历史原因也碍于代码的分层结构和代码的不规范性,未能实现行之有效的测试方案,所以测试工作回归量大、重复劳动、枯燥无味、效率低下。而平台化项目针对现状重新整理了Detail的技术质量体系。
分层测试
根据代码分层,技术质量维度也进行了对等的分层单元测试。各层主要包括:基础服务层(如DAO/Util等)、业务逻辑层(如Service/Manager等)、对外接口层(此层进行接口级测试)、页面展现层等。而Detail场景大都是依赖更后端的服务化接口,所以在Detail新平台的单元测试中为提升效率引入了PowerMock框架。
自动方式
除页面展现层外,其它各层均实施自动化测试方式,而页面展现层更多的还是需要通过手工介入方式来进行功能测试。能够代替手工测试、找出重复测试工作是进行自动化方式的思路。而在衡量自动化测试目标时,也应从追求自动化覆盖率,到追求正确率、关注运行时间、测试脚本可重用性等,进行全方位的提升。
持续集成
基于分层单元测试、自动化测试,才能实现最终的持续集成。持续集成平台通过跟踪代码库的变更而自动执行测试用例、静态扫描等,这样便能快速发现问题,当然也要在环境部署之前触发,同时持续集成平台还应支持执行对应的用例,比如,只修改了AbcUtil.java那么则只执行AbcUtilTest.java。将持续集成定义为常态化,设进标准流程,融入日常工作,不断有所追求,最终高效保障淘宝商品详情平台的技术质量。
4.6.研发模式
没有规矩,不成方圆。商品详情平台作为一个设计完整的平台,还针对各维度定义出一套规范(或称标准)、针对各阶段设计出一套流程,以指导平台化后的研发模式。包括:模块开发规范、提测标准、质量标准(包括业务逻辑质量标准、性能标准、稳定性标准、安全标准、可维护标准也包括运维)、发布流程(含紧急发布流程)、线上问题处理规范,这些规范和流程面向参与平台建设的所有前后端开发、测试、运维等各角色。
规范是必需要建立起来的指导原则,在通过商品详情平台化的实践后,针对规范的落地也更有理由相信:
1、设计再良好的架构,也是需要依靠合适的组织结构去保障的;
2、架构的认同、执行、维护,最终需转变团队成员的思想,这是一个过程;
3、尽量将规范和流程用以工具、示例等更形象更直接方式表达。
另一方面,在共享共建大背景下,平台在搭建之初也针对共建进行设计,无论是共建理念定位、模块逻辑结构、物理部署方式,再到各维度能力的协同。以下便是经过模块化改造后商品详情平台的共建研发模式:
商品详情新平台提供两种协作模式:
1、 独立模块级共建
默认共建方式。各方基于Aone二方库进行共建,一个二方库即一个模块,二方Jar包即模块单元,二方库内代码实现需符合模块规范才可被识别和部署。该方式在研发过程和部署方式上更灵活,更可管理,共建方的可控性更强,但对于Detail主维护团队所提供的平台底层扩展能力(即SDK)要求较高。
2、 源码分支级共建
多人基于同一个源码工程,通过开多个分支的方式,在各分支上实现业务逻辑,最终各个分支在整体合并后进行部署。优势是允许从底层能力至上层业务更大范围内进行编码,更具连贯和顺畅性,但缺点是分支和流程冲突大难以协调、业务理解能力和编码实现习惯各有不同,容易逐渐造成架构被腐蚀。
商品详情新平台更加关注“共建方体验”,在新架构和新研发模式的基础上,共建方将得到如下体验和权力:
4.7.部署结构
平台逻辑架构和部署物理结构,因所面临的问题领域和解决方式各不相同,所以不能互相限制对方的最终形态,即不能因为平台逻辑架构而绑死最后部署结构,这是平台化项目的架构设计基本原则之一。更何况在业务高速发展,需求多变,团队协作的特定场景,更是要求部署模式也一样的灵活,按需定制,可组合。
5. Use Cases
成功案例
农业Detail全部承载
挑食Detail全部承载
虾米Detail全部承载,暂未上线
旅行Detail部份后端接口
等等……
其它业务场景
高速业务场景:针对大型的垂直业务或快速发展的业务,在除主站Detail集群即主平台外,只需采购所需的业务模块(也可远程调用),就可以在独立集群再进行部署,业务方只需关注自身模块的开发迭代,其余的模块包括基础框架均由Detail团队负责推送更新,便可自行掌控所有权限和节奏,还能共享同一套CDN静态化体系。如当业务成熟、需求逐渐减少,认为有必要时,只需向Detail团队提供模块即可回归主平台。
无线终端场景:某APP或HTML 5端需要部份接口,Detail直接提供远程服务或将所需要的模块接入MTOP
非详情的场景:某页面需展现SKU,可将Detail前后端的SKU模块拿走,从后端数据到前端渲染及SKU选择计算就统统包括;某页面非商品详情但需展现卖家档案可结构略不同,则复用卖家档案模块,前端部份自行实现。复用时可直接拿走卖家档案模块包自行部署提,也可找Detail团队先沟通后直接调远程服务
等等……
版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。
-END-