Uber如何使用go语言创建高效的查询服务

在2015年初我们创建了一个微服务,它只做一件事(也确实做得很好)就是地理围栏查询。一年后它成了Uber高频查询(QPS)服务,本次要讲的故事就是我们为什么创建这个服务,以及编程语言新秀Go如何帮我们快速创建和扩展该服务。

背景

在Uber,一个地理围栏就是在地表人为定义的地理区域(或多边形几何区域)。地理围栏在Uber被广泛用于基于地理位置的设置。向用户展示给定区域有哪些产品可以使用,根据特殊需要(如机场)定义区域,并在乘车高峰时在相邻区域实施动态定价是我们产品的重要应用场景。

一个科罗拉多地理围栏示例。

第一步是通过用户手机获取地理位置信息如经纬度,进而确定用户所在地理围栏。这个功能分散在多个服务或模块中。因为我们从整体架构向微服务架构迁移,我们选择将这个功能做成一个新的微服务。

使用Go语言

Node.js曾经是我们实时市场团队主力开发语言,所以我们在Node.js上有较多的知识储备和经验。但是Go在以下几个方面更符合我们的需求:

1、高吞吐低延迟的需要。Uber手机应用中的每个请求都需要地理围栏查询,而且响应快速(99% < 100毫秒)频繁(每秒成千上万), 2、适用于CPU密集型。地理围栏查询是点聚计算的CPU密集型服务。Node.js非常适合我们其他I/O密集型应用,但由于Node天生就是解释型动态语言,所以它不适合此类应用。 3、非中断后台加载。为了给查询服务提供最新的地理围栏数据,服务需要在后台不断的从多个数据源加载内存数据。因为Node.js是单线程的,所以后台更新会对CPU造成较长时候的堵塞(例如,CPU密集的JSON解析),从而影响到查询响应时长。但Go不存在这些问题,因为goroutines 可以使用多核,后台任务和前台查询可以并行。

是否使用地理信息索引:这是一个问题

通过经纬度指定一个地理位置后,如果从我们成千上万的地理围栏中确定它属于哪一个?简单粗暴的做法是:使用点聚检查方式,如光线投射算法,从所有地理围栏数据中查找。但这种式太慢。所以,我们如何缩小查询范围以提高效率?

我们没有使用R-tree做地理围栏索引和比较复杂的S2,通过观察我们发现Uber的业务模式是以城市为中心的;业务规则和地理围栏通常用一个城市来定义,所以我们选择了一个简单的路由方式。我们把地理围栏整理为两层结构,第一层是城市地理围栏(定义城市边界),第二层是每个城市内的地理围栏。

对于每个查询,我们首先对所有城市地理围栏做线性扫描查找所在城市,然后对该城市的地理围栏数据做线性扫描。这个解析方案的运算复杂度是O(N), 通过这个简单的技术我们将N从10,000s减少到100s。

架构

我们希望这个服务是无状态的,这样每个请求可以发送到任意实例,而且得到结果是一致的。这意味着每个实例都拥有全量数据,而不是只存储部分数据。我们生成了一个统一的拉取计划,这样不同服务实际的地理围栏数据可以保持同步。因面这个服务的架构也就变得简单。后台任务定时从不同的数据存储拉取地理围栏数据。这些数据是在内存中存储,以提高查询速度,当服务需要重启时会序列化到本地文件。

处理Go内存模型

在我们的架构中需要对内存中的地理索引数据并发读写。当后台拉取任务写索引时,可能前台查询引擎同步读取索引。有Node.js经验的人熟悉了单线程模式,Go的内存模型对他们是一个挑战。这对我们曾产生对负面影响。我们试图使用sync/atomic 包的原语StorePointer/LoadPointer 来管理内存边界问题,但这导致代码很脆弱且难以维护。

最后,我们采取了折中的方式,使用读写锁来异步处理对地理索引的访问。为了减少锁的争夺,新的索引在以原子的方式合并到主索引之前先建立索引片段。与 StorePointer/LoadPointer的方式相比,这些稍微增加一些延迟,但我们有理由相信代码的简洁和可维护性比这一点小小的延迟更有价值。

我们的经验

回顾以往,我们很庆幸当初使用Go语言,并使用这种新的语言开发我们的服务。亮点如下:

1、开发效率高。C++,Java和Node.js的开发者只需要很短的时间就可以掌握Go,代码易于维护。(静态语言更加清晰,没有莫名其妙的意外)。 2、在吞吐量和延迟方面性能很好。我们主数据中心,有针对非中国区的独立服务,在2015年度高峰期间40台服务器在170k QPS的负载情况下CPU只使用了35%。95%的响应时间小于5毫秒,99%的响应时间小于50毫秒。 3、超级稳定,这个服务自上线以来,99.99%的时间正常运转。当机时间主要是由初学者的编程错误和第三方库的文件描述符泄露导致。我们至今尚未遇到Go的运行时错误。

接下来?

过去Uber主要使用Node.js和Python,很多Uber新的服务开始选择使用Go来创建。Go是Uber未来的趋势,所以如果你对Go很有激情,无论是专家还是初学者,都欢迎你来应聘,我们正在招聘Go开发者,噢对了,传送门请点这里!

图片来源:“金门地鼠”,作者:Conor Myhrvold,摄于三藩市的金门公园。标题解释:地鼠(Go gopher)是Go项目的吉祥物,是Go的标识。

本文分享自微信公众号 - Golang语言社区(Golangweb)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-05-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

手机数据传输安全分析

如今手机已经成了我们离不开的伙伴和知己,它了解我们的日常生活。然而每一天在路上的时候,它都会收集我们的私密信息。平时我们会用它拍照,在社交网络中分享我们的心情;...

249100
来自专栏ThoughtWorks

浅谈微服务架构中的鉴权体系 | 洞见

在微服务架构中,有一个核心的问题是处理好“集权”(中心化)和“放权”(去中心化)的关系。虽然微服务的主旋律是把数据和业务拆成小而独立的模块,但我们仍然需要一个强...

37760
来自专栏申龙斌的程序人生

笑来投资演练小程序0.9版本发布

在阅读了李笑来得到APP上《通往财富自由之路》专栏第24周的“开始投资活动的条件是什么”文章之后,我快速用C#写了一款windows小程序,没想到发布在学习小组...

34670
来自专栏数据和云

防范攻击 加强管控 - 数据库安全的16条军规

近日的数据安全事故,引发了很多企业的普遍关注,而不少用户从彻查中确实发现自己的数据库已经被注入,这为大家上了数据安全的重要一课。 甚至有的企业要求停用PL/SQ...

34360

密码即将消亡,真的假的?

保护敏感私人信息的安全,防止他人对其的窥视,并不仅仅是一个现代的理念,这是我们几个世纪以来一直在表现的一种行为。从根本上来说,只要我们一直试图保护信息安全,那我...

13230
来自专栏FreeBuf

安卓曝大漏洞:一条彩信可控制手机,影响95%设备

以色列移动信息安全公司 Zimperium 研究人员 Joshua Drake 在 Android 系统中发现了多处安全漏洞,Android 2.2到5.1的所...

28270
来自专栏FreeBuf

SiteLock最新报告显示:针对网站的攻击激增,平均每天有63起

根据SiteLock于本周一发布的最新分析报告显示,在过去的几个月里,针对网站的攻击活动数量出现了大幅增加。 ? SiteLock的网站安全内部报告是基于对超过...

24590
来自专栏IT大咖说

中国首位IoT领域的GDE:Android Things全解析及展望

25520
来自专栏FreeBuf

Google Play Store启动漏洞赏金计划保护Android应用

? Google终于发布了Google Play Store的漏洞赏金计划,安全人员可以寻找或者报告Android应用中存在的漏洞。 这个项目的名称为 “Go...

30650
来自专栏Pythonista

Linux入门

  如果Windows意味着生活和游戏,那么Linux久代表着严谨和工作,学习Linux代表着你要开发软件,你要测试程序,你要维护服务器。那么依据呢?请看如下:

94650

扫码关注云+社区

领取腾讯云代金券