TOP100summit:【分享实录-途牛】价格中心系统的优化之路

导读:价格中心系统是途牛网众多系统中很重要的一个,几乎所有的售卖价格计算都由此系统产生。初期由于缺乏合理设计,无法及时满足业务高速增长的需求,出现价格计算速度慢、系统不稳定、增加功能困难等问题,系统面临巨大压力,在这种情况下我们启动了系统优化。经过优化系统的稳定性得到巨大提升,特殊场景下的性能提升为原来的几十倍,平均性能提升为原来的几倍。本文将就途牛价格中心系统的优化实践作深入分享。

一、旅游产品的特点

旅游产品是各种资源的整合,包括机票、酒店、门票、签证等所有旅行过程中使用到的各种服务。如图1,以三亚五日游这个产品为例,第一天飞到三亚并入住酒店,然后各种玩,第五天飞走,其中涉及到很多种服务。



图1 三亚五日游

在计算过程中需要处理这些场景:

产品的每个团期都需要排列选出最低价格的资源组成;

同一资源在不同的团期下价格可能不同(淡旺季,不同供应商报价不同,资源库存影响);

需要考虑同行程下的酒店连住天数,选出连住多天总价最便宜的酒店资源;

某些资源只有计算时通过多个条件筛选才知道其价格(如飞机散客票);

某些产品配置很多资源,如上千酒店。

因此,得到一个产品的价格需要很多的数据和计算。另外,资源的价格会频繁变化,比如机票的价格;或者因一批低价格的库存卖光了那么这个资源就无法再以低价售卖,价格就要涨。这些情况就会使价格发生变化。这种变化的频率很高,加上资源的数量较多,每天达到几百万次,且每个资源会被多个产品使用,所以每个资源变化都会引起多个产品的价格计算。这些因素造成价格中心面临两大难点:计算复杂、计算量大。

二、重构实践

在整个系统优化重构的过程中我们遇到了很多问题,正是这些问题得到了很好的解决,才使得系统优化成功。

2.1 系统最关注什么?

系统最关注的是响应时间?吞吐量?并发数?功能正确?还是稳定性?这些都是我们关注的,但当鱼和熊掌不可兼得的时候,我们更关注什么?这个问题关系到我们的技术选型以及更深入地理解系统。

举个例子,假设是一个对响应时间要求很高的系统,一个请求要求立即得到响应,那么就不太适合过多的异步处理方式。价格中心系统更专注吞吐量,即资源的价格变化必须能够反映到产品的价格上。在响应时间方面不是强制要求必须毫秒级或秒级,事实上只要几秒内可以变过来就可以,因为客人在价格变化的一瞬间预定产品的概率是很低的。某些场景下响应时间可以更长,比如夜里刷价格,所以我们完全可以采用异步处理的方式。

2.2 系统的瓶颈在哪里?

系统的性能优化往往落到CPU/IO/内存这三个里面(图2)。那我们的系统瓶颈在哪里?



图2 CPU/IO/内存

这个问题决定着系统优化该往哪走。拿价格中心系统举例,假如CPU很低、IO时间很长、内存使用量很大,就能得出一个结论:速度上不去,时间都消耗在IO上了,CPU得不到有效利用。进而得出对策:提升并发线程数,把CPU打上去。但是这样做会带来内存的消耗增加,所以不合适。

经过分析就会有疑问:这么多IO读取出来的数据占用了内存,而CPU较低,是不是有冗余的数据读取?带着这个疑问再次检查发现确实存在,这只是一个附带的发现,经过分析我们基本确定的方向是降IO,通过控制计算规模降低内存使用量。

2.3 抽象领域模型



图3 领域模型

抽象领域模型为了解决什么问题?

原系统中的功能逻辑混乱,耦合严重;

大家对一个功能如何实现缺乏统一的认识;

产品和开发之间的沟通缺乏统一的语言。

领域模型凝聚了领域知识的元素,是系统运行不可或缺的成员,但领域模型不具备系统整体运行所具有的知识,只负责模型内部的逻辑。

系统至少要有领域层和应用层,领域模型在领域层完成各自的逻辑实现,应用层再将这些领域模型关联起来。我们经过抽象将系统提炼出大的领域模型为资源、产品、促销、交通、基础等(图3)。

2.4 微服务的划分

划分微服务是为了解决以下问题:

功能耦合;

数据耦合,即没有划分和保护的数据被多个功能模块依赖,导致一处数据的改动将波及大面积的功能模块;

升级的影响范围,因某个功能需要扩充实例将导致所有实例都重新部署,某一个小功能引起的上线也将是整个应用的上线。

具体做法是:通过抽象,基于之前建立的领域模型划分出符合系统特点的微服务,每个服务应对系统的一类复杂度。不同的微服务之间在逻辑功能、被调用频率、依赖的数据、实例个数等方面都是不一样的。我们的系统经过提炼划分为资源管理服务、产品价格管理服务、计算控制服务、促销计算服务、价格查询服务。

微服务之间的配合推荐采用异步消息队列的方式,这样服务只关心自己的处理逻辑,而不用关心自己产生的数据被谁以哪种方式处理,服务之间是互相不感知的。

当然这是这个系统的特点决定的,因为可以稍微牺牲一点响应时间。通过RESTFUL接口的方式调用也是不错的选择,这时服务提供方一定是提供通用的服务,即不会在自身逻辑里出现“当调用者是XX的时候如何处理”这种判断。

2.5 控制每次计算的粒度

我们的系统还面临一个特殊的问题:一个旅游产品由多种资源构成,比如酒店/门票/机票等,正如前文所说,一个产品的价格计算可能有多种资源和选择。

比如一个三亚5日游的产品,可以从北京/南京/上海/西安等多个城市出发(如图4),这些城市的航班都不一样,会有许多航班线路可供选择,数量与可以去三亚的城市数量成正比。而这种城市数量会有多少,程序事先无法知道,完全由业务人员的产品设计决定。

这种情况带来的问题就是:计算一个产品的价格时,不容易预先知道计算量大小,有时城市很少那么计算量就小,而有时出发去目的地的城市很多那么计算量就很大。



图4 多地出发

这至少会带来两个麻烦:

一是系统的能力不容易评估,因为每次技术的粒度都不得而知,没有统一的衡量标准;

二是内存会有很大的浪费。

经过分析,我们认为从北京出发到三亚的产品价格和从上海去三亚的产品价格不要求在同一个时间产生,所以可以分开计算。这样就可以缩小每次计算的粒度,使每次计算的粒度控制在一个相对原子的大小上。这就大大降低了内存的使用,同时也加速了计算速度。

2.6 无缝升级

每次升级都要考虑如何做到对外无感知、同时可回滚的设计,将风险控制在最低。一般如果涉及到数据库结构变化,可以选择双写,新结构数据库稳定之后再将老库作废。

但我们遇到一个问题:希望改变输入规则,如图5,将配置方式由蓝色方式改为绿色方式,因为绿色的方式更灵活,更符合业务需求,但问题是线上已经有大量的蓝色方式数据配置,把这些数据全删掉再按照绿色方式配置上去是不可能的。

那么怎么做到平滑切换?除非找到一个算法将蓝色配置映射成为绿色,遗憾的是没有这种方法。



图5 改变输入规则

我们的做法是将蓝色数据配置和绿色数据配置都向一个固定的模型去映射,把它们全部视为规则,那么蓝色和绿色就是四种规则,如图6所示。这样就做到了新老数据共存和平滑升级。



图6 规则

三、 技术实现

3.1 扩展立方体

随着业务对计算资源、存储资源的需求不断增加,另一方面摩尔定律失效,人们无法找到拥有巨大资源又价格合适的计算机来支撑自己的业务,所以转而通过大量廉价的小型计算机一起工作来达到超级计算机的目的。当计算需求增加,只要增加小型机的数量,就可以近似线性地增加系统性能。扩展性,是分布式系统的重要考量因素。



图7 扩展立方体

在图7所示的扩展立方体中:

X轴代表每个结点同质(同类型、同功能),只要增加结点数就可以增加系统处理能力;

Y轴代表基于不同业务的扩展;

Z轴代表不同用户类型(优先级、地域等)的扩展。

目前我们的系统主要是基于X轴和Y轴的扩展。

我们的存储水平扩展方式是分表分库,但分库容易带来跨库Join的问题。我们是基于产品ID作为分表关键字的,基本上没有产品之间需要关联计算价格的场景,所以基本规避了跨库Join的问题。

3.2 RESTFUL调用组件

在服务治理上我们开发了一个RESTFUL的调用组件(图8),通过它来解决服务发现/调用管理的问题。服务提供者将自身标识注册到注册中心,服务消费者通过注册中心获得可提供服务的列表。



图8 RESTFUL调用组件

3.3 消息队列

我们在较多场景下使用了消息队列。消息队列的一个好处是解耦,在发送端看只需要将消息送到队列,send and forget,不需要关注消息会被谁以哪种方式处理。

另外,负载均衡可以在消费侧完成,发送方无感知。这样就可以动态地增加或减少消费者数量。当然这样做也基本上要求业务的设计是无状态、异步化的。

四、案例总结

4.1 架构是为业务服务的。

评价架构的优劣要看是否更好地支撑业务发展,而不是使用了什么技术。只有最适合本业务特点的架构和技术选型才是好的。所以只有深刻理解业务本身才有好的架构。

诸多开源中间件的出现,减少了业务人员需要自己实现的功能(除非开源的项目不满足要求),而架构师也从设计实现更多地转向对业务需求的判断,从而进行架构决策和技术选型与模式的运用。

本案例是百亿次计算量的系统优化,其实第一个应该讨论的话题就是这100亿次是否是必要的。事实上我们确实通过优化降低了计算量,也降低了单次计算规模,这些都是建立在对业务的理解之上的。所以架构永远是从业务出发。

4.2 服务之间的边界要清晰,尽量耦合小。

DDD的思想可以帮助我们找到服务边界。

4.3 尽量异步化设计,这样的耦合很小,逻辑上更清晰。

异步的系统要稳定得多,某一个系统出现瓶颈的时候还有消息中间件帮助缓冲。同步的调用在等待时(线程会阻塞)会间接影响并发和吞吐量。另外,异步的系统在升级时会更容易,系统之间的限制要小一些,可以在队列里面积压一些。

4.4 无状态省去了很多负载均衡的烦恼, 不需要做黏性。

可以很容易地做到水平扩展.有状态系统在水平扩展时是非常痛苦的。

4.5 小步迭代,可回滚低风险。

我们做到了每一次上线都是可回滚的,数据库的设计在上线后的一段时间内都是向下兼容的。而且新老数据是可以并存的。

11月9-12日,北京国家会议中心,第六届TOP100全球软件案例研究峰会,刘晓涛:途牛研发总监将分享《天下武功唯快不破-微服务实践快速响应瞬息万变的市场》。

TOP100全球软件案例研究峰会已举办六届,甄选全球软件研发优秀案例,每年参会者达2000人次。包含产品、团队、架构、运维、大数据、人工智能等多个技术专场,现场学习谷歌、微软、腾讯、阿里、百度等一线互联网企业的最新研发实践。

TOP100大会将于11月9日-12日在北京国家会议中心举办,在现场通过对案例的复盘总结,分享成功者背后的经验和方法。大会开幕式单天体验票免费入口。

推荐阅读更多精彩内容