首先说一个概念GDS(GlobalDistributionSystem)即“全球分销系统”,是应用于民用航空运输及整个旅游业的大型计算机信息服务系统。通过GDS,遍及全球的旅游销售机构可以及时地从航空公司、旅馆、租车公司、旅游公司获取大量的与旅游相关的信息。
机票的源数据都来自于各种GDS系统,但每个GDS却千差万别:
服务器遍布全球各地:国内GDS主要有中航信的IBE系统、黑屏数据(去机场、火车站看到售票员输入的电脑终端系统),国际GDS遍布于东南亚、北美、欧洲等等。
通讯协议不一样,HTTP(API、Webservice)、Socket等等。
服务不稳定,尤其国外的GDS,受网路链路影响,访问很慢(十几分钟长连接很常见),服务白天经常性挂掉。
更麻烦的是:GDS一般付费按次查询,在大搜索量下,实时付费用它,估计哪家公司都得破产。而且就算有钱,各种历史悠久的GDS是无法承载任何的高并发查询。更苦的是,因为是创业公司,我们大都只能用免费的GDS,它们都是极其不稳定的。
所谓便宜没好货,最搞笑的一次是:曾经在米国的GDS挂了一、两天,技术们想联系服务商沟通服务器问题。因为是免费,就没有所谓的服务商一说,最后产品总监(算兼职商务吧)给了一个国外的网址,打开是这家服务商的工单页面,全英文,没有留任何邮箱。提交工单后,不知道什么时候回复。可以想想当时我的心情......
虽然有那么多困难,我们还是找到一些技术方案,具体如下。
引入NIO框架
考虑GDS访问慢,不稳定,导致很多长连接。我们大量使用NIO技术:
NIO,是为了弥补传统I/O工作模式的不足而研发的,NIO的工具包提出了基于Selector(选择器)、Buffer(缓冲区)、Channel(通道)的新模式;Selector(选择器)、可选择的Channel(通道)和SelectionKey(选择键)配合起来使用,可以实现并发的非阻塞型I/O能力。
NIO并不是一下就凭空出来的,那是因为Epoll在Linux2.6内核中正式引入,有了I/O多路复用技术,它可以处理更多的并发连接。这才出现了各种应用层的NIO框架。
HTTP、Socket都支持了NIO方式,在和GDS通信过程中,和过去相比:
通信从同步变成异步模式:CPU的开销、内存的占用都减低了一个数量级。
长连接可以支持更长超时时间,对国外GDS通信要可靠多了。
提高了后台搜索服务器的稳定性。
消息队列
为了异步完成航班数据更新到缓存,我们采用消息队列方式(主备AMQ)来管理这些异步任务。具体实现如下。
有一个问题,如何判断缓存过期呢?这里面有一个复杂的系统来设置的,它叫Router。资深运营会用它设置可以细化到具体一个航段的缓存有效期:比如说北京—NY,一般来说买机票的人不多的,航班信息缓存几天都没有问题。但如果北京—上海,那可能就最多5分钟了。
Router还有一个复杂工作,我叫它“去伪存真”。我们长期发现(真是便宜无好货),某些GDS返回航班数据不全是准确的,所以我们会把某些航线、甚至航班指定具体的GDS数据源,比如北京—新加坡:直达航班数据来自于ABAQUS,但是中转数据,北京—上海—新加坡,或者北京—台北—新加坡从IBE来会精准些。
因此Router路由规则设计要很灵活。通过消息队列,也其实采用异步化方式让服务解耦,进行了很好的读写分离。
GDS服务抽象虚拟Node
为了管理好不同GDS资源,较大的利用它们。我们把GDS服务器抽象成一组Node节点来便于管理,像下面这样: