【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

Uber 是如何基于 Go 语言构建高 QPS 服务的?

  • 2016-04-11
  • 本文字数:2255 字

    阅读完需:约 7 分钟

在 2015 年初,我们构建了一个只做一件事(也的确做的非常好)的微服务——查找地理围栏(geofence lookup)。一年后,这项服务已经成为 Uber 数百个正在运行的服务中每秒查询次数(QPS)最高的服务。接下来,本文将谈论我们构建这项服务的原因以及我们是如何使用 Go 语言快速构建和扩展这项服务的。

背景

在 Uber,一个地理围栏就表示地球表面上人为划分的一个地理区域。此外,我们进一步在基于地理的配置中使用地理围栏的概念。地理围栏的概念在很多地方发挥了很重要的作用——向用户展示在某个位置可使用的产品时,定义机场等特殊用途区域时以及在很多人同时呼叫实现动态定价时。

Colorado 的一个地理围栏样例

在提取用户手机的经纬度坐标等基于地理位置的配置信息时,需要做的第一件事情就是确定该位置落在哪个地理围栏中。而该功能已经在多个服务或模块中被实现。但是,当我们从一体化架构转移到基于微服务的架构时,我们选择了将这项功能集中在一个新的单独的微服务中进行实现。

选择Go 的原因

当我们评估所要使用语言的时候, Node.js 正是广大的服务设计团队普遍采用的编程语言。而我们也在 Node.js 的使用方面有着丰富的经验。然而,Go 语言却由于以下原因满足了我们的需求:

  • 高吞吐量和低延迟的需求。Uber 的手机端 App 在发送请求时,必然会触发一次查找地理围栏的操作。而服务器必须要能够对每秒上万次的请求以 99% 的概率响应时间小于 100ms 的速度进行响应。
  • CPU 密集型负载。查找地理围栏需要使用计算密集型的 Point-In-Polygon(PIP)算法。尽管 Node.js 可以很好的用于 I/O 密集型的服务,解释执行以及动态类型定义等的特性使得它并不适合于我们的使用场景。
  • 非中断式的后台加载。为了保证查询操作是基于最新的地理围栏信息而进行的,服务必须要能够根据多个数据源的信息在后台实时刷新内存中的地理围栏数据。因为 Node.js 是单线程的,后台刷新很可能会占用一定的CPU 时间(如CPU 密集型的 JSON 的编译工作),最终导致部分查询的响应时间过长。但是,对于 Go 语言而言,这完全不是问题。 Goroutine 可以运行在多个 CPU 核上,并且可以在响应前端查询的同时后台并行进行刷新数据的工作。

是否建立地理信息索引——这是个问题

当给定了一个经纬度坐标的位置信息时,如何从上万个地理围栏中找到该位置所在的那一个呢?最直接而暴力的解决方法为:浏览所有的地理围栏,然后采用光线投射(Ray Casting)等算法进行PIP 检查。但是,这种方法实在是太慢了。那么,我们怎么才能有效的缩小搜索空间呢?

由于Uber 的商业模型是以城市为中心的,我们并没有采用 R-tree S2 等结构来索引地理围栏,而是采用了一个相对要简单很多的算法;商业规则以及相关的地理围栏都和城市相关。这使得我们可以采用层次式的方式来组织地理围栏——第一层是定义城市边界的围栏,而第二层是城市内的城市围栏。

对于每一次查询,我们首先线性扫描所有的边界城市围栏,找到目的城市。然后,我们采用另外一种线性扫描的方法来找到城市内的目标城市围栏。尽管新算法的复杂度仍然是 O(N),它却把 N 从万降到了百,大大减少了算法执行的复杂性。

架构

根据设计需求,我们希望这项服务是无状态的。因此,每一次请求都可以发送给任意的服务实例,并获得相同的结果。这意味着每一个服务实例都必须掌握全局信息,而非局部信息。我们采用了一种确定性的轮询调度策略,从而保证了不同服务实例的地理围栏数据都是同步的。这样,该服务的架构也非常简单。后台任务周期性地轮询来自不同数据源的数据。而这些数据就保存在主存中以服务不同的查询。同时,数据被串行保存在本地文件系统中,以实现系统重启时的快速引导。

查询地理围栏服务的架构

处理Go 存储模型

我们的服务架构需要对内存中的地理索引信息进行并行读写访问。在特殊情况下,后台轮询任务修改索引,而前台查询引擎同时从索引中读取信息。相比于利用单线程的Node.js 进行服务编写的情况而言, Go 存储模型必然会遇到挑战。尽管 Go 语言可以利用 goroutine 和 channel 自然的实现并行读写,其带来的性能影响不可忽视。我们试图利用 sync/atomic 包中的 _StorePointerLoadPointer_ 原语来自己管理内存屏障。但是,这导致了代码难以理解和维护。

最终,我们选择了一种折衷的方式——利用读写锁来保证对地理索引的同步访问。为了减少锁的竞争,新的索引片段在被自动交换到主索引之前都是处于隐藏状态的。相比于_StorePointerLoadPointer_ 方法,这种锁会轻微增加查询延迟。但是,我们维护代码库的工作却变得简单很多,非常值得。

我们的经验

回首过往,我们非常开心选择了 Go 语言来编写我们的服务。其带来的好处包括:

  • 高的设计效率。对于 C++,Java 或 Node.js 的开发者而言,学习 Go 语言只需要几天。而且代码维护工作也很简单。(这一切都是因为静态类型检查。)
  • 高吞吐量和低延迟。在我们处理非中国区流量的数据中心中,该服务在 40 台机器 35% 的 CPU 利用率的情况下的最高 QPS 为 170,000。其响应时间为 95% 的概率低于 5ms,99% 的概率低于 50ms。
  • 超级可信。从启动到现在,该服务正常运行的时间为总时间的 99.99%。唯一的一次停止服务也是由初级编程错误和第三方库中的文件描述符泄露 bug 引起的。最重要的是,我们迄今为止没发现任何有关 Go 运行时的问题。

未来的展望

尽管 Uber 曾经主要采用 Node.js 和 Python,Go 语言正在成为 Uber 设计师构建新服务的选择。


感谢郭蕾对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-04-11 17:276625
用户头像

发布了 268 篇内容, 共 117.7 次阅读, 收获喜欢 24 次。

关注

评论

发布
暂无评论
发现更多内容

数据库主键一定要自增吗?有哪些场景不建议自增?

CRMEB

Move Protocol Beta测试版稳定,临时决定奖池规模再扩大

西柚子

如何使用 DATAX 以 UPSERT 语义更新下游 ORACLE 数据库中的数据

明哥的IT随笔

oracle 大数据 数据仓库 DataX

启动!阿里巴巴编程之夏2022

阿里巴巴云原生

阿里云 云原生 编程之夏

数字藏品系统开发,NFT艺术品交易平台搭建

薇電13242772558

NFT 数字藏品

Move Protocol Beta测试版稳定,临时决定奖池规模再扩大

小哈区块

深入剖析 HIVE 的锁和事务机制

明哥的IT随笔

大数据 hive 数据仓库

天天在都在谈的S3协议到底是什么?一文带你了解S3背后的故事

wljslmz

对象存储 S3 6月月更

自助洗车方便主要体现在哪方面

共享电单车厂家

自助洗车 自助洗车加盟

Move Protocol Beta测试版进行时,瓜分生态核心权益MOMO

鳄鱼视界

昇腾科研创新使能计划赋能开发者  华为计算提供三大维度支持

Geek_2d6073

自助洗车一次费用不到10元你敢信

共享电单车厂家

自助洗车加盟 自助洗车费用

Gartner 网络研讨会 “九问数字化转型” 会后感

明哥的IT随笔

数字化转型

有哪些好用的工作汇报工具

优秀

低代码 工具软件

Docker入坑篇

青柚1943

Docker DevOps 云原生 容器化

揭秘支撑百度搜索、Feed、小程序三大业务的MVVM框架设计思想,San 核心人员倾力打造

图灵教育

前端开发 好书推荐 框架设计

Nebula Graph入驻阿里云计算巢,助力企业打造云上超大规模图数据库

阿里云弹性计算

spark 分布式 云原生 图数据库 计算巢

去中心化挖矿LP流动性DAPP系统开发案例

开发微hkkf5566

自助洗车到底有哪些不一样的地方

共享电单车厂家

自助洗车加盟 24小时自助洗车

你的城市有24小时共享自助洗车吗

共享电单车厂家

自助洗车加盟 自助洗车店

为 Serverless Devs 插上 Terraform 的翅膀,实现企业级多环境部署(上)

阿里巴巴云原生

阿里云 Serverless 云原生 开源项目

带你区分几种并行

华为云开发者联盟

后端 开发 华为云

Dubbo3 官方文档贡献者征集令

阿里巴巴云原生

阿里云 开源 云原生 dubbo

北京web前端培训 | React全家桶之入门介绍

@零度

React web前端开发

Move Protocol Beta测试版再调整,扩大总奖池

股市老人

Move Protocol Beta测试版稳定,临时决定奖池规模再扩大

EOSdreamer111

在线文档协作:办公必备高效率神器

小炮

秒云云原生信创全兼容解决方案再升级,助力信创产业加速落地

MIAOYUN

运维 云原生 信创 智能运维 信创云

力扣每日一练之双指针1Day8

京与旧铺

6月月更

自助洗车为什么可以做到24小时营业

共享电单车厂家

自助洗车机 自助洗车加盟 24小时自助洗车

Serverless 时代下微服务应用全托管解决方案

阿里巴巴云原生

阿里云 Serverless 微服务 云原生

Uber是如何基于Go语言构建高QPS服务的?_语言 & 开发_张天雷_InfoQ精选文章