没有免费用户却飞速发展,Uber技术栈全解析!

转载至:微信公众号 infoq http://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&mid=2650994162&idx=1&sn=9fe3c9497139d0189a6300a53ecd6393&chksm=bdbf0da18ac884b71f337d1091295eb35be3eb7cb73a93d620c0af351edbfcd0f63a522cfffb&scene=0#rd

版权信息

英文原文:

https://eng.uber.com/tech-stack-part-one/

https://eng.uber.com/tech-stack-part-two/

https://eng.uber.com/tech-stack-part-two/

作者:LUCIE LOZINSKI

译者:足下

InfoQ已获得原网站翻译授权

Uber的使命是要让交通可靠得像人人随处可用的自来水一样。要实现这个使命,我们生成并处理着非常复杂的数据,再把它们用平台的方式梳理得整整齐齐的,让司机可以获得订单,让乘客自由出行。

截图为2016年春季纽约、中国和印度的Uber乘客App界面

我们希望能把Uber的App UI设计得简单易用,而后台支撑系统可以很复杂,来处理复杂的交互和大量的请求。为了支撑业务量的快速增长,我们把最初的单体架构拆散成了许多小块以方便扩展。在有了数百个相互依赖的微服务之后,现在要想画出一张Uber系统的工作流程图已经相当困难,而且还演进得非常快。在本篇文章中我们也只能简介2016年春天的架构。

没有免费用户和飞速发展 Uber技术挑战

与许多非常成功的软件公司一样,我们也面临着相同的全球发展问题,但不同的是:1)我们才刚成立六年,所以我们还没能完全解决;2)我们的业务是实时地和真实世界打交道的。

2015年4月,Uber运营的300个城市在地图上的分布情况

与“免费+增值服务”的模式不同,Uber只有付费用户:过去是乘客和司机,现在又增加了外卖和导游。人们要靠我们提供的技术来赚钱和去他们要去的地方,所以我们没有可以停歇的时间。我们非常看重系统可用性和可扩展性。

随着我们的业务扩展到越来越多的城市,我们的服务也伴随着一同扩展。我们技术栈的易伸缩性鼓励不同技术之间的比较和竞争,这样最好的方案才能最终胜出,而且胜出的也不一定必须是唯一的方案。如果已经有非常好用的工具了,我们就会拿来用,直到超出了它的承受范围。当我们需要更强大的东西时,我们就自己开发。

在过去的几年里Uber技术团队已经表现出了非常棒的适应性、创造力和纪律性。在2016年,我们有更大的计划。当你读到这篇文章时,肯定有很多东西已经变了,但这毕竟也是我们曾经的技术栈的一个快照。通过我们的描述,希望你能理解我们关于使用工具和技术的想法。

Uber的技术栈

我们先想象一棵树,而不是一堆的限制条件。统观Uber使用的技术栈,可以看到一些通用技术(就像一棵树干),每个团队使用得各有侧重(树枝)。所有东西都出自相同的本源,但工具和服务在各个不同方面发挥出完全不同的作用。

感谢Conor Myhrvold, Botswana的图片:Chapman’s Baobab。  

Baobab(猴面包树)以其生命力强、长寿、粗厚的树干和树枝而闻名。喀拉哈里沙漠中的这棵猴面包树也是非洲最古老的树之一

我们从底层开始讲起。

底层:平台

第一部分关注Uber技术平台,即所有支撑着更广泛的其它Uber技术团队的东西。平台团队打造和维护这些技术,让其他团队可以更好地构建程序和功能,以及你使用的App。

基础设施和存储

我们的业务运行在混合云模型上,使用不同的云服务提供商和多处同时提供服务的数据中心。如果一个数据中心出了故障,我们的核心Trip数据会自动完成故障转移,切换到另一个数据中心继续提供服务。我们会尽量把每个城市的数据放到离它地理上最近的数据中心,但每个城市的数据都会在另一个不同的数据中心有备份。

这意味着我们所有的数据中心都在同时提供着在线的Trip数据服务,我们并没有传统意义上的“备份”数据中心。要在这样的基础设施上部署程序,我们同时使用了各种内部工具和Terraform。

我们对存储的需求随着业务量的增长而变化。公司刚创立时我们只有一个Postgres数据库实例,但当我们快速发展之后,我们需要增大可用的磁盘存储量和减小系统响应时间。

2014年夏天,Mezzanine项目把系统重构成了上图所示的高级架构

我们现在使用Schemaless(基于MySQL自行研发),Riak和Cassandra。Schemaless用于数据持久化存储,Riak和Cassandra满足高可用性和低延迟的需求。慢慢地,Schemaless实例会替换掉单个的MySQL和Postgres实例,而Cassandra会因为速度和性能而替换掉Riak。我们使用Hadoop来存储和分析复杂数据。在这些数据库之上,我们西雅图的工程师们还在开发着一个新的实时数据平台。

我们用Redis作缓存和队列。Twemproxy实现了一致性哈希算法,提供了缓存层的扩展性而又没有牺牲缓存命中率。Celery的团队就是用这些Redis实例来处理异步工作流操作的。

日志

我们的服务会与彼此及手机进行交互,这些交互信息对于内部技术调试以及业务上的动态调价等功能都非常有价值。为了支持日志功能,我们使用了好多套Kafka集群,在数据被Kafka淘汰之前会最终归档到Hadoop和Web服务的文件存储。数据还会被各种服务实时消费掉,并在ELK上为搜索和可视化功能创建索引(ELK是Elasticsearch,Logstash和Kibana的缩写)。

App部署

借助于Aurora的长时间运行服务和定时任务,我们用Mesos的Docker容器来做微服务的一致性可伸缩配置。我们有个基础设施团队——应用平台组——实现了一个模板库,可以把服务构建成可部署的Docker镜像。

路由和服务发现

我们的面向服务架构(SOA)让服务发现和路由功能变得对Uber的成功至关重要。服务必须能在我们的复杂网络中与彼此通信。我们用了HAProxy和Hyperbahn来一起解决这个问题。Hyperbahn是Uber开发的一系列开源软件之一:Ringpop、TChannel和Hyperbahn都是要为服务网络增加自动化、智能性和提高性能。

传统的服务会用本地的HAProxy实例来把JSON包通过HTTP请求传送到其它服务,从前端的Web服务器和Nginx到后端服务器。这样清晰的数据传送方式就让查错变得简单,这在去年我们做的演进到新系统的几次升级中起到了关键作用。

然而,在可调试功能之上,我们更看重长期可靠性。在速度和可靠性方面,我们还可以用HTTP之外的SPDY、HTTP/2和TChannel等协议和Thrift或Protobuf等接口定义语言来方便我们演进系统。Ringpop是个一致性哈希层,提供了应用层的协作和自愈功能。Hyperbahn让服务可以简单可靠的进行相互通信,即使服务是用Mesos动态调度的。

我们现在已经迁移到了一种发布-订阅的模式(将更新发布给订阅者)上来,而不是传统的为查看是否有状态改变而要不断轮询方法。HTTP/2和SPDY都更容易实现这种推的模式。如果改用推的模式,Uber App很多现有的基于轮询的模式都会有性能上的飞跃。

开发与部署

很多内部操作都依赖Phabricator,从代码审查到生成文档,到流程自动化。我们用OpenGrok来查找代码。对于Uber开源的项目,我们直接用GitHub来跟进问题和做代码审查。

Uber技术团队一直致力于让开发活动尽可能地模拟实际生产,所以我们大多数时候都在云服务提供商的虚拟机或者开发者的笔记本电脑上进行开发。我们构建了自己的内部部署系统来管理构建活动。Jenkins用作持续集成。

我们把Packer、Vagrant、Boto和Unison组合在一起来打造在虚拟机上进行构建、管理和开发的工具。我们用Clusto来做开发的清单管理,用Puppet管理系统配置。

我们也一直努力打造和维持稳定的沟通渠道,不止是服务之间,更是在工程师之间。就信息发现功能来说,我们用uBlame(git-blame的改进版)来跟踪哪个团队管理着哪种特定服务,用Whober来查询名字、相片、联系方式、组织架构等。

我们内部的文档服务器会用Sphinx自动从存储服务器的信息中构建文档。企业级告警服务会提醒值班的工程师保持系统正常运行。许多开发者都在笔记本电脑上运行OSX,大多数生产环境服务器上运行的Linux都是Debian Jessie。

开发语言

在底层Uber工程师主要用的是Python、Node.js、Go和Java。最早只用了两种语言:Marketplace团队用的是Node.js,其他人用的是Python。到现在Uber运行的大多数服务仍是用这些最初的语言写的。

用Go和Java主要是出于高性能的考虑。Java利用了开源生态系统,并与Hadoop和别的分析工具等结合得非常好。Go可以带给我们高效、简单和高运行时效率。

在把最早期的代码拆成微服务时我们就开始慢慢替换掉原有的Python代码了。异步编程模式让我们有了更好的吞吐量。我们把Tornado和Python一起使用,但Go对并发的原生支持对许多非常重视高性能的新服务来说也非常合适。

必要时我们会用C和C++来写些工具(比如在系统级的高效和快速的代码)。我们会使用这些语言写的工具,比如HAProxy就是其中之一,但大多数情况下我们不会用它们。

当然,比较高些层级的代码就不是用Java、Go、Python和Node.js写的了。

测试

为保证服务可以处理我们生产环境的需求,我们开发了两个内部工具:Hailstorm和uDestroy。Hailstorm用来做集成测试,在非繁忙时间模拟峰值压力。uDestroy会故意搞坏一些东西,来测试我们对非预期故障的处理。

我们的员工都会先使用测试版的App来不断测试新功能,然后才会交到用户手中。我们有个App反馈器,用于记录发布给用户之间遇到的所有问题。每当我们为Uber App生成新版本时,这个功能都会让我们去Phabricator上加一个修复问题的任务。

可靠性

每个写后台服务的工程师都要为他的代码负责。不管谁写的代码搞坏了生产环境的东西,值班人员就会给他打电话。我们用Nagios来做监控告警,绑定到一个告警系统上。

现场可靠性工程师的任务是保证最高的可用性和每天10亿次叫车服务,他们会关注于创造和满足服务运行所需要的条件。

这里有个2016年二月的技术讨论视频,谈到了Uber的现场可靠性保证。 

https://youtu.be/qJnS-EfIIIE

可观测性

可观测性是要保证Uber作为一个整体,或者它的各个不同部分都是正常工作的。这个功能主要由我们纽约团队开发,有许多个系统来作为Uber遍布全世界的技术系统的眼睛、耳朵和免疫系统等。

遥感勘测

我们用Go语言开发了M3来收集和存储Uber技术系统(每台服务器、每个服务和每一行代码)的各项指标。

收集到数据之后,我们会查看趋势。我们修改了Grafana来更容易的通过仪表盘和图形来展示和上下文紧密相关的信息。每个看着仪表盘的工程师都有自己想观察的东西,有的是某个特别城市或区域的数据,有的是若干个实验,或者是某个新产品的数据。我们给Grafana加上了数据分片的功能。

异常检测

我们自行研发了异常检测工具Argos,用于把输入指标和基于历史数据生成的预测模型做对比,判断当前数据是否超出了正常的界限。

我们把由异常值检测算法每小时算出的动态阈值(红线和黄线)与输入数据做对比。我们还画上了实际的每分钟产生的真实数据(蓝线),这部分当然不能提前画上。这些阈值都会紧密的跟进着实际指标的模式。

根据指标做出的反应

Uber的工程师们在看到了实时信息和阈值(不管是静态的还是Argos动态生成的)之后,就可以用工具μMonitor来采取行动了。如果某个数据流越界了,也就是说Trip数据在某个城市下降到某个阈值之下了,这条信息就会被发送给常见行为网关。这是我们自动响应系统的功能。

除了在发生故障时给工程师们打电话之外,它也会做些事情来缩短故障持续时间。如果某次部署发生问题,回滚操作也是自动的。

我们有许多观测工具都是限于Uber内部使用的,因为它们和我们的基础设施太相关了。不过我们也希望能把通用部分抽象并开源出来。

创造性地使用数据

Storm和Spark会把数据流处理成有用的业务指标。我们的数据可视化团队也有可重用的架构和应用来处理可视化数据。

表格和置信区间的可视化增强了我们的A/B测试平台Morpheus的功能

地图和实验团队都依靠数据可视化技术来把数据变成清晰、可感知的信息。城市运营团队可以直接实时地看到他们的司机以车的形式在地图上移动,而不是枯燥地做些SQL查询来了解情况。

我们用JavaScript(ES5和ES6)和React来构建了数据产品,作为我们的核心工具。我们也在我们的可视化模块中使用了各种标准图像技术:SVG、Canvas 2D和WebGL等。我们已经开源了许多库,比如我们就是依赖react-map-gl来做地图可视化的:

可视化证明了react-map-gl的功能,这是一个由Uber数据可视化团队开发的MapboxGL-js的封装器

我们也开发了可视化框架,让诸如R、Shiny和Python等也可以访问我们的图表组件。我们希望有在浏览器中可以运行得非常流畅的高密度数据可视化技术。要达到这样的目标,我们开发了开源的基于WebGL的可视化工具。

一张Uber热度地图显示了正在载客的车辆的密度。然后我们还可以把顶层的百分比数据移除掉来看底层数据。

地图

Uber地图团队非常重视数据、算法和与地图数据、展示、路由、收集和推荐地址和位置等功能相关的工具集。地图服务主要是基于Java技术开发的。

这一块量最大的服务是Gurafu,它通过许多精细的路由选项来提供了许多处理道路地图数据的功能,提高了效率和准确性。Gurafu前端是µETA,它在原始ETA之上加了个业务逻辑层。Gurafu和µETA都是基于DropWizard框架开发的Web服务。

我们的业务和客户都非常依赖高度精确的ETA数据,所以地图服务工程师会花费很多时间来保证这些系统运行得正确。我们会作ETA错误分析,来找出并修复出错的源头。

在精确性之外,这个问题的级别也是很重要的:每一秒,整个公司的系统都会基于ETA信息做出非常大量的决策。由于这些请求的延迟不能超过5毫秒,那算法效率就也成了个大问题。我们要关注各种细节问题,比如分配内存的方法、并行计算和对服务器磁盘或数据中心网络等低速资源的访问。

地图服务也在支撑着所有乘客和司机App中搜索框底层的后台服务技术,包括自动补齐搜索引擎、预测引擎、反向地理编码服务等。自动补齐搜索引擎提供了对地点和地址的本地搜索功能。

预测引擎使用机器学习算法来基于用户历史和其他信息预测乘客的目的地。预测要能占到用户输入目的地的大概50%。反向地理编码服务根据GPS信息来判断乘客的位置,再辅助以各种其它信息,根据整体行程对乘客的上车地点做出推荐。

中间层:Marketplace

Marketplace是Uber引擎的最前端,负责把真实世界的实时请求和位置数据等送入Uber系统。数据持久存储层、匹配系统和实时交易处理等都在这里。这里也有很多关于UberRUSH和UberEATS等产品的逻辑。在Uber系统中,Marketplace的可用性要求是最高的。

要理解Marketplace,非常重要的一点是要记住Uber技术栈中的各个部分是会相互作用的。最底层的基础设施支撑着它上面的所有东西,但上层的功能也会调用下层。就Marketplace而言它自身是独立的,但它上层和下层的东西都会调用到它。

与Uber其他团队一样,Marketplace自身也有着一套技术栈。在它内部,工程师们只为Marketplace自己构建基础设施和数据方案,包括数据组、集成组、前端工程师、后端工程师、还有用我们的四种编程语言(Python、Node.js、Go、Java)写成的各种服务。这种分层架构保证了Uber系统的高可用和对故障免疫。

Uber的核心Trip处理引擎最早是用Node.js写的,主要看中的是它的异步原语和简单的单线程处理。事实上我们也是敢把Node.js用到生产环境的仅有的两个公司之一。Node.js让我们可以管理大量的并发连接。但现在我们大多数服务都是用Go写的了,而且越来越多。我们看中的是Go的并发、效率和类型安全操作。

边界

我们手机App的前端API包含着超过600个无状态节点,它们把许多服务组合起来了,把手机用户输入的请求路由到别的API或服务上。所有东西全都是用Node.js写的,只有边界部分除外,NGINX前端在这里会终止SSL并做一些鉴权。NGINX前端也会借助于HAProxy负载均衡器做前端API的代理。

Marketplace的这一部分集成了许多内部基础设施原语。这个团队的工程师用开源软件logtron来向磁盘和Kafka写日志。我们用uber-statsd-client模块(stasd的Node.js客户端)生成统计信息,再与我们自己研发的M3交互(前文讲过)。

高可用、自愈和持久化

为了支持最大程度的可用性需求,Marketplace技术栈必须实时地接收和处理请求。即使这个模块发生非常短暂的停服,也会对我们的用户和业务产生非常严重的影响。Marketplace的很多技术都是团队里的工程师研发出来先给自己用的。

Ringpop是一套构建协作式分布式系统的库,它在Marketplace被推广到Uber其他团队乃至更外部之前解决了一些问题。对于开发者来说它在应用程序一级提供了与DynamoDB或Riak等分布式数据库类似的高可用、分区容忍等特性。

实时处理乘客和司机的状态数据并把它们作匹配的逻辑是用Node.js和Go语言写的。这些团队用Ringpop和Sevnup来实现功能,并在哈希环中有节点发生故障、或者有别的节点成为主键空间的主节点时做对象角色切换。

速度与吞吐量

为全公司的团队构建跨功能工具的工程师们是在Uber里用Cassandra和Go用得最多的,最主要的原因就是速度。Cassandra做横向扩展非常容易,而Go语言的编译超级快。

吞吐量对Marketplace团队来说也是非常重要的。它们必须能处理最大量的业务请求,因为所有请求都会经过Marketplace。即使是在业务量最高峰的时候,Marketplace也必须能够抗住所有压力,否则请求都压根到不了Uber的其它模块。

优化与均衡

Marketplace用动态调价、智能匹配、健康状态检查等手段来控制优化和均衡。这一块主要用的是Python写的Flask和uWSGI等,但我们为了追求更高的性能而在用Go替换Python。网络调用和I/O产生的阻塞都很诡异地拖慢了我们的服务,要能处理相同量级的请求就只好部署更多的服务。在访问后端的MySQL数据库时Python很好用,但我们已经在用Riak和Cassandra集群替换MySQL的主从架构。

看到并使用数据

Marketplace内部有个小团队是负责把Marketplace的数据可视化的,方便团队理解和观察全世界的状态。我们用JavaScript实现前端应用程序。后端用到的库和框架包括React+Flux、D3和Mapbox等。就后端而言,使用的Node.js服务器也和Uber的Web工程师们用的是同一台。

Marketplace内部的数据工程师们综合使用了各种数据库、自行研发的解决方案和外部开源技术,来实现数据处理、流处理、查询、机器学习和图处理等功能。

数据流用的是Kafka和Uber的生产数据库。Hive、MapReduce、HDFS、Elasticsearch和文件存储Web服务等都被用来做特定用途的数据存储,以及处理相应的操作请求。

与大家用惯了的LIDAR不同,我们开发了它的一个变种。我们现在只在内部分享交互式数据分析记录的全量数据,运行的是JupyterHub,支撑多用户的Jupyter(IPython)Notebook,并把它与Apache Spark和我们的数据平台做了集成。Marketplace的数据团队是与Uber的数据技术团队不同的,但用的技术大部分还是重叠的。

在Marketplace之上,网页和手机端用的东西是完全不同的了。

上层:网页与手机

我们的网页端和手机端的工程师共用着许多相同的底层模块,但也还是有很多是上层独有的。这些部门的工程师们构建出来的就是你常用的App了,也会构建出给所有网页和手机工程师们共用的库和框架。这个团队最看重的是用户体验和易用性。

网页

网页团队和产品团队一起协作,构建和推广模块化的、分别部署的网页App。所有的App都有相同的用户操作接口和用户体验。

语言

网页的核心功能都是用Node.js写的。Node.js的社区非常大非常活跃,聚集了许多网页开发者。Node.js让我们可以在客户端和服务器之间共享JavaScript代码,以打造通用、同构的网页应用程序。我们客户端的程序需要Nodejs风格的模块,因此我们用Browserify来打包。

网页服务器

我们的基础网页服务器Bedrock构建在应用非常广泛的网页框架Express.js基础之上。Express.js自带了许多中间件,可以提供安全、国际化和其他Uber特有的模块,来集成基础设施。

我们自建的服务通信层Atreyu会处理后端服务之间的通信,并且与Bedrock集成。Atreyu让我们可以很容易地向SOA服务API发送请求,和Falcor或Relay非常相似。

如上图所示,司机的主页就是在坚实的Bedrock基础之上构建出来的许多Uber页面之一。

渲染、状态处理和构建

我们用React.js和标准Flux来做应用程序的渲染和状态处理,有些团队已经开始尝试用Redux来做未来的状态容器。我们也在把名为Superfine的现有OOCSS/BEM风格的CSS工具集演进成一套由CSS封装的React.js UI模块,是用样式对象构建的,和Radium类似。

我们的构建系统叫Core Tasks,是一套基于Gulp.js构建的脚本集合,用于编译前端程序并且管理版本,再推送到网页服务的文件存储上,让我们可以利用CDN服务。

最后,我们用内部的NPM注册表来管理超大量的公共注册包和只向内部广播的包。每个工程师都可以向它发布,这就让我们可以轻松地在团队间共享模块和React.js组件。

手机

Uber曾经有过非常专门的手机部门。现在公司的部门都是跨职能的,我们叫应用程序团队。每个跨职能团队的成员都有不同背景,从后端到设计到数据科学都有。

开发语言

Uber的iOS程序员是用Objective C和Swift开发的,Android程序员用Java。也有一些开发React组件。Swift有很多静态分析和编译时安全检查,所以要把代码写的有错也不容易。我们比较喜欢面向协议编程。在手机方面我们的演进方向是基于模块化的库的系统。

在不同的顶层技术方面,我们会用第三方的库,或者为了自己的特定需求而专门构建。许多现有可用的开源库都是通用的,这样会造成App安装包非常庞大。对手机程序来说,每一K字节都很重要。

Android

Android方面用Gradle作构建系统。我们用了OkHttp、Retrofit和Gson。Dagger是依赖注入框架。

我们用开源库来使UI代码简洁易用。Butter Knife让我们可以通过处理注解把视图和回调绑定到字段和方法上。Picasso提供了图像加载功能。

Espresso扩展让我们可以在IDE(我们用Android Studio)中用熟悉的Android SDK写出许多自动化代码。为了适应架构,我们用RxJava来简化对异步和基于事件的编程。日志功能我们用Timber。

iOS

所有iOS代码都保存在我们用Buck构建的一台集中存储上。Masonry和有自动布局功能的SnapKit用于做组件摆放和大小适配。崩溃检测用的是KSCrash,但上报崩溃报告用的是我们自己的报告框架。我们用OCMock模仿和构造测试类来测试Objective-C代码。我们也用协议生成了虚拟类来测试Swift。

存储

我们用LevelDB做存储。后台是标准的Schemaless和MySQL,我们正在逐步向全部使用Schemaless迁移。

开发

我们主要有四个App:Android乘客端、Android司机端、iOS乘客端和iOS司机端。这意味着每个星期我们每个平台上都要有上百位工程师向代码库中提交代码,并且发布。万一发生了什么问题我们也没办法快速恢复。所以,我们要构建一些系统来让这种开发模式更可靠。

手机程序开发是百分之百确定地,大家都在主分支上进行开发,然后大家各自提交。我们也还在用Git做软件版本管理,但写App代码的工程师都是直接向主分支进行提交的。这么多人在主分支上提交代码风险是非常大的。

所以我们用了自己开发的服务和程序配置平台,非常易用,也非常容易基于它进行构建,这样相关团队就可以限制自己的代码改动对Uber其他服务和业务的影响。平台使用功能标记位来在服务器端打开或关闭代码功能。我们会做灰度发布,并且仔细跟进发布的情况。

工程师们不必考虑构建窗口的问题,它们都是按照功能标记增量进行的。我们没有手工的QA流程,相反我们对自动化和监控做了很多投入。我们的持续集成让我们可以快速扩张,而监控让我们的快速响应系统可以捕获并改正任何有问题的代码。

栈外之栈

难以描述好Uber的技术栈,一部分原因是这里没有非常确定性的规则。当大家想到技术栈这个词时,脑海中往往会浮现出一根图腾柱,最下面是基础设施,最上面是用户可用的各具特色的功能。有非常清晰的层次和边界。

而Uber,则差不多每一层都有自己的一套技术栈。比如手机功能团队就前端工程师和后端工程师都是在一起工作的,而且只要能满足自己项目的需要,爱用什么数据存储方案都可以自己决定。而对于某些团队来说,比如财务相关的钱,相应的技术栈都是以保证自己需求为最高目的的。

总之,我们的工作有趣之处不在于我们用什么技术栈,而在于我们做实时真实交易时要进行的大规模高速处理。支撑Uber的技术一定会变,但我们的速度、可塑性、紧迫感和克服困难的动力会一直都在。

一张由在旧金山随机选取的一百万条匿名行程汇总而成的街道图。较亮的是市中心

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值