干货 | 携程商旅订单系统架构设计和优化实践

2021-11-12 15:11:54 浏览数 (1)

作者简介

Gavin, 携程技术专家, 致力于系统优化、重构和开发效率提升。

概述

携程商旅是一站式互联网差旅服务平台,为客户提供差旅管控、预订、出行、结算、成本、风控等服务的在线TMC(Travel Management Companies)。主要产线有:机票、酒店、火车票、打车、接送机、包车、租车、汽车票等。携程商旅订单系统针对B端客户定制化需求多样性,商旅产线丰富性,TMC业务与产品业务结合的复杂性等,对订单系统架构进行了优化。

一、背景介绍

订单系统作为入口,承上启下,涵盖了订单生命周期中所有的流程管理、TMC管控、创单中心、费用中心、履约中心、服务中心、聚合中心以及投诉理赔管理等。订单打通用户、商家、产品、库存、订后等关键业务,是驱动交易全流程运转的核心。

订单系统具备的能力,可以按照下面两个维度和多个角度进行切入拆解:

  • 客户维度

员工视角:预定、公账垫付、个人支付、行程管理、退改服务、物流跟踪等;

财务视角:成本中心、交易流水、费用明细、账单报表、报销凭证等;

审计视角:审批授权、差旅管控、数据合规、政策合规、风控、反作弊等。

  • 平台维度

客户实施视角:账户配置快照、订单聚合服务、API对接等;

供应商视角:商家拆单、订单状态管理、费用结算、售后管理等。

携程商旅订单系统在早期为了快速满足业务需求,产线为纯纵向独立模式,在业务的快速上线、风险隔离,有效地降低产品之间的耦合性作用明显。随着业务的快速发展、产品丰富度增加、产品之间业务关联程度深化、客户的定制化需求增多和创新型产品快速上线,当前系统架构出现了单一臃肿、耦合严重、功能重复、流程不统一、监控和运维弱等各种问题。

  • 无统一的流程引擎,不能以灵活、可配置的方式支持B端客户快速增长的定制化、碎片化需求;
  • 无统一的编排服务,导致子系统之间高度耦合、相互调用、边界不清晰;
  • 多产线纵向模式、流程不统一、相似功能重复开发,不能快速的支持业务迭代上线;
  • 多产线的订单数据无统一抽象、设计、聚合;接入端学习成本高、接入工作量大;
  • 实时场景 (online) 和非实时场景 (offline) 耦合、不能有效隔离和保护核心资源,降低了系统可靠性;
  • 缺乏有效的自动化补偿机制、监控体系、数据埋点,不能满足B端客户越来越严苛的数据合规要求。

二、基于业务特性的设计原则

2.1 分层设计 - B端系统C端化

从预定视角能够感知到的系统功能:快速下单、支付、出票、确认供应商等。这一点无论C端还是B端都是相通的,使用对象都是“人”,核心目标就是确保用户的行程可以顺利起飞、出行、入住等。C端系统的功能是B端系统的基础,从时间维度划分以下流程,在此基础上B端系统会有其特有的业务属性被嵌入。

这里需要避免按照传统的思维梳理流程图,将B端的功能与C端的功能作为一个整体的流程开发。B端特有的逻辑和服务应该独立开发并且与产品、产线解耦,通过服务编排、流程引擎等实现功能可配置、可扩展、可插拔的方式对订单系统进行统一的调度和驱动,实现B端业务特性与C端逻辑分离和解耦,达到产品需求C端化。

  • 下单 - 购买

B端嵌入功能:选择审批人、成本中心;差旅申请、管控校验;支付账号(公账)、费用政策、报销凭证规则等。

  • 履约 - 支付、出票、发单等

B端嵌入功能:发起审批(审批人通过、拒绝、终止);公账扣款、公司信用支付、费用流水;公司对接推送等;

  • 服务 - 出票、出行等

B端嵌入功能:退改审批流程、管控校验、打卡校验;订单金额、订单状态、票号状态终态的合规验证;未出行订单自动处理流程;费用流水处理、报销凭证处理等;

2.2 流程设计 - 产线多、共性小

从多产线中抽取出共性(去重复、标准化),针对B端系统所具有的定制化、碎片化、特定性等需求,提供通用、统一的标准化流程方案。在标准化流程的基础上再结合订单系统特有的订单状态、订单事件、票号状态等,抽象出流程引擎,实现可配置、可扩展的标准化流程,从而达到化繁为简的效果。

2.3 快照设计 - 管控粒度细、维度多

差旅管控信息(账户配置、支付账号、审批授权、差旅标准、政策执行人等)作为差旅业务的基础信息,对其任何的改动都至关重要,一方面把每次的更改记录都保存下来。另一方面在每次交易订单的各个场景,需要将当时差旅管控信息持久化。以此作为各个子系统交易和运行的基础服务实现统一收口,同时以便问题的排查、处理投诉、维权、溯源等。

三、架构演进

系统架构图
3.1 Order Consolidation - 数据抽象

纵向模式向分层模式演进最大的挑战是不同产线之间的数据库的横向,任何数据层面的改动和迁移犹如行驶中的汽车更换轮胎。系统在初期通过纵向的订单详情接口满足前端、各子系统、IM等不同场景,此模式简单清晰,数据全量输出,但是带来的问题也是显而易见的:DB压力过大、接入复杂、学习成本高、数据耦合等。

通过借鉴数据仓库建模的方式,以offline系统思维来降低online系统的复杂性。以子系统的数据详情服务作为事实表Fact, 根据不同的业务场景和粒度输出维度表Dimensions。较少的系统是从头开始、从轮子开始建造,都是在已有基础上优化和迭代,通过通用数据冗余和增加维度数据聚合输出服务, 实现数据层面的横向。

1)数据建模、数据冗余,降低系统复杂性

订单数据进行统一的抽象建模。以订单基础数据、常用数据、各子系统数据行程服务, 报销凭证,退改服务,管控服务等详情数据作为事实表Fact, 建立各种维度表Dimension:订单基础数据、产品基础数据、账户配置快照、行程信息、支付费用信息、TMC管控详情等。

2)核心资源隔离和保护、确保关键流程高可靠性,支持单量的快速增长

随着运维能力和分布式系统的发展成熟,业务系统的API和微服务较容易水平扩展。性能瓶颈不容易解决主要是在DB层面,并不能轻易的扩展分库分表或者引入新的存储服务。订单库和online DB是整个订单系统交易的基础,降低数据库IO压力、保护关键路径和确保订单系统全流程的驱动顺畅需要尽可能的减少DB执行负荷重的任务:通用数据查询、列表查询、复杂查询、聚合查询、模糊查询、高频查询等,而这些场景基本上做到NRT(near real-time)准实时输出即可。OCS通过冗余落地的准实时数据和提供一系列的API服务达到隔离核心资源的目的。

3)大幅降低客户端学习成本、接入成本,支持业务的快速迭代

一个订单从预定到最终的结算经过较多的子系统和数据处理,整个订单生命周期产生的数据大而全、零碎、分散在各个DB和各个子系统,对于client端接入需要感知较多的逻辑和熟悉众多的交互API,不仅学习成本高,同样一个场景在不同的产线需要多次实现。

对于较多的场景如列表查询、基本数据查询、交叉推荐查询等通过数据的统一抽象、融合、落地实现流程统一、API统一、交互统一大幅简化接入端的成本,包括产品、BI、业务的取数规则等。

Order Consolidation Service
3.2 Order Message - 客户端减负

设计API和服务时通常考虑较多的是API如何设计、交互、传参、client需要遵循的各种规则,较少的考虑client可以不做什么,client在享受服务的同时应该可以不做一切可以不做的事情。传统上消息发送服务设计为提供不同消息的发送接口,由各客户端根据各自的业务流程处理指定模板、类型、动态内容等调用对应的接口, 这个看起来是符合高内聚、低耦合的通用设计原则的。

原流程发送一个消息需要client端调用多个数据源查询数据、各种逻辑判断、消息类型、发送场景、模板处理等。新的设计将各个消息抽象为产线,场景模式对应一套配置和一套数据源API(统一为key-value输出),注册在消息中心,类似网卡插入主板后,需要安装驱动程序实现可插拔的方式大幅减少client端的工作,client只需要传一个场景即可实现一系列消息的发送。根据实际的业务场景,需要根据下面6种场景的排列组合,触发一个或多个消息的发送。

1)发送场景:创单、支付、确认客户、出行、成交等。

2)发送模板:支付类型、订单处理阶段、订单状态等。

3)发送语言:中文简体、中文繁体、英文、日文、韩文等。

4)发送类型:短信、邮件、微信、企业消息、站内信、手工操作等

5)收 件 人: 联系人、预订人、出行人、授权人。

6)数据来源:订单数据、成本中心子系统、审批子系统、账户配置子系统,消息附件等

通用消息中心-配置服务
3.3 Order Conductor - 服务编排

1)服务拆分和边界确定之后,系统之间的交互同样需要统一的规则和整体上的设计。尤其需要避免单一事件触发后,需要调用一系列的API和较为复杂的交互流程,导致调用链过长,服务之间相互调用、耦合严重等问题。

  • 每个服务都需要感知下游服务;
  • 业务规则治理和调用的灵活性、扩展性较差;
  • 不能做到真正意义上的事件驱动;

2)服务编排是根据业务逻辑,以串行、并行和分支等结构编排多个API及函数服务为工作流,达到统一调度和指挥各个业务子系统,同时更加有利于事件驱动机制的落地。极大简化了多个服务之间组合调用的开发和运维成本,更加专注于业务本身。要实现服务编排、自由组合、灵活扩展满足下面的条件是基本前提。

任务原子化:每个接口或API拆分合理、职责单一,实现任务的原子性。

数据私有化:每个任务都有一套对应的数据表,并且仅自己本身可以操作和访问。如果没有实现数据库和资源的隔离,那任务原子化也就不存在,需要避免多个任务读写同一数据表和资源。

契约相通:任务编排,契约相通才能实现服务的统一调度。契约的设计必须是高度精简、覆盖全场景的。订单系统全流程驱动中一定会有的是:订单号、场景。如事件:退票完成,各被调用的服务通过数据反查获取数据,实现服务之间通信的契约大幅简化, 而不是依赖调用端的各类传参。

事件驱动:定义全流程统一的事件列表枚举,非查询类的操作完成后触发其对应的事件通知编排服务中心,根据流程配置统一调度实现服务编排。尤其需要避免的是各个服务之间相互直接调用,相互订阅广播通知导致系统的耦合和边界不清晰,以及服务扩展较差。

服务幂等性:服务的统一调度,不可避免的会出现多次被调用和触发的可能,每个原子性任务都需要根据自己的业务支持幂等性。

重试机制:与服务幂等性相辅相成、缺一不可,编排服务中心需要统一调度,就需要在配置和设计上支持任务的可重试、可降级和最终到达。

DB访问降频:由于任务的原子性拆分,每个任务执行依赖的数据都需要调用详情查询服务获取需要的数据,即使是相同的数据也是需要各自获取导致数据库IO压力的大增。针对这个场景有很多种方案。

  • 通过建立全局的thread context查询一次,各子线程或分支线程统一依赖降低DB的频繁访问。优缺点很明显的方案,维护和数据隔离较差,尤其是跨线程、子线程存在数据被篡改的风险和其他问题;
  • 数据冗余、准实时同步、隔离核心资源以offline思维降低订单库和online DB的访问。通过维度建模,满足基本查询、通用查询、高频查询等场景实现流量的分流。由于统一的抽象,对于数据的输出更加有利于服务端的缓存建立。比如我们采用上一段落介绍的OCS方案。
Order Conductor
3.4 Order Fulfillment - B端场景

B端用户需求与传统的企业级软件有非常多的相似点,定制化和多样性是最为显著的共同点。商旅B端客户即使是基本的功能:支付流程、发单方式、管控流程、通知内容等都有显著的差异。以订单系统酒店产线举例,下面的订单属性和订单状态各个值排列组合出50种以上需要处理的流程,这仅是酒店一条产线。

订单属性:订单类型、授权顺序、支付方式、垫资方式、配置流程、国际国内、三方协议等,

订单状态:已提交、未提交、授权通过、授权拒绝、已支付、已出票、已取消、确认客户等;

不仅微服务之间需要统一调度和流程配置,而对于每个应用或每个服务同样如此。订单系统仅通过工作流驱动并不能满足要求,其最为重要的属性就是订单状态、订单事件等触发和驱动订单系统的流转。通过开发基于订单状态、订单事件结合工作流开发流程引擎达到可配置、可扩展、易维护的目的,而又避免引入较笨重的开源工作流引擎达到“够用即可”:

  • 有状态、可重试、支持幂等,
  • 任务持久化、可配置、可扩展、易维护,
  • 基于状态、事件的统一处理流程;

流程引擎

状态驱动 流程引擎

四、论述与总结

什么是好的设计?
  • 一个好的设计不是还能增加什么,而是不能再减少什么。
  • 一个好的设计不是客户端需要做什么,而是可以不做什么。
  • 一个好的设计是自动化一切可以自动化的东西。
  • 一个好的设计是标准化一切可以标准化的流程。
什么应该横向?什么应该纵向?

什么应该纵向? 业务上共性少,差异大,对于客户需求需要快速响应,业务上需要快速试点和产品创新,正如图第一象限业务层展示的特点:特殊性,可变性。所以业务层在系统上应该纵向,隔离,但是在流程上标准化,可配置化,确保其灵活快速响应,尽可能少的涉及具体实现。

什么应该横向? 高确定性、高通用性功能应该横过来供不同的团队和子系统共享,作为各个产线子系统的基础,同时也是支持业务在已有系统上快速上线,创新,试点的平台。

当业务需要快速的上线和需要速度运行,快速推向市场的时候,我们应该让业务、产线分开快跑,应该把业务变成纵向的,独立成建制的往前快速推进,摧枯拉朽,策马狂奔。随着单量的增长和业务的延伸,尤其各产线趋同功能变多,定制化需求和创新性需求增多,合规性性要求越来越严格,系统就暴露出大量问题:大量低水平重复建设,重复开发,效率低下,运维困难,任何新功能,新产品上线都需要从轮子开始建造。当我们需要效率、要积累、要沉淀的时候,尤其需要在已有系统上快速创新、试点新的产品时候,就非常明显要把有些东西横过来,从整体上规划和设计,让整个系统架构统一,流程统一成支撑体系包括技术沉淀,子系统中台化演进让其能够有办法共享给其他子系统和团队。

什么叫中台,为什么需要它?中台被过度神化过,也被极端的污名化。中台并无对错,主要取决于设计与边界。其被诟病主要原因:

1)圈地运动:什么都能做,什么都可以做,基于不能重复建设的原则各client被强制要求接入。基于原则而实际上是无原则的开发模式,导致系统臃肿、边界不清晰、耦合严重,不能通用而被通用只能通过给client制定各种条条框框和特殊定义来约束系统之间的交互、接入成本和沟通成本高,反映不够灵活,对于创新型的业务不能快速的支持,需要反复的沟通和规则制定,不仅没有享受到便利,反而成为了负担。是一种明显的山头主义,势力范围的划分。

2)代理式中台: 高举高内聚、低耦合的伟大旗帜,这也不能做,那也不能做,将client视为负担而非流量。client需学习各种接入规则和传参契约,接入成本高和使用复杂,不同渠道稍有逻辑不一致需要client 自行处理,某种意义上说仅起到转发的作用。中台实际上是一个横向策略。如果你要速度,要快速,要机动灵活,一定是这根杆子从上到下,都是一个人或一个团队负责,这是最快的。而如果client 需要感知各种流程,等于是逻辑分散,没有做到真正的抽象和下沉。业务中台需从整体上设计基于配置化编程满足不同渠道的需求,而又不打破低耦合的原则,而非仅仅是个代理,是个类似基础架构的网关。

3)资源垄断:垄断了资源输出和对外交互,client除了接入无其他选择。同时又受制于团队体量,团队话语权,ROI等各种因素得不到排期或列为低优先级任务。

0 人点赞