作者简介
灿荣,携程软件技术专家,目前关注互联网中台以及中间件领域。
一、背景介绍
为解决系统重复建设、能力复用性低的问题,携程启动了中台化建设步伐。旅游行业的中台建设,携程并非从零开始,前期已经积累了行业中多个场景的业务和技术的中台能力。因系统建设的复杂,亟需一个中台大脑站在全局视角进行公司中台能力的梳理和建设。
Tripyun-携程云是中台团队打造的产品中台,采用独特的”电商开店“方式,鼓励各业务线发布具备中台能力的产品,管理产品的整个生命周期,包括产品的创建、展示、开通服务、产品需求、产品联通和度量,以此来吸引其他团队下单采用,并通过需求结构化、能力优化不断打磨各团队产品的中台能力。Tripyun-携程云最终将以全局视角,通过能力地图将旅游行业里的中台能力都呈现出来。
为满足各团队在平台上对产品进行管理和“开店”的需求,我们为平台搭建了底层类目属性管理能力和SPU管理能力服务,满足了旅游行业特色的多样化中台产品和能力的发布需求。
本文以此中台能力的建设作为实操,详解我们探索中的中台建设的思路,重点介绍搭建类目属性和SPU中台能力过程中的技术实践,读者将了解到:
1)如何提升一个产品的中台能力;
2)以一个具体案例了解什么是元数据驱动和其架构实践;
3)Tripyun-携程云类目属性和SPU管理能力,可考虑使用我们的能力,共建行业类目属性库。
二、什么是元数据
元数据,是指一种结构化的信息,用于对某项信息资源进行描述、解释、定位,使其易于提取和使用。我们先看下在电商应用中商品领域中的元数据定义:
2.1 名词解释
(1)SPU:Standard Product Unit (标准产品单位),是用来聚合描述一种商品信息的单元。基于SPU的商品信息结构,可以实现丰富的应用,比如商品信息与资讯、评论、以及其它SPU的整合起来,就可以基于同一模型来满足多种场景的业务。
我们借鉴了电商中对于SPU的应用,即通过SPU的“类目属性”来描述Tripyun-携程云上的一切业务数据,包括系统上的产品、团队、订单信息、能力信息等数据,都作为SPU进行整合,进行了业务数据模型的统一。
(2)类目,也就是分类,分类是为了更好的管理商品(商品管理的核心就是分类)。
在Tripyun-携程云中,我们通过类目来分类产品、订单、能力、团队的类型。比如在产品上,通过类目把携程的基本产品做了基本分类,以满足不同类型的产品区分管理。
类目的设计主要从两个维度考虑,除了后台类目,还有前台类目,即给前端用户看的类目。电商应用在针对用户的展示或者营销过程中,往往会给商品划分一个只用于前端展示的类目,该类目可能由于运营的需要而进行调整。Tripyun-携程云平台也具有前台类目。
(3)类目属性,就是某个商品的特性,属性值即属性的具体内容。对于电商来说,属性主要是商品的品牌、尺寸、大小、颜色等,对于品牌属性而言,其属性值可以为阿迪达斯、耐克、马自达等等。类目是为了分类商品,而属性是为了描述商品。
在Tripyun-携程云平台中,我们定义了一个”广义数据模型“的概念,把产品、订单、能力、团队等业务的模板都使用属性来定义。比如,把产品的中台能力定义为一个特殊的类目,而用户应该怎样描述自己的能力,则提供统一的属性定义页面,来允许用户定义复杂结构的属性集。
(4)SKU, 大家都知道电商行业中的SKU(Stock Keeping Unit)是指库存量单位,即库存进出计量的单位, 可以是以件、盒、托盘等为单位。
SKU是物理上不可分割的最小存货单元,而在Tripyun-携程云中,因为我们卖的不是货,而是中台能力,所以可以理解为我们让大家在平台上录入的能力即为SKU(可以直接”卖“给产品采用方的最小单元,可以是小到一个工具、一个框架、一个接口,或一组服务组成的应用,也可以大到一个产品线)。
对中台产品而言,能力除了可以作为SKU,还多了一层额外的意义——能力是中台产品可以被(个性化)复用的体现,由产品开放出来的互相解耦的能力选项而构成。
2.2 Tripyun-携程云中的元数据
为保证Tripyun-携程云上产品数据结构足够灵活,我们夯实了类目属性的底层数据结构,支持多种不限于K-V结构(包括表格、级联等复杂表单数据存储结构)的数据存储、查询、校验等需求。
这样的数据管理能力也可以支撑除了产品外很多其他领域的数据模型,目前这套能力已经为Tripyun-携程云平台提供团队管理、产品管理、能力(SKU)管理、服务单/调用单管理等多个业务场景的数据模型定义、动态表单生成、动态规则校验和动态业务数据的存储/查询了,实际上已经成为Tripyun-携程云平台所有模块的业务数据模型——支持通过不同的规则定义和流程扩展,来做不同场景下的个性化定制。
我们把这种描述业务数据的数据,统称为元数据。如上面提到的,元数据除了包含数据模型定义,还包含了业务规则定义、业务流程定义、业务配置(标)定义和面向前端的组件数据定义。
三、我们的实践
3.1 可自定义的产品结构和能力发布结构、满足全球化背景下可复用的数据多语言——数据模型
3.1.1.解决方案
我们认为业务中台化的一个重要措施是进行业务模型的抽象,将模型抽象为可统一表达的元数据模型,将非常有利于行业数据的标准化管理,进一步缩短基础数据管理的成本,而基础数据模型的统一也将为后续规则的统一和业务流程的标准化提供了可能性。
在搭建Tripyun-携程云平台的时候,我们将原本业务上很多互不相干的数据管理,如团队、产品、能力、服务单等模块的信息类数据抽象成了统一的模板元数据。而将随着业务不断变化的属性数据,如状态、活动标签、业务标记等使用业务配置(可以认为是动态的扩展字段,下一节介绍)的方式进行解耦和动态管理。
3.1.2.数据模型统一的困难
电商应用中的SPU属性,因商品本身普适性强,以及买方一般具备对商品一定的基本认知,可能K-V类型的存储结构满足大多数的应用场景,结构上并不复杂。
但是Tripyun-携程云的业务范围是描述“软件产品服务”类商品,且更多的是与具体业务相关的服务,软件类“商品”本身确实比一般性商品更加“复杂”,且用户在浏览Tripyun-携程云平台上发布的软件产品时,也没有普适性认知,所以我们需要足够灵活、方便的,不只是k-v结构的展示方式,所以仅仅K-V类型的存储结构并不满足我们的需求。
如在中台概念中,我们一般会把前端应用按照“端”、“场景”属性进行分类,以端为例,我们可能需要三级级联的属性和属性值来组成“端”属性,三级级联属性分别为:
第一级:online/offline,用于描述是外网应用还是内网应用;
第二级:web/app/H5,用于描述前端应用触达用户的方式;
第三级,则是按照行业业务归属进行的端的定义。
此类需求的难点在于,三级属性互相影响、互相依赖,如online和offline,以及不同的端类型,其对应的第三级属性的属性值,是完全不同的。
又如卖方在产品定义时,为了方便开发角色的用户在浏览产品时对接口等技术指标进行评估,可能需要对系统接口或者整体系统指标进行说明,需要一个表格类型的控件。就这样一个简单的表格需求,对于我们类目属性的这套产品结构复杂度构成了指数级的挑战,这需要我们进行属性的层层嵌套,并且对前端页面更为友好的方式,将嵌套数据输出给前端页面。
除了结构的复杂度,我们还面临复杂结构下带来的性能、数据安全性等的挑战:
(1)基于类目属性的产品结构中,因为产品属性并不是简单关系型数据库的行列,而是倒树形非关系型的结构以行列方式存储。这对我们的查询性能造成了较大挑战。
在互联网应用对性能极致的追求下,网络连接是最“宝贵”的资源,我们需要尽可能节约前端请求数量,不可能用户每次点击属性都实时进行子属性和属性值的拉取。所以在这种场景下,需要服务端能够一次输出该产品类目定义的所有属性和枚举的属性值、以及产品对应的属性值。因产品属性的级联嵌套复杂度,其输出性能将会随着产品属性嵌套复杂度的提升而升高。
(2)类目、属性、规则等“描述数据的数据”(文中统一称为“元数据”),与应用运行过程中产生的产品等数据不同,元数据(如属性和规则等修改)的修改,将会影响到大批量下游数据的变更。如果是因为人为或者程序的误操作导致元数据的错误变化,将会产生不可估量的业务后果。
3.1.3.模型元数据的技术方案
为解决上述问题(1)的挑战,我们使用mysql作为存储数据库,底层数据结构:抽离一套类目属性表和类目属性值表,类目属性表描述属性的父子关系,而类目属性值表描述属性值的父子关系,即存储属性和属性值的两棵树。再通过数据加工,组成一套产品属性模版数据和属性值关系数据。
如上面提到的“端”这个属性的例子,我们可以构造给前端应用这样一颗树及模版:
树
Template
类在关系型数据库存储树形结构的问题,其实业界已经有一些可以参考的方法论,一般比较普遍的就是四种方法:(具体见 《SQL Anti-patterns》这本书)
- Adjacency List:每一条记录存parent_id
- Path Enumerations:每一条记录存整个tree path经过的node枚举
- Nested Sets:每一条记录存 nleft 和 nright
- Closure Table:维护一个表,所有的tree path作为记录进行保存
书中介绍的各种方法的常用操作代价见下图:
我们的场景中,对于全树查询、插入、删除有较强的需求,根据以上表格的结论看起来Closure Table方案更好一点。但其实需要考虑到:Closure Table需要额外维护一套全路径表,而存储路径本身就是一个不可预知的大字段。考虑到未来的接入类目属性和SPU管理能力的数据量级、对db的读写压力以及分库分表等db的需求,我们最终选择的一个对mysql比较轻量的Adjacency List方案,为弥补此方案对全树查询的短板,做了以下两个措施:
一是构建SPU树的模版,将类目属性和类目属性值相互独立,之间通过属性编码进行关联,同时构建出两条树形结构:类目属性和类目属性值。类目属性绑定到模版,这样可以直接输出给前端应用使用,类目属性值则直接输出每一层级的数据,保证每次查询都是非常简单sql查询。
二是通过消息通知机制,尽及时做到若mysql的这两棵树有任何变更,将都能及时都获知并且更新模版及类目属性值。
上面的解决方案,解决了每个类目属性树的存储和查询的问题,我们称为类目属性模板。模板的缓存为最终SPU树的输出性能提供了一部分保障,为了进一步解决性能问题和模板数据安全的问题,我们设计了以下方案:
(1)为保证类目属性元数据模板的安全性,除按照上面陈述的存储和查询方案,我们多加了一层模板数据临时区(预览区)和正式区(运行区)的区分。类目属性控制台可以实时修改类目的属性、属性值和规则,但是数据会保存在临时区。只有相关运营人员发起元数据上线申请,才会触发将模板数据同步到正式区。
(2)SPU详情的查询,依赖元数据模板进行merge,具体的操作无非就是树的搜索,把SPU选中的属性值merge到模板树上对应的节点中。若商品模板在临时区也存在数据,那同样也merge到临时区模板树上,满足商品针对新元数据结构进行发布预览的需求。
(3)SPU数据正式区和预览区,存储的都是可以输出给前端的完整树形结构,如果调用类目属性和SPU能力的前端为进一步提升性能,可以考虑直接将正式区的数据同步到CDN上。
3.1.4 可复用的数据多语言
除了充分利用携程现有多语言中台、AI中台的能力,对接机器翻译和人工翻译,我们在属性库额外做到了多语言的复用(这也是属性库数据的价值之一)。在属性库中已经翻译过的属性-属性值对,就可以达到最底层数据层面的复用,这样上层应用就无需考虑数据模型层面的多语言问题,在获取数据的接口上只需要给一个语言参数即可获得对应语言的数据。
另外,比较重要的一点,属性库的翻译需要支持多维度翻译,既要支持单独属性或者属性值的翻译,也要做到”属性属性值对“的翻译和”特定类目下的属性和属性值对“的翻译。
例如单看属性值,直接翻译“小米”这个属性值本身可能是millet,这是一个缺省的属性值英文直译。但如果跟不同的属性,比如:”品牌:小米“和”食物:小米“,或者说手机类目下的“分类:小米”和粮食类目下的“分类:小米”其对应的翻译可能完全不同。只有在各个维度上都支持翻译的覆盖,即上层优先级高于下层,才能够真正能够达到属性库多语言复用的效果。
我们正在建立旅游行业-软件产品管理领域的类目属性库,也欢迎共建旅游行业其他领域属性库。
3.2 解耦动态业务配置数据——业务配置标
我们通过模型元数据的方式,对业务信息类数据进行了模型统一。而在实际的业务应用中,会存在许多涉及到随业务动态变化的“属性”,比如状态、临时分类标签、临时身份标签、活动标签(如促销)等。我们将这些属性采取“打标”的方式关联到SPU上。
解决方案
“标”相当于SPU的扩展属性字段,其逻辑存储是与SPU的属性存储分离的,实践过程中,我们将业务“标”通过动态配置中心下发给需要关注此标的业务身份,前提是关注对应标的业务方需要在标控制台中订阅该业务“标”,即标的查询和修改使用的是发布-订阅的模式来完成。
一个具体的应用场景,比如当有产品推广需求时,我们需要在首页展示一些对应的产品,可能会有一级展示区域的产品、二级展示区域的产品。在传统的开发模式中,我们面对该需求可能是单独建一个数据库表来存储放于一级或者二级展示区域的产品,但为此数据和数据库的管理、运维都会带来额外的负担,而如果需求中额外需要扩展产品的一个新的“状态”字段,开发同学因此在产品主表中增加一个新的字段就会变的像hard code。
为了开发的便捷性和不涉及到产品SPU的原有数据,我们采取给产品SPU打标的方式来解决。该业务是活动类需求,我们定义几个“活动标”,将标与产品进行关联,而只有“Tripyun-携程云商城首页”这个业务方才会使用这些标,我们只需要将这些标的配置下发到“Tripyun-携程云商城首页”这个业务身份对应的应用中就可以了,而其他业务身份的应用则不会受到该“标”的干扰。如果业务需求需要在产品上新增一类新的状态,我们也可以通过“状态标”的方式来同样应对。
以上方案通过基于标的发布和订阅机制,来作为业务配置类属性的扩展,一定程度上实现了业务的灵活性,也遵循业务数据与流程低耦合的设计原则。基于配置中心进行标的下发,则是考虑到降低数据分布存储和查询带来的额外性能损耗,以及避免不相关的业务字段给对该业务本身不相关的数据消费方造成干扰。
3.3 统一全局业务身份,支持数据的校验规则和响应规则配置——业务规则
产品的属性可能会存在不同的数据校验规则,而级联属性可能会存在属性间的关联校验规则。需要提供用户自定义校验规则的需求。
针对同一个能力不同的调用端,其可能存在属性、属性值不同的输出逻辑(是否显示、校验规则等),以及是否需要额外推送一些业务”标“的逻辑,需要针对不同业务身份的用户作出不同的规则响应。
解决方案
(1)统一全局业务身份
在业务规则化以及后续的流程化之前,首先要做的其实是统一业务身份,业务身份的统一依赖于企业全局视角的把控,中台能力在Tripyun-携程云平台上发布的时候,需要选择采用方的业务身份。
业务身份就好像业务服务链路中每个节点的身份证,业务身份的统一可以用于每个中台能力识别不同业务身份而触发对应的业务规则,有利于集团中台能力的输入模型统一。目前基于携程当前的系统建设现状,我们引入应用ID、产品ID等系统维度的业务身份,未来有赖于更多业务中台能力的发布,有利于我们统一更多的业务身份。
(2)引入规则引擎
相信大家在编写业务代码时,会尝试使用规则引擎来解决大多数频繁变动的诸如if...else...的业务需求。其实需求类似,在建设业务中台化的过程中,为满足不同场景的前端应用对我们能力的复用,我们也需要根据其不同的业务身份进行识别写诸如if...else...不同分支执行的代码,而将其抽象出来就是规则引擎需要做的事情。
需要注意的是规则引擎不仅是动态执行代码,诸如groovy、SPI等动态执行代码的方式,他们更多的是站在开发视角,我认为他们可以对业务规则化满足不了的场景进行一定的补充,但并不是全部。
好的规则引擎,一定要能够支持业务规则化,能够允许没有开发能力的业务人员,根据其对所属业务领域的理解和对业务的需求,定义出原本需要程序员代码写出的if...else...逻辑。
我们对比了业内主流的规则引擎,首先排除掉了对业务不够友好的groovy;又排除了对开发的学习成本太高的Drools,我们最终选取了开源的QLExpress作为规则表达式,并自建规则引擎框架。QLExpress在执行效率和表达式解析的性能足够支撑电商级别的业务,提供的操作符定义和宏定义又能灵活满足业务定制的需求,我们在属性和属性值的表单校验、SPU输出属性过滤等多处预留了业务规则扩展点,结合我们能力本身提供的可以动态构建表单的能力,也搞定了可供使用人员配置规则的页面。
QLExpress虽然可以提供完整语义的规则解析和规则执行,但是要达到可以开放给业务人员直接使用的程度还尚缺一些功能。比如规则知识库的维护和管理,需要有一个支持业务人员可以录入规则并进行版本管理、规则结果预览分析的知识库前台;开发人员可能也需要有一个方便的系统来进行排障分析、规则链路跟踪分析的视图和一系列支撑的系统。另外是一些高级特性,如结合规则的版本控制功能,可能会有灰度引入某些规则的需求,当业务在需要支持的时候我们也希望能够可以支持。
考虑到目前主要的业务都是一些实时数据量较小的业务,规则的执行器可以放在业务程序本地,通过配置中心规则下发的方式接收规则;未来可能也会因为实时数据量较大对计算造成压力,而统一调度到远程执行的模式。
当我们平台的运行模式和上层框架固定好后,底层也可以支持更多的规则解析执行引擎。
很多时候,可以灵活变化的业务规则,其实就是你的”中台能力“。
3.4 支持业务流程标准和可扩展——业务流程
我们在将自己的类目属性和SPU管理中台能力输出给Tripyun-携程云产品不同类目的场景时,发现对于数据模型管理的流程有较多不一致,如产品在生成数据后,有的类目的产品需要对产品状态进行审批;有的类目的产品要求在审批通过后要求自动生成SKU,进入后续的能力配置流程;有的类目的产品需要同步关联前台类目;有的类目的产品需要在Tripyun-携程云商城中建立产品索引......
解决方案
上面提到的流程上遇到的困难,这又是在业务流程上的if...else...逻辑,我们当然可以设置很多的开关,让用户来设置是否开启某些功能。但是如果用户要求在某些流程节点后要能够执行一些自己的远程服务任务,类似的需求该如何实现?这就需要流程引擎来帮忙。
使用流程引擎带来的优势有:
- 支持流程的扩展和编排,前提是每个流程节点需要具备统一的上下文参数认知,如定义全局业务身份,统一的参数数据模型。
- 天然将业务按照任务节点解耦,更有利于业务标准流程的梳理——将标准的流程放到主流程分支,将不同业务身份的个性化流程放到扩展点。
- 业务流程解耦后,更容易基于统一的流程模型做到业务流程的监控以及排障。
- 技术架构上更解耦,适合践行微服务等分布式架构。
如下图,是我们针对上述的需求设计的流程引擎配置的原型设计,目前正在以我们类目属性和SPU管理能力为原型开发中。
流程引擎平台我们正在基于开源框架来改造。目前我们希望可以将流程的委托任务与引擎本身解耦需,要引擎本身所在的业务域与业务扩展点在代码、运行环境上能够完全解耦开,做到单独的治理维护且扩展点程序的运行不影响业务域的正常运行。以及,流程引擎需要与业务紧密贴合,需要能够定义一些跟业务紧密结合的如全局业务身份、扩展点上下文数据等通用应用层的东西。
四、小结
我们认为中台化过程就是对业务模型的抽象化过程。Tripyun-携程云将复杂事物模型抽象成类目属性的结构化过程,通过对类目属性数据模版化和业务抽象扩展”标“,规则引擎和流程引擎引入等,从而可以使业务逻辑解耦,满足大部分携程业务场景和个性化需求。
Tripyun-携程云目前还处在推广和系统完善中,在推广过程中也遇到不少问题,比如前端应用有数据展示联级个性化需求,目前难以自定义化,“标”使用虽然带来灵活性,但同时导致系统的复杂度和难以理解,数据上也是泛滥成灾。后期我们主要将围绕这些问题进行升级改造,需要对数据模型再次抽象,数据规范化,模版做到通用化和可自定义化等。