本篇文章内容来自2016年TOP100summit途牛旅游网高级架构师,技术委员会开发组长赵国光的案例分享。
编辑:Cynthia

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

一、旅游产品的特点

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

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

● 产品的每个团期都需要排列选出最低价格的资源组成;
● 同一资源在不同的团期下价格可能不同(淡旺季,不同供应商报价不同,资源库存影响);
● 需要考虑同行程下的酒店连住天数,选出连住多天总价最便宜的酒店资源;
● 某些资源只有计算时通过多个条件筛选才知道其价格(如飞机散客票);
● 某些产品配置很多资源,如上千酒店。

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

二、重构实践

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

2.1 系统最关注什么?

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

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

2.2 系统的瓶颈在哪里?

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

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

2.3 抽象领域模型

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

● 原系统中的功能逻辑混乱,耦合严重;
● 大家对一个功能如何实现缺乏统一的认识;
● 产品和开发之间的沟通缺乏统一的语言。

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

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

2.4 微服务的划分

划分微服务是为了解决以下问题:
● 功能耦合;
● 数据耦合,即没有划分和保护的数据被多个功能模块依赖,导致一处数据的改动将波及大面积的功能模块;
● 升级的影响范围,因某个功能需要扩充实例将导致所有实例都重新部署,某一个小功能引起的上线也将是整个应用的上线。

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

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

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

2.5 控制每次计算的粒度

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

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

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

这至少会带来两个麻烦:
● 一是系统的能力不容易评估,因为每次技术的粒度都不得而知,没有统一的衡量标准;
● 二是内存会有很大的浪费。

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

2.6 无缝升级

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

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

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

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

三、技术实现

3.1 扩展立方体

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

在图所示的扩展立方体中:
● X轴代表每个结点同质(同类型、同功能),只要增加结点数就可以增加系统处理能力;
● Y轴代表基于不同业务的扩展;
● Z轴代表不同用户类型(优先级、地域等)的扩展。

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

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

3.2 RESTFUL调用组件

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

3.3 消息队列

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

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

四、案例总结

4.1 架构是为业务服务的。

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

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

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

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

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

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

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

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

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

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

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

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

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

TOP100summit:【分享实录-途牛】价格中心系统的优化之路的更多相关文章

  1. TOP100summit:【分享实录-美团点评】 业务快速升级发展背后的系统架构演进

    本篇文章内容来自2016年TOP100summit美团●大众点评高级技术专家,酒店后台研发组eHome团队负责人许关飞的案例分享.编辑:Cynthia 许关飞:美团●大众点评高级技术专家,酒店后台研发 ...

  2. TOP100summit:【分享实录】爆炸式增长的斗鱼架构平台的演进

    本篇文章内容来自2016年TOP100summit斗鱼数据平台部总监吴瑞城的案例分享. 编辑:Cynthia 吴瑞诚:斗鱼数据平台部总监 曾先后就职于淘宝.一号店. 从0到1搭建公司大数据平台.平台规 ...

  3. [分享]运维分享一一阿里云linux系统mysql密码修改脚本

    [分享]运维分享一一阿里云linux系统mysql密码修改脚本       大象吃豆子 级别: 小白 发帖 12 云币 27 加关注 写私信   只看楼主 更多操作楼主  发表于: 2014-09-3 ...

  4. 九思老客户分享:部署OA办公系统的四大意义

    原文:http://www.jiusi.net/detail/472__776__4009__1.html 关键词:OA办公系统.oa系统 .九思OA 九思老客户分享:部署OA办公系统的四大意义 当今 ...

  5. 微博MySQL优化之路--dockone微信群分享

    微博MySQL优化之路 数据库是所有架构中不可缺少的一环,一旦数据库出现性能问题,那对整个系统都回来带灾难性的后果.并且数据库一旦出现问题,由于数据库天生有状态(分主从)带数据(一般还不小),所以出问 ...

  6. 关于web系统整体优化提速总结

    关于web系统整体优化提速总结 一.背景 随着公司业务的拓展,随之而来就是各种系统横向和纵向的增加,PV.UV也都随之增加,原有的系统架构和模式慢慢遇上了瓶颈,需要逐步的对系统从整体上进行改造升级,通 ...

  7. 百度APP移动端网络深度优化实践分享(三):移动端弱网优化篇

    本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为<百度App网络深度优化系列<三>弱网优化>,感谢原作者的无私分享. 一.前言 网络优化解决的核心问题有三个 ...

  8. 从游击队到正规军:马蜂窝旅游网的IM系统架构演进之路

    本文引用自马蜂窝公众号,由马蜂窝技术团队原创分享. 一.引言 今天,越来越多的用户被马蜂窝持续积累的笔记.攻略.嗡嗡等优质的分享内容所吸引,在这里激发了去旅行的热情,同时也拉动了马蜂窝交易的增长.在帮 ...

  9. web系统整体优化

    关于web系统整体优化提速总结   关于web系统整体优化提速总结 一.背景 随着公司业务的拓展,随之而来就是各种系统横向和纵向的增加,PV.UV也都随之增加,原有的系统架构和模式慢慢遇上了瓶颈,需要 ...

随机推荐

  1. 基于Centos7.5搭建Docker环境

    docker很火,基于容器化技术,实现一次编译到运行.实现运行环境+服务的一键式打包! 00.部署环境 centos7.5(基于vmware搭建的测试环境,可以跟互联网交互,桥接方式联网) docke ...

  2. 解决 Firefox 下载文件名乱码扩展 ReDisposition

    作者 muzuiget  发布 2013-03-13 19:23  标签 redisposition Firefox 下载文件名乱码问题由来已久,偶然一两次还可以手动改名,批量下载时简直要亲命,最终我 ...

  3. .NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)

    本随笔续接:.NET 同步与异步之锁(ReaderWriterLockSlim)(八) 之前的随笔已经说过.加锁虽然能很好的解决竞争条件,但也带来了负面影响:性能方面的负面影响.那有没有更好的解决方案 ...

  4. Swift Guard 守护

    前言 guard 语句和 if 语句有点类似,都是根据其关键字之后的表达式的布尔值决定下一步执行什么. guard 语句只会有一个代码块,不像 if 语句可以 if else 多个代码块. guard ...

  5. Effective Java 第三版——48. 谨慎使用流并行

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  6. SNF快速开发平台MVC-EasyUI3.9之-WebApi身份验证问题解决方案

    在我们的整体bs框架当中前端采用的是MVC+WebApi的处理方式.WebApi使用起来确实很方便但也会有新的麻烦事,就是身份验证. 如果没有启用身份认证,那么任何匿名用户只要知道了我们服务的url, ...

  7. 去除移动端alert/confirm的网址(url)

    移动端的alert.confirm都会显示来源的url,影响体验 下面的代码将alert和confirm重写了一遍,可去除url  参考了网上代码,完善了confirm不同状态跳转   示例代码: & ...

  8. linux每日命令(4):pwd命令

    Linux中用 pwd 命令来查看"当前工作目录"的完整路径. 简单得说,每当你在终端进行操作时,你都会有一个当前工作目录. 在不太确定当前位置时,就会使用pwd来判定当前目录在文 ...

  9. 编译错误 ld: cannot find -lz

    [时间:2017-04] [状态:Open] [关键词:makefile,gcc,linux,ld,libz.so] 在新安装的centos上编译程序遇到上述问题,找了半天,原来是没有安装 需要安装z ...

  10. 【转】HTML embed标签使用方法和属性详解

    一.基本语法 代码如下: embed src=url 说明:embed可以用来插入各种多媒体,格式可以是 Midi.Wav.AIFF.AU.MP3等等,Netscape及新版的IE 都支持.url为音 ...