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

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

  1. 企业和团队如何创建高效实用的知识管理体系

    现代管理学之父彼得·德鲁克讲过:"21世纪的组织,最有价值的资产是组织内部的知识工作者和他们的生产力." 如今从美国到中国,知识管理开始被越来越多的团队和组织重视,都纷纷建立起属于 ...

  2. Python 语言创建 SAP2000 s2k 文件

    目 录 Blog Links 一.前言 二.s2k文件数据库 2.1 数据库结构 2.2 默认Table的创建 三.属性 3.1 材料属性 3.2 框架截面 3.3 板壳截面 3.4 实体截面 四.对 ...

  3. R语言创建自定义颜色(分类变量与颜色形成稳定映射)实战:设置因子变量(分类变量)到可视化颜色的稳定映射

    R语言创建自定义颜色(分类变量与颜色形成稳定映射)实战:设置因子变量(分类变量)到可视化颜色的稳定映射 目录

  4. C语言创建map,遍历map

    C语言创建map,遍历map 要实现的功能如下 完整源代码实现如下 要实现的功能如下 代码实现创建一个map,代码实现打印map 完整源代码实现如下 #include <iostream> ...

  5. C语言创建一个节点Node(附完整源码)

    C语言创建一个节点Node 节点结构体定义 C语言创建一个节点Node完整源码 节点结构体定义 struct node {struct node *leftNode;int data;struct n ...

  6. go tcp客户端自动重连_使用 Go 语言创建 WebSocket 服务

    今天介绍如何用 Go 语言创建 WebSocket 服务,文章的前两部分简要介绍了 WebSocket 协议以及用 Go 标准库如何创建 WebSocket 服务.第三部分实践环节我们使用了 gori ...

  7. 使用Go语言创建WebSocket服务

    今天介绍如何用Go语言创建WebSocket服务,文章的前两部分简要介绍了WebSocket协议以及用Go标准库如何创建WebSocket服务.第三部分实践环节我们使用了gorilla/websock ...

  8. c语言链表错误,C语言创建链表错误之通过指针参数申请动态内存实例分析

    本文实例讲述了C语言创建链表中经典错误的通过指针参数申请动态内存,分享给大家供大家参考之用.具体实例如下: #include #include // 用malloc要包含这个头文件 typedef s ...

  9. 先根序列创建二叉树c语言,用C语言创建二叉树并先序遍历.doc

    用C语言创建二叉树并先序遍历 用C语言创建二叉树并 #include "stdio.h" #include "stdlib.h" typedef struct ...

最新文章

  1. 洛谷-题解 P2672 【推销员】
  2. png库结合zlib库使用出现的一个链接问题的解决
  3. wxml 点击图片下载_微信小程序通过ipfs-api 实现图片文件在私有ipfs网络的上传与下载显示...
  4. 网页全终端视频流媒体视频直播/点播播放器EasyPlayer.js实现WEB播放H265/HEVC视频方案介绍
  5. python线程代码_python--(十步代码学会线程)
  6. Penang Industrial Zone
  7. strace oracle
  8. 如何在网页中插入Flv视频文件
  9. pycharm提示your evalluation license has expired解决方法
  10. 超适合小白的python新手教程
  11. 矩阵的Cholesky分解
  12. ios 裁剪框大小_IOS UIimageView裁剪
  13. 谈谈基于SQL Server 的Exception Handling[中篇]
  14. 微软云计算介绍与实践(实践之二十七)
  15. leetcode笔记--7 Find the Difference
  16. 用“压缩映射”原理证明数列收敛
  17. [3维影像]360度杯子环绕拍摄
  18. 如何找到最新的RFC文档
  19. html设置首缩选进两字符,首行缩进2字符怎么设置 css
  20. android 遥控器按键监听,安卓遥控器按键监听

热门文章

  1. zabbix监控linux网卡流量,zabbix实现linux流量变化率监控
  2. vue怎么使用php调取数据,vue 数据操作
  3. 小型云台用的是什么电机_直流电机的工作原理是什么?未来的电动车都会用直流电机吗?...
  4. cas4.2.7与shiro进行整合
  5. windows8建立局域网的方法
  6. cups支持的打印机列表_Win10“Microsoft Print to PDF”虚拟打印机不见了,如何找回?...
  7. (2021) 25 [持久化] 文件系统实现:FAT和UNIX文件系统
  8. mysql异常恢复工具_[MySQL异常恢复]mysql ibd文件恢复
  9. Java线程怎么发送消息_Java客户端Socket如何能在阻塞线程下收到服务端发送来的消息?...
  10. oracle tns 代理配置_Toad for oracle安装配置与使用