3238dd1ba2e37d7597bfca5ca2abca81.png

在旅游系统的各种实践中,各品类资源描述方式的差异化给系统的抽象带来了很大困难,从采购、到库存、到销售的整个链路上,每个环节都要有大量的代码去解析或封装各种类型资源和产品的代码逻辑,而这些数据结构,绝大部分是本系统用不到的,只是为了传递给下游系统,所以当一个系统改动了一个字段后,各个环节的系统都要跟着改一遍,这在系统设计、开发和维护上,都带来了很多不必要的麻烦,增加了复杂度,如何减少甚至屏蔽这类困扰,降低成本,提高系统的稳定性,是一个需要重视的课题。

01

现状分析

目前,途牛涵盖了机票、酒店、门票、跟团、用车、签证、游轮等大小几十种旅游资源类型,由于属性、特点和售卖形式的不同,描述这些资源的数据结构差异也非常大。

以机票和酒店为例,数据结构分别如下(注:这只是一小部分,真实数据字段的数量是下图的两倍甚至三倍):

26c540b13dc5f90d86a22dff43ab958c.png

图:机票数据结构

085dba72208235d6a764d221b702dcc9.png

 图:酒店数据结构

大家可以看出,这两种资源的描述字段几乎没有相同的,这是客观存在的事实,我们无法强行统一或者合并,更何况还有很多其他不同的资源类型,所以如果想要在它们上层做抽象将十分困难。

接下来试想一个简单的业务场景:

采购系统将这两个资源生成采购单采购,完成后录入资源及库存系统,产品系统将资源封装并上架销售,客人在网站上查找然后下单到订单系统,订单系统去库存系统申请出库,结算系统到账期生成结算单给供应商。

以上是一个常规的进销存的过程,系统关系大致如下:

c53d7487d047194f6b73198368f6dcb1.png

图:系统关系简略图

(圆角方框为系统,圆形为交互对象)

在整个过程中,各个系统都需要机票和酒店资源的信息,但又不尽相同,比如对客系统需要知道名称、价格、航班号、位置、退改政策及一些简介信息;订单系统只需要知道资源编号、价格;产品系统需要知道资源编号、价格、售卖形式、促销限制;结算系统需要知道资源编号、成本价、售卖价……但这些系统真的只需要关注自己用到的字段吗?

现实告诉我们,必然不是,有很多字段都是上游系统或下游系统需要,中间的系统不得不在接口的出入参中定义甚至存储,比如对客系统需要展示客人订单中的资源详情,那么订单系统就不得不把资源的详细信息透传甚至存储,也就意味着,对客系统、订单系统、资源系统两两交互的接口中,需要定义这些字段,而随着系统不断的演进和迭代,维护和开发人员的更替,字段列表越来越庞大,甚至无法再相互对应,造成了系统越来越臃肿、代码无法理解、稳定性越来越差。

在这样的背景下,有一套统一的字段定义显得尤为重要。在做旅游工厂项目时,对这个问题做了大量的思考和尝试,最终选择了“协议”的方式。

02

协议式报文产生

 为解决上述问题,我们主要调研了几种方式:

No.1

统一公共数据仓库

收拢各品类的数据,提取出各品类的主数据,按维度统一存储在一个或一组库中,其他非关键字段仍然存在各品类的数据库中,在应用需要查询时,统一从这个数据仓库中查询,数据仓库根据需求,从本地及各个品类库中拼装数据统一返回。

d5b897eb3dde0f3c5d9db4a794840abe.png

  图:公共数据仓库

优点:

集中化管理便于统一规则,聚合服务,使应用端开发简单一些。

缺点:

1、品类差异化大,要想提取出各品类的主数据不易实现。

2、所有冗余数据的通病:数据一致性及实时性问题,要做好这一点,需要付出较大代价。

3、大部分系统都已成型多年,无论是应用端还是服务端都已经有了一套方案,贸然去收拢存在很大风险。

总结:

这种方式更适合公司系统建设初期,大家有一个统一的认知,在设计和实现时期为这个目标而努力,留有统一的数据交互接口。另一种场景就是BI做报表分析,对数据及时性和准确性要求都不是太高,但需要很多复杂的联合查询。注:各品类一开始就将所有数据存在一起也不合适,这样各系统间影响会很大,且很容易限制各品类根据不同自身特点个性化的发展。

No.2

建设数据代理层

在应用系统和各数据源之间加入一层数据代理层,由这一层适配所有的数据源,定义标准化的输入和标准化的输出。

e709bcd96395c0dea73b26f1d6a5d9b5.png

优点:

1、应用系统将变简单,只需对接这一个代理层即可,与后端的数据源解耦。

2、各品类的后端服务更加稳定,相对而言前端应用的变化比较频繁,以此来满足业务需求,而后端更加注重稳定性建设,不应跟随前端应用高频变化。

缺点:

1、对设计要求极高。看似简单,只是做适配和路由,但较难把握,设计把控不好,系统边界不清,很容易变成一个大杂烩,系统变得杂乱无章,难以维护和扩展。

2、开发难度大,如果要做一个完整的数据代理层,制定出各个业务线适用的标准,那就需要这一层了解所有业务,至少是主要业务,知识量过于庞大,难以为继。

总结:

看似简单,但容易烂尾,且要做的完善,工程量巨大。

No.3

协议式报文

对于异构的数据服务,业界有一种称为“联邦服务”的方式,大致思路是,在各数据源的上层增加一层虚拟数据库,将分布式异构数据集成到一个虚拟表中,用户或应用程序可以通过该虚拟表对数据进行实时操作。这种方式由于不在本地存储真实数据,所以没有了实时性和一致性的问题,且没有太多的业务逻辑要做,功能定位比较简单,很适合做数据收口和集成的工作,但它的问题也显而易见:数据源数据模型改动频繁;有些功能只能在数据源完成,如有些数据源独有的特殊函数或算法;由于安全或者数据归属等问题,很难定义出哪些应该走联邦服务器。

以上几种方式究其根本,是先将底层数据库(或数据库代理层)的结构统一,从而反推应用层的API接口数据结构统一,毕竟绝大部分的交互字段最终都将落到某一个存储中。

鉴于上述原因,我们在“联邦服务”思想基础上加以修整,提出了协议式报文的交互方式。相当于将虚拟表通过文件的方式定义出来,使用XSD的格式进行描述,并提供解析工具包,协议文件中规定了每个品类、在各个主要场景中的规范,这些描述到的场景中,所有链路上的系统都采用同一个标准,从而最大限度的解决了品类差异化。各数据源可以按原先的方式定义结构,处理内部逻辑,但用于交互时,必须按规定注册到协议文件中,否则解析工具包会将其拦截,发出警告,这样一来,用技术手段保证了协议化的权威。

协议如下(部分截图):

1fe80f0532f79e36d71fe569b359b72a.png

这种方式最大程度地保证了各品类系统的自由性,同时又保证了交互链路上的标准化。交互方式如下图所示:

bc6def83963a87d49b0c3528fe6f98bf.png

图:协议式报文交互

(示意图)

各个应用系统和数据源服务方嵌入协议工具包,由工具包负责协议报文的解析和校验,工具包中带有缓存机制或ZooKeeper等工具就可以解决性能损耗的问题,而协议的管理和发布都由管理中心统一支配,避免协议不受控的问题。

03

具体实践过程

在机酒打包订项目的大工厂和大仓库中实践了协议报文的方案。

1、根据业务场景,用XSD文件定义出了打包资源获取的协议,协议内容包含两部分,为出游需求信息和资源列表信息(相当于接口的入参和出参)。客人的出游需求信息的具体字段,以及每个信息的校验规则,如取值范围、字符串规则(正则表达式)、出现次数、是否可空等;资源列表信息描述了各种资源的描述字段和校验规则。

2、编写校验工具包,工具包分为4部分:

 ①协议监听器,通过监听ZK获取协议管理平台发布的协议信息;

②协议转换器,将客户端即将发给服务端的报文转成XSD,支持json和thrift格式的源文本;

③协议解析器,将服务端收到的协议报文解析为目标对象,支持解析成json和thrift格式;

④协议校验器,在协议转换后和协议解析前,先根据协议编号和版本号,从管理平台获取协议定义并进行本地校验,校验失败客户端不发送请求/服务端不处理请求,快速反馈失败信息,既提高响应速度,又节省了网络开销和服务器资源,同时可以防止非正常渠道的访问。

3、应用系统接入工具包,通过jar包引入和简单配置的方式快速接入,而开发时刻工具包对开发人员透明,专注于业务逻辑。

4、协议的发布管理,协议在发布时,需要通过协议负责人的审核,保证在合理的场景有合理的协议内容后,负责人会编写协议发布到管理中心,此时协议方可在公司内部通用,以此保证的通用性、可靠性和安全性。

04

总结

在旅游工厂的项目中,我们采用了这种方式,与工厂交互的系统均走协议式报文,很好地解决了工厂系统频繁变更交互数据结构频繁变更的问题。

e79716d9bce464b1e10b404290cb38ed.png8f504e3b59521c4a0335574fbfd017ec.png

扫码关注我们

途牛技术中心

期待与你相逢

Logo

更多推荐