(屏幕截图显示了2016年春季纽约、中国和印度三地的Uber打车应用)
虽然我们希望Uber的用户界面简单,但我们在背后设计了复杂的支撑系统,处理棘手的交互,支持海量的流量。我们将原来的整体式架构分成了许多部分,以便伴随业务成长而扩展。由于成百上千的微服务相互依赖,绘制一张图来表明目前Uber是如何工作的显得异常复杂,这一切在迅速变化。本文介绍了我们从2016年春天开始使用的架构。
Uber工程部门的挑战:没有免费用户,高速增长
与一些最成功的软件公司一样,我们同样遇到了全球规模问题,但是1)我们成立才短短六年,所以我们还没有解决这些问题;2)我们的业务基于现实世界,而且具有实时性。
(2015年4月,Uber运营有业务的300个城市分布在地图上)
不像免费增值服务,Uber只有交易型用户:乘客、司机,还有现在的食客和快递员。人们依赖我们的技术来赚钱,前往想去的地方,所以没有时间暂停下来。我们格外注重可用性和可扩展性。
就在我们扩大覆盖范围的同时,我们的服务必须能够扩展。我们架构的灵活性鼓励竞争,那样最好的想法才能胜出。这些想法未必很独特。如果有一种强大的工具,我们使用它,直到它无力满足我们的需求。当我们提出更高的要求后,我们构建内部解决方案。在过去的一年,Uber工程部门以巨大的适应性、创造力和方法准则,应对业务迅猛发展的势头。纵观2016年,我们拥有还要宏大的计划。等你读到本文,想必发生了很大的变化,不过本文概述了我们现在使用的系统。通过本文介绍,我们希望展示在使用工具和技术方面的理念。
Uber的技术架构
想象一棵树,而不是种种限制的塔式体系。看一下整个Uber使用的技术,你会看到共同的架构(好比树干),每个团队或工程办公室有不同的侧重点(好比树枝)。它完全是用同一种东西做成的,但工具和服务在各个领域大放异彩。
我们先从底层开始说起。
底层:平台
这第一篇文章关注Uber平台,这意味着支持更广泛的Uber工程部门的所有系统。平台团队创建和维护的系统让其他工程师能够构建用户使用的软件、功能和应用程序。
基础设施和存储
我们的业务在混合云模式上运行,结合使用多家云服务提供商和多个活动数据中心。如果一个数据中心出现故障,行程(以及与行程有关的所有服务)就会自动切换到另一个数据中心。我们为城市指定了地理位置最近的数据中心,但每个城市都由另一个地方的不同数据中心提供后备机制。这意味着我们的所有数据中心始终在处理行程;我们并没有 “备份”数据中心这个概念。为了提供这套基础设施,我们结合使用了内部工具和Terraform(https://www.terraform.io)。
我们的存储需求因业务发展而发生了变化。单单一个Postgres实例让我们度过了发展初期,不过随着业务迅猛发展,我们需要增加可用的磁盘存储空间,并缩短系统响应时间。
2014年夏末,Mezzanine项目(https://eng.uber.com/mezzanine-migration/)重构了系统,以匹配这个高级架构。
我们目前使用Schemaless(在MySQL上运行的内部构建系统)、Riak和Cassandra。随着时间的推移,Schemaless实例取代了单独的MySQL实例和Postgres实例,Cassandra取代了Riak,以提升速度和性能。Schemaless用于数据的长期存储;Riak和Cassandra满足高可用性低延迟方面的需求。至于复杂数据的分布式存储和分析,我们使用了Hadoop仓库。除了这些数据库外,我们在西雅图的工程师专注于构建一个新的实时数据平台。
我们使用Redis用于缓存和队列。Twemproxy通过其一致的散列算法,提供了缓存层的可扩展性,又不牺牲缓存命中率。Celery worker进程使用那些Redis实例来处理异步工作流操作。