一键用车如今已经烂大街,可是 Uber 简单的界面下又隐藏着怎样复杂的后端架构和服务呢?这些复杂的路径规划和订单匹配算法又是如何让车找到人,将人送到目的地的呢?如今让咱们揭开Uber App这神秘的面纱。git
下面是Uber以前解决路径规划问题的方法而且讲解了咱们是如何从五年之前 三藩市单一的 UberBLACK 服务成长的到如今天天百万以上用车量的。github
当五年之前Uber平台刚刚开始运营的时候,到达时间估算(ETA)是咱们推出的第一个特性。这是乘客对咱们的第一印象。web
2012年早些时候,应用内显示的 ETA算法
在Uber早期,咱们用了一系列路径引擎的组合(包括OSRM)来作ETA。(咱们刚开始并无应用内的导航功能,所以咱们只能用它来作ETA而且匹配所显示的车辆位置。segmentfault
咱们称这种服务为”黄金ETA”,而它是须要在一个路径引擎上创建模型而且用Uber 数据在同时同地的相似数据中调整初始的估计值。这种解决方案一般要同时观察成千上万的Uber行程,用初始路径引擎对比它们的ETA。黄金ETA比以前没有数据对比的ETA显然来得更好。可是,这里又产生了一个冷启动的问题:当咱们进军一个新的城市拓展业务的时候,咱们是没有足够的数据来打造”黄金ETA”的。也就是说,随着咱们不断扩张,咱们按期须要一些新特性来知足咱们快速增加的须要,这时候用以前的开源解决方案(OSRM)已经知足不了咱们高度定制化的需求了。后端
咱们已经用黄金ETA好久了,可是随着咱们在更多城市和服务上的扩展(好比在2014年3月开启的 UberRUSH 服务以及 咱们在2014年春天开启的 UberPOOL 服务),咱们须要为Uber打造一个本身的路径引擎的需求已经变得很是迫切了。所以,在2014年,咱们开始着手打造咱们本身的全套路径引擎,咱们称之为 Gurafu。Gurafu的目标是为Uber量身定制一个高性能、高精度的ETA计算工具。服务器
在咱们具体实施以前,让我讨论一下咱们须要一个路径引擎的必要性。这整个的路网就像一个图结构(运筹学中图论的概念)。节点表示十字路口,边表示道路。这个边的权重表示一个有趣的度量:这段距离或时间路线常常被人通过。其余一些像单行道,转弯限制、转弯成本以及速度限制等概念都在这个图结构模型被考虑到了。微信
固然,这不是通往真实世界的惟一模型。有些人也把道路做为节点,而两个路段的转换做为边。相对于以前提到的基于点的表达式这就是所谓的基于边的表达式。每种表达式都有本身的权衡,所以在肯定使用哪一种模型以前明白咱们须要什么就变得很是重要了。数据结构
一旦你的数据结构肯定了,你就可使用不一样的路径规划算法来寻优。举一个简单的例子,你能够尝试的最基础的Dijkstra搜索算法,这种方法是今天大多数搜索算法的基石。可是在生产环境下,Dijkstra或者其余一些算法经常无法处理太大规模的图结构,它老是显得速度太慢了。架构
OSRM 是基于收缩的层次结构的。系统基于收缩层次结构来提高性能,经过预处理路线的图结构只用毫秒级的时间就完成整个路径的计算。下面99%的响应都是在100毫秒内完成的。由于在每次车辆收到用车请求的时候咱们都须要计算因此咱们很是须要这个功能。可是,由于这个预处理步骤是十分缓慢的,想要作实时的交通处理是很是困难的。而我咱们的数据会用全世界的道路花12个小时来构建这个收缩图结构。这意味着咱们很难去更新这个交通流量的信息。
这是就是为何一些预处理和变换经常须要加速查询。(这类算法的最近例子包括了高速公路层次算法、ALT算法以及自定义路径规划算法)
这里是从2014年开始尝试构建本身的路径引擎所作的工做:
在咱们的路径图结构中,随着新交通讯息涌入,咱们须要可以动态更新这些图结构的边权重。就像咱们提到的,咱们所用的 OSRM 是用 收缩层次来作路径寻优算法的。收缩层次的一个缺点就在与当边权重更新,预处理过程须要用整个图结构从新再跑一边,当咱们跑稍微大一点的图结构就须要好几个小时,更不要说全世界了。因此这个收缩层次算法对于实时的交通变化并不适合,咱们须要一个增量算法。
收缩层次的预处理步骤能够经过动态更新来快速实现,这种状况下大多数节点的排序将保留,而只是更新须要更新的节点,这样咱们就能够大大减小预处理的计算量了。(这不是Virtual Dom吗?)用这种方法,咱们以每次更新整个世界的图结构中10%的路段,仅仅用10分钟就能够实现动态更新。可是,要估算ETA的话,10分钟看起来对交通流的变化仍是有点延迟。所以,这条路最后也是一条死胡同。
咱们也用一种叫做分片的方式,将整个图结构分解成几个小的地理区域,分而治之。分片加速了咱们构建图结构的时间。可是,这里须要在咱们的基础设施上为此作一些工做,而且在每一个地区的服务器集群大小上老是会遇到瓶颈。若是一个地区在高峰期一会儿有太多的请求,那么其余服务就不能共享加载这种分片的资源了。咱们想要利用咱们有限的服务器资源,所以咱们也并无采用这种解决方案。
对于小范围的实时更新,咱们尝试使用A星搜索算法。在更高的层面上,A星是Dijkstra搜索算法的启发式实现,所以A星优先找到从A到B之间的一条可能的最优路径。这意味着咱们能够实时更新图结构的边权重,并且在不须要作任何预处理的条件下计算交通流量的状况。既然大多数航线咱们须要计算是短途旅行(从司机乘客途中时间),那么A *在这种状况下其实很是有效的。
不过咱们知道A星算法是一个权宜之计,由于它对于比较长的路线规划显得很是的慢:A星的响应速度与深度遍历的节点是相关的,以几何增加。(例如,从普雷西迪奥到旧金山地区教会区的路线计算时间是120毫秒,这相比收缩层次算法花费了数倍的时间)。
即便A星算法利用地标、三角不等式和几个预先估计技巧,不会增长A星遍历的时间,足以使它成为一个可行的解决方案。
可是对于长途旅行来讲,A星仍是不可以快速响应,所以咱们又回到了使用静态收缩图结构的老路。
咱们既须要使用预处理来加速计算,又想要快速更新边权重来支持实时交通。咱们的解决方案仅仅经过从新刷新整个图结构的一部分就能够完成预处理过程,有效解决了实时交通更新的问题。
由于咱们的解决方案将图像划分为层相对独立的小模块,经过并行执行预处理,让咱们可以在必要时让这个过程加速计算。(了解更多,点击这里!)
在咱们的第一个Gurafu版本已经准备好测试以后,为了知道咱们咱们以前推出的新路径引擎的效果,咱们打算在2015年1月分别纪录了Gurafu和黄金ETA的实时ETA准确度。
2015年1月份咱们制做了一个内部的仪表盘来显示ETA准确性:Gurafu(紫色)相比黄金ETA(绿色和红色)每次预测ETA都更加精准。注意到每每在交统统勤的高峰期(周三晚上和周四早上)Gurafu的表如今这时候一骑绝尘。这里,咱们显示的三藩市,可是这个模式在咱们全部的其余城市也是如此。
2015年4月,咱们在全球范围内推出了一个全新的更精确的ETA预测的路线引擎系统。新系统是基于咱们新的路线引擎Gurafu。另外,基于咱们从司机端收集的GPS数据,咱们还推出了 Uber的第一个历史交通系统,咱们称之为 Flux。
咱们使用独有的ETA系统主要是为了获取乘客,但咱们也在整个行程中记录每次ETA预测的准确性。为了衡量ETA的准确性,咱们用从Kafka日志获取的实时检索实际到达时间(ATA)对比咱们的ETA并以此构建了一个工具。此外,咱们的团队成员也可能直接从应用程序内报告ETA 的 bug。衡量线路质量有时须要在个案基础上作可视化检查,咱们还创建了一组丰富的web可视化工具,检查和比较不一样模型和路线从提供者的ETA预测状况。
Goldeta 对比 Gurafu: 经过超过十万次的行程采样,系统计算预计到达时间(ETA)与实际到达时间(ATA)的差距。新的ETA系统Gurafu的偏差分布更尖更高,这意味着新系统比旧系统在预测是更加稳定(方差更小)。纵轴是行程估计错误的数量。这意味着只有在不多几率会有小错误发生。
这些健康检查显示去年咱们已经走过了漫长的道路。
超高效路线规划和高精度的ETA是相当重要的。咱们用现代的路线算法来创建一个精心优化系统来应对每秒成千上万请求而且作出毫秒级的快速响应。咱们全部的新服务,如uberPOOL和UberEATS都开始部署这个系统。
这个优化之旅尚未结束。随着咱们扩展和改进像UberPOOL这样的产品,根据地点和时间的上下文肯定最优路线还将使得服务准确性和效率进一步提高。
咱们还想让打电话保平安也变得更加可靠、智能。若是这听起来像是一个你感兴趣的挑战,咱们但愿你加入咱们的行列。
原文做者: THI NGUYEN 译者: Harry Zhu 英文原文地址:
https://eng.uber.com/engineering-an-efficient-route/做为分享主义者(sharism),本人全部互联网发布的图文均听从CC版权,转载请保留做者信息并注明做者 Harry Zhu 的 FinanceR专栏:https://segmentfault.com/blog/harryprince,若是涉及源代码请注明GitHub地址:https://github.com/harryprince。微信号: harryzhustudio商业使用请联系做者。