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

原文:HOW WE BUILT UBER ENGINEERING’S HIGHEST QUERY PER SECOND SERVICE USING GO

作者:KAI WEI      译者:杰微刊兼职翻译张万程   


在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的标识。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容