Uber是一家总部位于旧金山的风险投资的创业公司和交通网络公司,以移动应用程序链接乘客和司机,提供租车及实时共乘的服务。在最近短短四年间,Uber的业务量已经惊人地增长了38倍,在这背后起到支撑作用的是公司强大的系统架构。
其中一项非常杰出的工作是他们在处理系统故障时,包括当出现数据中心故障的时候,通过将司机的手机作为一个外部分布式存储系统,Uber采用了一种非常出色的系统恢复方式。那么,Uber采用这种方式的理由是什么?他们是如何在扩展时确保系统的可靠性的?最近,Uber的首席系统架构师Matt Ranney 在他的报告“ 扩展Uber的实时市场平台 ”中提到他们一个很吸引人的工作:
Uber在处理数据中心失效转移时,使用司机的手机作为一个外部的分布式存储系统,用于数据恢复。这个系统具体是如何工作的呢?最近Uber的工程师 Nikunj Aggarwal 和 Joshua Corbin 在 Scale conference 上做了一个题为“ How Uber Uses your Phone as a Backup Datacenter ”的报告,对系统的工作方式做了一个生动的介绍。本文是一篇翻译稿,原文题目为“ Uber Goes Unconventional: Using Driver Phones As A Backup Datacenter ”,已获得作者授权。
在文章“ How Uber Scales Their Real-Time Market Platform ”中提到一个亮点:Uber在处理数据中心失效转移时,使用司机的手机作为一个外部的分布式存储系统,以用于数据恢复。
这个系统具体是如何工作的呢?最近Uber的工程师Nikunj Aggarwal和Joshua Corbin在Scale 会议上做了一个题为“How Uber Uses your Phone as a Backup Datacenter”的报告,对系统的工作方式做了一个生动的介绍。
数据库通过使用一个传统的后端复制方案,实现数据中心之间的状态同步。但Uber并没有这样做,他们在司机的手机上存储足够的状态,这样,当数据中心发生失效转移时,用户的行程数据也不会在失效转移中丢失。
为什么选择这种方式呢?事实上,传统的方法更加简单。我认为这是为了确保客户始终有一个良好的客户体验,对于一个活动的行程来说,行程信息的丢失会给用户带来一个槽糕的客户体验。
通过围绕着手机建立自己的同步策略,虽然这有些复杂并需要大量的工作,但Uber还是能够保存好行程数据,并且在数据中心出现故障时能为客户提供一个满意的客户体验。而让客户满意应该是最重要的,尤其是在那些用户转换工具的成本接近零的市场中。
因此,我们的目标是,即使在数据中心失效转移的过程中也不丢失任何行程信息。使用传统的数据库复制策略,这几乎是不可能的,其原因类似于 网络管理系统 所一直采用的工作方式。 下面我对此进行详细解释。
在一个网络中,设备是分组错误、警报、发送和接收分组等状态信息的权威来源。网络管理系统负责配置警报阈值等配置信息以及管理客户信息。复杂之处在于设备和网络管理系统并不总是处于连接状态,因此它们工作相互独立,并导致它们之间不同步。这意味着在启动、失效转移以及通信重连时,为了确保正确性和一致性,不得不通过一个复杂的调节将所有这些信息在两个方向上进行合并。
Uber也有同样的问题,只是这些设备是智能手机,而手机包含的权威状态是行程信息。因此在启动、失效转移和通信重连时,行程信息必须保留,因为手机是行程信息的权威来源。
即使是在连接断开的时候,手机也准确地记录了所有的行程数据。所以,你不需要进行从数据中心到手机的行程数据同步,因为这将消除掉手机上正确的数据。正确的信息肯定来自于手机。
Uber也从网络管理系统中学习到了一项技术。他们定期对手机进行查询,以测试数据中心中信息的完整性。
让我们来看看他们是如何做到这一点的。
状态变化的转换包括:行程的要求、提供给司机、行程的接受、挑选乘客,结束行程。只要行程在持续,整个行程事务就在持续。
行程开始的时候,行程数据就开始在备份数据中心中被创建。Uber似乎在每个城市都设计了一个数据中心。
数据中心发生故障的典型解决方案:从当前数据中心到备份数据中心复制数据。工作的效果主要取决于你使用的数据库。缺点:
使用两个数据中心使问题变得复杂。
数据中心之间的复制滞后。
数据中心之间一直需要一个高的带宽,特别是当你的数据库并没有很好地支持数据中心复制,或者你没有为优化增量调整业务模型的时候。
(一个好处在这里并没有被讲到,可能对Uber来说不重要,但对一些小的公司来说影响很大,即使用司机手机的计划所需要补贴的带宽成本没有数据中心之间传输所需要的带宽成本高。)
创意应用感知解决方案:由于与司机的手机之间一直存在通信,因此只需要将数据保存在司机的手机里。优点:
可以失效转移到任何数据中心。
避免出现手机失效转移到错误的数据中心的问题,出现这个问题将导致所有行程数据丢失。
使用司机的手机进行数据中心的备份需要一个复制协议。
与数据中心通信时,所有的状态转换发生。例如,有一个开始行程或开始驾驶请求,这些状态转换过程是与手机交换状态数据并获得手机存储数据的一个绝佳机会。
在数据中心失效转移过程中,当手机ping新的数据中心时,行程数据被要求从手机上切换。停机时间非常小。(没有处理任何关于数据中心如何映射的信息)。
挑战:
并非所有保存的行程信息都是司机可访问的。例如,一次行程有大量关于乘客的信息,它们都不应该被暴露。
数据必须防篡改。因此,所有手机上的数据都是加密的。
需要让复制协议尽量简单,以方便推理和调试。
尽量减少额外的带宽。使用基于手机的方法,通过调试哪些数据被串行以及哪些增量被保留,可以尽量减少移动网络上的流量。
复制协议
一个简单的key-value存储模型,使用了get、set、delete和list等键值操作。
一个key只能get一次,以防止出现意外覆盖和消息乱序的问题。
使用一次get规则,版本控制不得不进入key空间。更新一个存储的行程是这样的:set(“trip1, version2”, “yyu”);delete(“trip1, version1”)。它的优点是,如果在set和delete操作之间发生了故障,将有两个值被存储,不会出现什么都没有存储的情况。
失效转移仅仅是手机和新的数据中心之间合并键值的问题,具体通过将存储的键值当成任意已知的正在进行的司机的行程,为任何丢失的数据发送一个或多个get操作。
确保系统是非阻塞的,同时提供最终的一致性。即使在系统已关闭的情况下,系统中的任何后端应用程序应该能够更新。应用程序更新应该付出的唯一代价是,它可能需要一段时间将数据存储在手机上。
数据中心之间的数据可以移动,因此没有必要担心数据在什么地方。需要有一种方法来调和司机与服务器之间的数据。
当失效转移到数据中心的时候,数据中心有一个活动的司机和行程的视图,数据中心中没有任何服务感知到故障的发生。
转移到原始数据中心的过程中,司机和行程数据有些过期,这会导致一个糟糕的客户体验。
使其可测试。数据中心故障很少发生,所以它通常很难测试。他们希望能够不断地测试系统是否成功,因此当故障发生时他们能够有信心进行失效转移。
一个司机在发生更新或状态变化时,例如,接上了一个乘客。当请求调度服务的时候,更新就开始了。
调度服务为行程更新行程模式。更新被发送到复制服务。
复制服务将请求进行排队,并返回success。
调度服务更新自己的数据存储,并返回success到移动客户端。其他的数据也可能被返回,例如,如果它是一个Uber Pool行程,另一个乘客也可以被接上。
在后台,复制服务对数据进行加密并将它发送到消息传递服务。消息传递服务为所有司机提供一个双向通道。这个通道与司机用来进行服务通信的原始请求通道是分开的。这确保了正常的业务操作不受备份过程的影响。
消息传递服务将备份发送到手机。
这种设计的好处:
避免了应用程序出现复制延迟和失败。复制服务立即返回。并且应用程序只需要做一个简单的调用(同一个数据中心内),就可以实现数据的复制。
消息服务支持手机的任意查询,而不会影响正常的业务操作。手机存储可以被视为一个基本的key-value存储。
第一种方法是手动运行失效转移的脚本,从数据库中清理旧的状态。这种方法存在操作成本的硬伤,必须有人来进行操作。并且由于同一时间在多个城市同时进行失效转移是可能的,这时脚本会变得过于复杂。
回想一下,key-value数据库中的key包括一个行程ID和一个版本号。版本号过去常常是一个递增的数。这里被更改为一个修改的矢量时钟。在手机上使用矢量时钟数据可以比作为服务器上的数据。任何因果侵犯可以被检测和解决。这解决了正在进行的行程的问题调和。
传统上,完成的行程会从手机中删除,因此复制数据将不会无限增长。问题在于,当因为故障返回到原始数据中心的时候,数据中心将有过时的数据,这可能会引起调度异常。解决方法是在行程完成时使用一个特殊的 墓碑 键值(tombstone key)。该版本有一个标志,用来标记此行程已经完成。当复制服务看到标志时,它可以告诉调度服务,次行程已经完成。
存储行程数据的开销很大,因为它是一个巨大的加密的blob。已完成的行程需要少得多的存储空间。一个星期内完成的所有行程的存储空间仅仅相当于一个活动的行程的存储空间。
失效转移系统不断进行测试,以确定失效转移成功后,它还在正常地工作。
第一种方法是为单个城市手动地进行失效转移。然后查看恢复的成功率,并通过查看日志进行问题调试。
高的操作成本。每周手动地执行此过程是行不通的。
糟糕的客户体验。对于没有正确恢复的几次行程,其收费必须重新调整。
低覆盖率。同一时间只有少数几个城市可以测试,因为有些问题仅仅在特定的几个城市中存在。
不清楚一个备份数据中心能否处理负载。有一个主用和一个备用的数据中心。即使他们的配置相同,你又怎么知道备份数据中心能处理这种“惊群”问题,即在失效转移中会出现大量的请求。
为了解决这些问题,他们查看了系统中他们想测试的关键概念。
确保调度服务中的所有变化实际上都存储在手机上。例如,一名司机在接上一名乘客之后可能会失去连接,因此复制数据可能不会立即发送到手机上。需要确保数据最终在它的手机上。
确保存储的数据可用于复制。有很多问题可能影响数据的复制,例如,加密/解密问题。
确保备份数据中心能处理负载。
需要一个监视系统监视系统的健康状态。
每隔一小时服务从调度服务中获取一份所有活动的司机和行程的列表。对于所有司机来说,消息传递服务可用于获取复制数据。
然后将数据进行比较,以查看数据是否符合预期。这就产生了很多不错的健康指标,如失败比例。
按区域和应用程序版本分解指标对查明问题有很大的帮助。
需要一个阴影重建(shadow restoration)用于测试备份数据中心。
由监测服务收集的数据被发送到备份数据中心,用于阴影重建。
通过使用调度服务将一个来自于主用数据中心的快照与备份数据中心中活动的司机和行程的数量进行比较,成功率能够被计算出来。
关于备份数据中心处理负载好坏的指标也被计算出来。
在备份数据中心的任何配置问题可以通过这种方法被捕获到。
《他山之石》是InfoQ中文站新推出的一个专栏,精选来自国内外技术社区和个人博客上的技术文章,让更多的读者朋友受益,本栏目转载的内容都经过原作者授权。文章推荐可以发送邮件到editors@cn.infoq.com。
感谢郭蕾对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入InfoQ读者交流群 )。