如何用Go实现一款类似滴滴优步的网络约车软件(含源码)

本文介绍了一款类似Uber的打车应用NambaTaxi的技术实现,包括如何通过内存存储实现地图上的动画车辆效果、如何优化数据传输减少流量消耗、以及如何在后端存储和处理大量司机位置数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导读:我们经常使用打车软件出行,也经常思考其架构设计。本文作者在所在国家也负责开发一款打车软件,并且开源了其中大部分代码,可以帮助我们更好了解网络约车软件的架构体系。本文由高可用架构翻译。


0?wx_fmt=png

各位读者好,本文将给大家分享我们如何通过内存存储实现地图动画车效果。 我们公司也运营了一个类似 Uber 的软件 Namba Taxi,我们需要在客户端主屏幕上显示动画车。 这篇文章是关于功能如何完整实现的文章,主要目的不是介绍 Go 语言。


开始

这个故事始于2015年,我们的移动开发人员开发一款软件,工作主题是为出租车司机提供打车服务。 在应用程序中,动画汽车看起来像下面的图中动画那样 [1] 。

0?wx_fmt=png


实现的第一步

我们第一次的尝试比较简单:

  1. 处理请求并保存坐标。

  2. 创建另一个请求并为汽车设置动画。

显而易见,这样做存在一些问题,如大家在一些打车软件所见,我们不能正确地绘制汽车路线,汽车可能跑在田野,森林,湖泊和公寓上,用这种方法后效果看起来是这样的 [2]。

0?wx_fmt=gif

  1. 发起请求。

  2. 获取坐标。

  3. 将保存的坐标发送到服务器。

  4. 通过 OSRM 构建路线。

  5. 返回数据到客户端。

通过线路规划体系,现在似乎可以工作了,但我们又面临单向道路的问题

0?wx_fmt=png

例如,司机停留在红点的十字路口。 但他的设备位置准确性有问题,导致数据标记在十字路口的对面。 在客户端,我们获取这些坐标,保存并发送到后端,OSRM 建立一个合法的路线,并返回给应用程序。因为客户端移动得非常快,所以这种情况路线规划很可笑。我们检查两点之间的最短距离,并且不建立距离小于 20 米的路线。 使用该算法经过几天的测试后,我们决定发布我们的应用程序并希望获取一些反馈。

  1. 第一是车费计算器,计算是在司机端(客户端)完成,这样避免发送无用的请求,可以节约很多服务端资源。 另一方面,为了安全等方面考虑,我们需要在服务器端复制数据并保存它。

  2. 此外,我们意识到每 15 秒一次上报太少,因为用户在屏幕打开后,15秒后才会看到车在移动。

  3. 此外,我们在司机端的 GPS 模块有很多问题,这个可能跟司机的手机设备相关。

  4. 最后,我们想要在主屏幕上渲染动画车。


还需要解决的问题


  1. 从司机收集更多的数据

  2. 在主屏幕上显示动画车

  3. 在服务器端存储行车过程中计费数据

  4. 节约移动流量

  5. 每秒收集一次数据

我想谈一谈有关节约移动流量带宽的问题。在我们国家,出租车收费非常便宜,我们像使用公共交通那样使用出租车。 例如,从城市的一边跑到另一边可能只需要 2 欧元,这就跟在巴黎坐地铁价格差不多。但另外一方面移动带宽成本还也很高,如果我们每秒节约 100 字节,那么我们将给为公司节省差不多 2000 美元。


数据追踪


  1. 司机位置(纬度,经度)

  2. 司机当前的 session 信息,在登录时我们会给司机端提供 session id

  3. 订单信息(订单 ID 和车费)

我们决定每一次数据上报应小于 100 字节。 我们寻找传输协议来解决这个问题

正如你可以看到,我们审视了以下几个协议:

  1. HTTP

  2. WebSockets

  3. TCP

  4. UDP

对我们来说理想的选择是 UDP,因为:

  1. 我们只发送数据报

  2. 我们不需要保证送达

  3. 极简主义

  4. 保存大量数据

  5. 只有 20 字节开销

  6. 在我们的国家的移动网络没有被阻止

至于数据序列化,我们考察了:

  1. JSON

  2. MsgPack

  3. Protobuf

我们选择 ProtoBuf,因为它对小数据非常有效。

0?wx_fmt=png

以看到最近的竞争对手是 PB 的三倍。(小编:可以参考 TimYang 的一条微博 [3] )

每次上报总共的数据


  1. 42 字节的业务数据

  2. 加上 20 字节的 IP 报头

  3. 得到每次上报 62 字节数据

当我们获得数据时,我们考虑如何存储。


数据存储


我们需要存储这些数据:

  1. 标识司机的会话信息 session id

  2. 车牌号

  3. 订单 ID 和计费信息

  4. 执行搜索的最后位置

  5. N 次最后位置以规划路线


使用的存储


  1. 使用 Percona 存储所有数据。 我们存储司机,订单,计费等。

  2. Redis 作为用于缓存。

  3. Elasticsearch 用于地理编码

如上所述,当有大量在线司机时候,使用这些存储来保存数据并不方便。 所以我们需要地理索引。

我们评估了两个地理索引:

  1. KD 树

  2. R 树。

我们对地理索引的要求:

  1. 搜索 N 个最近的点。

  2. 我们需要一个平衡树,以在最糟糕的情况下提供最好的搜索


KD 树0?wx_fmt=png

KD 树并不适合我们的需要,因为它是不平衡的,只能搜索一个最近的点。 我们可以在 kd-tree 上实现 k-nearest 邻居,但是没必要重造轮子,因为 R-tree 已经解决了这个问题。

R-树

0?wx_fmt=png

它看起来像这样。 我们可以执行搜索 N 个最近点,并且它是平衡树。 我们选择了这个。

您可以得到它的 Go 语言实现源码 [5]。

下面是我们的存储架构。

  1. 我们将所有数据存储在内存中。

  2. 我们使用 R-tree 执行搜索最近的司机

  3. 此外,我们使用两个检索图,可以并按车牌号或session执行搜索


我们打车软件最终算法


这里是后端的最终算法:

  1. 使用 UDP 传输数据

  2. 尝试从存储获取司机

  3. 如果存储不存在 - 则从 Redis 获取司机

  4. 检查并验证数据

  5. 将司机保存到存储

  6. 如果不存在 - 初始化 LRU

  7. 更新 r-tree

HTTP 接口

我们实现了这些接口:

  1. 返回最近的司机;

  2. 从存储中删除司机(通过车牌号或session id)

  3. 获取行程信息

  4. 获取司机信息


结论

最后,我想给出我们在后端系统中总结的经验:

  1. UDP + Protobuf 以节省数据

  2. 内存存储

  3. R 树获取最近的司机

  4. LRU 缓存用于存储最后的 n 个位置

  5. OSRM 用于地图匹配和定制路线

    0?wx_fmt=png

您可以在 github [5] 上查看上面整个过程的源代码。现在功能还比较简单,但实现了文章中描述的许多功能。

参考资源

  1. GIF 动画下载:https://cdn-images-1.medium.com/max/1600/1*nI6cNApASR1mg6F5Sjgp7Q.gif

  2. https://cdn-images-1.medium.com/max/1600/1*KfGB1SARPoqOUtPtl4NNBg.gif

  3. http://weibo.com/10503/24F1QpDmL

  4. https://github.com/dhconnelly/rtreego

  5. https://github.com/maddevsio/openfreecabs

  6. 本文英文原文:https://blog.maddevs.io/how-we-built-a-backend-system-for-uber-like-map-with-animated-cars-on-it-using-go-29d5dcd517a#.npo2x5788

推荐阅读

本文由高可用架构翻译,转载请注明出处,技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。

高可用架构

改变互联网的构建方式

640?wx_fmt=jpeg
长按二维码 关注「高可用架构」公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值