微信号:infoqchina

介绍:有内容的技术社区媒体

Algolia通往高可用搜索API的狂暴之路|实战案例

2015-07-31 11:25 谢丽

Algolia是一家提供托管式搜索API的初创企业。作为一家年轻的企业,其架构令人印象深刻:


其高端专用机器托管在世界上13个地区的25个数据中心里 ;

其master-master配置至少会在三台不同的机器上复制他们的搜索引擎 ;

每个月处理超过60亿次查询 ;

每个月接收和处理超过200亿次写操作。


Julien Lemoine是Algolia的联合创始人兼首席技术官。他将以时间为线索,介绍他们如何分15步构建出如此高可用的基础设施。


在开始介绍架构之前,Julien比较了云和裸机。对于大多数情况而言,云基础设施都是一个不错的方案。它易于部署,而且本身提供了高可用性。而基于裸机的基础设施需要他们自己构建高可用性。但选择裸机基础设施,他们可以购买性能更好的硬件,而且与所获得的性能相比,价格也算相当便宜了。


步骤1:2013年3月

这个阶段,他们的搜索服务API内测版本开始运行。基于对产品市场前景的自信,他们在两个不同的地点(加拿大东部和欧洲西部)分别部署了一台机器。每台机器根据地点为不同的用户提供服务。此时,他们百分百关注性能,时钟频率是他们决策时重点考虑的一个因素,因为就同一代CPU而言,时钟频率与搜索引擎的搜索查询速度有直接关系。索引由单独的进程完成,而且优先级较低;而所有的查询都直接在nginx内处理,并且优先级最高,即它可以占用更多的CPU时间,这样可以有效地处理流量峰值。让他们引以为豪的是,其中一个内部测试用户执意用Algolia的服务替换了其当时正在使用的解决方案。


步骤2:2013年6月

经过三个月的开发和大量的测试,他们在Beta测试中引入了高可用性,其思想是:用集群取代了单机,集群由三台相同的机器组成,每台机器都完美地复制了所有数据,均可以作为master。也就是说,每台机器都可以接受用户的写操作,每次写操作都会触发一个一致性保证机制。另外,基于前期的测试,他们发现:


32G的内存不够用,单是索引进程有时候就会用掉10G ;

磁盘空间不够用,为了处理节点失败,机器需要将多个任务保存在磁盘上。


由于内存需求增加,他们将机器由Xeon E3系列换成了Xeon E5系列,因为前者只能处理32G内存。而考虑到时钟频率的重要性,他们决定采用Xeon E5 1600系列。至此,他们已经提供了高可用性。


与此同时,他们还测试了多种负载均衡和故障检测方法,发现所有的硬件负载均衡器均让他们几乎不可能使用多个提供商。于是,他们在API客户端中实现了一种基本的重试策略,即在开发的时候确保每个API都能够访问三台不同的机器。


步骤3:2013年8月

他们将API客户端的数量增加到10种, 包括JS、Ruby、Python、PHP、Objective-C、Java、C#、Node.js等。而且,他们尽量避免自动生成代码,人工开发了API客户端。2013年8月,他们在上述两个地点(加拿大东部和欧洲西部)正式推出了其搜索服务API。每个地点一个集群,每个集群包含三台相同的主机。主机换成了E5-2687W,内存加倍(128G),并且使用了更好的SSD。这主要是因为他们观察到,内存不足以缓存所有的用户数据,而SSD成为索引速度的瓶颈。接下来,他们又重点实现了“可用区域(availability zone)”。关于这一点,Julien并未提供细节信息。


步骤4:2014年1月——部署会危及高可用性

在这个阶段,他们主要关注如何实现敏捷开发而又不以牺牲稳定性为代价。为此,他们开发了一个测试套件,其中包含6000多个单元测试和200多个非回归测试。但还是不够,即使一项新特性通过了所有的测试,仍然可能在生产环境中引入Bug,比如,曾有个Bug导致了8分钟的索引操作中断。多亏他们在设计架构时实现了搜索查询与索引操作的分离,前者才没有受到影响。不过,这个问题让他们确定了其高可用设置中的一些问题:


回滚要快,为此,他们实现了通过单行命令回滚;

部署脚本需要执行完整性检查,如果有错误,就自动回滚;

不能仅仅因为测试通过就部署到生产环境中的所有集群上,他们会按照顺序依次部署测试集群、社区集群(面向免费用户)、付费用户集群。


现在,当有新特性需要测试时,他们会选择一个集群组用于测试,并按照下面的步骤部署:


1.向所选集群组中所有集群的第一台机器部署;

2.监控24小时,然后向所选集群组中所有集群的第二台机器部署;

3.监控24小时,然后向所选集群组中所有集群的第三台机器部署;

4.几天后,向生产环境中的所有集群部署。


通过这种方法,他们可以在几个小时内检测到几乎不可能通过单元测试发现的Bug。


步骤5:2014年3月——处理高负载的写操作

他们开始解决一个新问题:延迟。他们位于亚洲的集群延迟过高。为了测试市场反应,他们决定将机器部署在AWS上。他们并不愿意这样做,因为即使使用AWS提供的最好的CPU,搜索查询的性能仍然比使用E5-2687W CPU低大约15%。不过,为了缩短测试推出时间,他们这样做了。但是,他们尽量确保不引入对AWS的依赖,以便后续可以迁移到其它提供商。


同时,欧洲的客户开始抱怨搜索查询延迟增加。他们很快就发现,那与索引操作大幅飙升有关。这初看起来有些不可思议,因为他们在设计时实现了索引操作和搜索查询的分离,但调查之后他们发现,确保集群间一致性的一致性算法在处理写操作时存在瓶颈。当瓶颈出现时,它会阻塞HTTP服务器线程,导致搜索查询等待。为了修复这一问题,他们在一致性操作之前实现了一个队列,由它接收写操作,然后将它们批量发送给一致性操作算法。这样,写操作就不会冻结HTTP服务器线程了。此后,他们再也没有遇到集群冻结的情况。


步骤6:2014年4月——网络高可用几乎不可能在一个数据中心里实现

2014年4月初,他们开始收到用户的抱怨。这些用户来自美国东部,但使用加拿大东部的集群,而美国西部的用户则没有受到影响。原来是一场车祸导致了加拿大和美国东部之间的网络路由路径发生了变化,而新路径的带宽不够,不可避免地出现了数据丢失。他们早先没有考虑这种情况,因此,当这种情况出现时,他们只能联系用户并说明情况。


他们认识到,需要基于多提供商、多数据中心和多网络提供商改进高可用性,实现一种真正的分布式基础设施。


步骤7:2014年7月——首次部署到两个数据中心

他们从最大的客户开始将机器部署到不同的数据中心(相距超过100公里)。这两个数据中心为同一个提供商所有。同时,根据先前的经验,他们将硬件进行了升级。虽然E5-2687W的CPU使用率就未到过100%,但他们还是升级到了使用下一代CPU的Xeon E5-1650v2。结果,他们的服务性能提高了将近15%。


步骤8:2014年8月——在美国部署服务!

2014年8月,他们在美国东部(弗吉尼亚州)和美国西部(加利福尼亚)通过一个新的提供商推出了服务。根据先前的经验,他们使用了同一提供商(不同的网络设备和电源装置)提供的不同的可用区域,并借助更低的延迟和更高的带宽改进了搜索体验。


步骤9:2014年10月——通过Chef实现自动化

随着机器数量不断增加,他们将管理工具改成了Chef。与使用Shell脚本相比,这会节省大量的时间。在配置数以百计的机器时,Chef非常有用,但也有缺点。他们曾经因为cookbook的输入错误而导致部分生产环境的服务器宕机。为了防止这类问题再次出现,他们决定将生产环境使用的cookbook分成两个版本:


第一个版本为稳定版本,部署到所有集群的第一台和第二台机器上;

第二个版本为生产版本,部署到所有集群的第三台机器上。


当修改cookbook时,他们首先会将修改应用到生产版本。经过几天的测试后,他们才会将修改应用到稳定版本。


步骤10:2014年12月——DNS时架构中低一个SPoF

随着时间推移,越来越多的用户抱怨他们的服务时断时续,尤其是在亚洲。通过调查他们发现,使用.io TLD是问题的原因所在。事实证明,同其它顶级域名(.net、.com和.org)相比,.io TLD选播网络中的可选地址更少,导致DNS服务器过载。用户有时候会在DNS解析时遭遇超时。于是,他们将.io TLD换成了.net TLD,并换了一个允许他们在algolia.io和algolia.net之间同步的DNS提供商,这使他们很容易保持向后兼容。迁移完成后,他们进行了广泛的测试,发现了几个对部分客户有影响的问题。DNS比他们想象的复杂,他们的测试并不全面。


这个问题让他们认识到,唯一的DNS提供商是一个SPoF(单点故障点),而他们的迁移行为实际上是非常危险的。因此,他们开始着手制定计划,消除架构中的SPoF。


步骤11:2015年2月——全球同步的基础设施

这个月,他们实现了自2014年4月开始就一直为之努力的目标,“将服务扩展到全球,更好地服务于用户”。他们的网络包含12个不同的地点:美国东部(弗吉尼亚)、美国西部(加利福尼亚)、澳大利亚、巴西、加拿大、法国、德国、香港、印度、日本、俄罗斯和新加坡。最重要的是,他们推出了“分布式搜索”特性。借助这个特性,用户只需几次点击就可以在他们的网络中选定需要自动复制数据的地点。用户使用的API保持不变,而查询请求会自动路由到最近的地点。这不仅降低了延迟,还提高了搜索基础设施的可用性。


据Julien介绍,他们的“分布式搜索网络(Distributed Search Network,DSN)”与CDN(内容分发网络)完全不同。他们不是在每个边缘地点缓存常用查询,而是存储一个包含所有数据的完整副本。边缘地点本身都可以响应任何查询。就是说,如果用户选择了三个接入点(美国东部、德国、新加坡),那么位于德国的接入点会响应欧洲用户的查询,位于新加坡的接入点会响应亚洲用户的查询,而位于美国东部的接入点则会响应美国用户的查询。


为了支持这种变化,他们修改了API客户端的重试逻辑。客户端会首先指向主机名APPID-dsn.algolia.net,后者会使用DNS将客户端请求路由到最近的地点。如果最近的主机不可用,那么为了能够返回下一个最近的地点,DNS记录会在1分钟内删除那台主机。这就是他们将每条记录的TTL设为1分钟的原因。如果遇到这种故障,那么他们的官方API客户端会通过在APPID-1.algolia.net、APPID-2.algolia.net和APPID-3.algolia.net上重试将流量重定向到“主区域(main region)”。他们认为,这种方法可以实现高性能与高可用性的最佳平衡。


步骤12:2015年3月——提高单个地点的高可用性

对于搜索和国际用户而言,分布式搜索网络极大的提高了可用性。而为了提高主区域的可用性,他们将美国的集群分布在两个完全独立的提供商那里:


两个位置相近的、不同的数据中心;

三台不同的机器——同以前一样,两台位于一个数据中心的不同的可用区域中,一台位于另一个数据中心;

两个不同的自治系统。


这样,他们可以选择将流量路由到另一个提供商。他们在提高单个地点的可用性方面迈进了很大一步。


步骤13:2015年4月——随机出现的文件损坏问题

这个月,他们开始注意到生产环境中随机出现的文件损坏问题,这是由部分SSD的TRIM实现中存在Bug导致的(具体原因参见这里)。这是个棘手的问题,他们花了一个月的时间来跟踪和定位。所幸,他们没有丢失任何客户数据,这主要得益于以下两个方面:


他们存储了数据的三个副本;

更重要的是,他们没有复制索引操作的结果,而是在每台机器上重复了用户的操作。这有效避免了问题向其它机器传播。


他们没有预见到这种问题,但使用独立的机器是他们能够将问题影响最小化的原因。因此,Julien强烈建议,任何需要高可用性的系统都要采用这种独立性。


步骤14:2015年5月——引入多个DNS提供商

他们选择NSONE作为一个DNS提供商,因为该提供商提供了很棒的DNS API,允许他们通过API针对每个用户配置查询的路由方式,并且支持edns-client-subnets,可以提供更准确的地理位置路由。


这里的挑战在于,他们需要引入第二家DNS提供商,而又不损失NSONE提供的强大功能。他们决定通过修改API客户端重试策略的方式引入。所有的API客户端都会首先连接 APPID-dsn.algolia.net,如果有问题,它们会在另一个提供商提供的顶级域名上重试。他们选择将AWS Route 53作为第二家提供商。如果有任何问题,API客户端将从APPID-1.algolianet.com、APPID-2.algolianet.com和APPID-3.algolianet.com中随机选择一个重试。这样,他们就在algolia.net域上保留了NSONE所有的地理位置路由特性,同时引入了第二个提供商在algolianet.com域上提供了更高的可用性。


步骤15:2015年7月——每个集群跨三个完全独立的提供商

虽然经过了一系列的扩展,但他们的基础设施并不能完全应对所有问题,这主要是因为Link/Router丢失数据包和路由泄露。在上个步骤中,他们改进了在美国的部署,构建了跨多个数据中心、多个自治系统和多个上游提供商的集群。不过,索引操作需要三台机器中的两台运行正常方可进行。当使用两个提供商时,如果其中一个宕掉,他们就会无法提供索引服务,但搜索服务仍然可用。正是因为这个原因,他们决定实现跨三个完全独立的提供商的集群。这让他们的基础设施超级冗余,但却同时提供了高可用的搜索和索引服务。


总之,构建高可用的架构是需要时间的。所以,作为初创企业,不用在开始的时候就担心基础设施不够完美。但是,应该尽早考虑如何扩展基础设施,Julien甚至建议在Beta测试之前就开始。


相关文章推荐

这个曾价值10亿美元的应用,这样做热门推荐|实战案例

简化你的Java代码,让工作更高效|语言

如何正确看JS,掌控你的职业线?|语言

怎么用API网关构建微服务|架构

投稿请联系:lillian@infoq.com

版权归属InfoQ,禁止私自抄袭转载。

回复关键词React | 架构师 | 运维 | 云 | 开源 | 物联网 | Kubernetes | 架构 | 人工智能 | Kafka | Docker | Netty | CoreOS | QCon | Github | Swift | 敏捷 | 语言 | 程序员



有话想说?!戳“写评论”👇

 
InfoQ 更多文章 Facebook如何实现PB级别数据库自动化备份 学术派Google软件工程师Matt Welsh谈移动开发趋势 Spotify为什么要使用一些“无聊”的技术? 妹纸们放假了,汉纸们做啥? 大多数重构可以避免
猜您喜欢 ISO26262?车载软件测试?ThreadingTest(TT)穿针引线(一) 校招专属 | ThoughtWorks西安办公室开放参观日 真实而震撼:同班同学20年后,身价15亿与月薪5000元的区别 虽然这个国产机器狗很像泰迪,但这个中国学生的研究精神令人敬佩! 【教师节活动】老师,我爱你!