地图&消息的全球化解决方案
1.背景
货拉拉一直坚持中国和海外市场双线发展的策略。截至2020年3月,货拉拉业务范围已覆盖286座中国大陆城市,同时在中国香港、中国台湾地区运营,并在东南亚、印度、南美洲开通了19座城市。由于历史原因,目前业务在中国大陆和大陆以外的地区,分别使用两套系统在支撑业务。 由于系统的差异,导致用户体验、运营策略、支撑工具等都不一致;可能造成运营效率降低、人力成本提升等问题。在此背景下,公司提出“One Team, One System”的口号,地图平台&消息平台在此背景下,也需要满足全球化业务。
为了方便理解,本方做如下约定:
HLL: 表示中国大陆地区业务
LLM:表示中国大陆以外地区的业务
2. 地图解决方案
2.1 地图服务介绍
- 地图选点:App端拖图选择地点
- 地址检索:根据关键字进行地址检索
- 计价里程:起终点路径规划
- 距离测量:司机距离发货地距离测量
- 司机圈选:圈选附近的司机,用于派单
- 服务区域&行政区域支持:用于判断下单地是否提供服务
针对地图不同应用场景,我们的总体规划如下:
场景 | HLL | LLM | 备注 |
地图选点 | 百度SDK | Google Maps SDK | |
地址检索 | 百度检索 高德检索 | Google Maps Places API | 稳定性要求 |
里程规划 | 百度路径规划 | Google Directions API | 稳定性要求 |
距离测量 | 高德距离测量 | OSM | |
服务区域&行政区域支持 | 城市配置实现 | 地理围栏实现 | |
轨迹服务 | 自研轨迹服务 | 自研轨迹服务 | 100亿量/天 50K+ QPS |
2.2 地图服务总体规划
根据地图的业务场景,我们对地图的微服务做如下规划:
序号 | 项目 | 主要功能 |
1 | POI服务 | 地址检索服务、逆地址编码、Service Area等 |
2 | 路径规划 | 里程计算、路径规划、批量算路 |
3 | 轨迹服务 | 轨迹上报服务、轨迹查询服务 |
4 | LBS管理平台 | 服务管理、地图工具,地图数据等 |
地图服务在设计之初,充分考虑了未来可扩展性。在国际化项目进展,我们只需要针对Google Maps接口进行适配,通过较少的改动,就可以满足国际化地图业务需求。地图服务总体架构如下:

主要满足以下目的:
- 支持公司的多种产品线(同城、跨城、搬家、零担、企业等)
- 支持公司海外的产品线
- 对图商的服务能力进行抽象,适配;对业务系统提供统一的地图服务能力
- 服务稳定性保障、多图商备份
- 提供地图数据及工具平台
- LLM service area支持
2.3 POI服务国际化
2.3.1 POI服务场景
- 在HLL的业务场景下,我们主要使用百度以及高德的接口;在LLM场景下,我们需要支持Google Maps API接口。同时,由于Google Maps的接口与百度、高德存在差异,我们需要做些特殊处理。
- 地址多语言场景:LLM场景下,由于部分城市有多种流行的语言,因此我们需要对地址进行多语言化,以满足本地不同语种人的需求。如:A用户使用印度语下单后,地址会使用用户的印度语,司机B可能只会英语,需要看到A用户下单地址的英语版本。
- Service Area场景:在LLM中,能够提供服务的区域与HLL有很大区别。在HLL中,我们主要按行政区域来决定是否能够为用户提供服务;但在LLM场景中存在以下问题:
- 问题一:在海外国家,由于电子地理信息涵盖不完整,系统不能简单透过检测地图商返回的城市名称和区域名称来决定某个地点是否落在服务区域之内。需要自行定义服务区域。
- 问题二:在一个城市内, 某些区域有特别限制或这基于安全原因,我们不希望司机在这些地区提供服务。
- 问题三:某些区域只有指定服务能提供。例如在孟买,只有跨城运输的服务类型,才能从孟买去到 Pune 地区(终点必须是 Pune 地区),一般的运输只能服务孟买市区内的订单。
2.3.2 地址解决方案
针对以上两种场景,我们采取以下方式解决:
1.地址检索
- 我们使用Google autocomplete进行地址检索。POI服务前期设计我们充分考虑了多图商的场景,我们统一封装一套对外的接口,对图商的接口进行抽象、封装、适配;实现对上层应用系统屏蔽图商细节的目的。图商接入对应用系统而言相对透明。因此,我们在保持对外接口不做结构调整的情况,接入Google autocomplate接口,我们只要封装好接口,即可快速支持。
- Google autocomplate接口,与目前百度、高德的接口有较大差异,该接口拿不到地址POI点的经纬度信息,只会返回一个place id。因此,应用系统拿到这个POI信息无法得到下地址的坐标信息,我们需要做些个性化处理。
- App端拿 到place id后,需要再调用一次地址详情接口,通过地址详情接口,拿到对应POI的经纬度信息。
2.多语言场景
- 在这种场景下,我们提供了多语言地址接口,此接口也是依托Google Maps的地址国际化功能实现。
- 应用系统在需要展示多语种地址的情况,调用后台的多语言地址接口即可实现。
2.3.3 Service Area解决方案
Service Area属于LLM的核心基础功能,直接关系用户是否能够正常下单。统合考虑我们项目进度以及服务现状,我们针对Service Area功能总体按两步来进行:
1.第一步:先实现简单的自定义区域功能,并且提供接口,判断一个地点是否属于该区域。此功能目前已经上线, 主要解决Service Area的问题1,实现方案如下:
- 由LLM团队线下提供要服务的区域的多边形地理数据
- 我们将区域的多边形地理数据存储到数据库,目前通过人工方式导入。
- POI服务启动时,从数据加载所有多边形区域数据
- POI服务从数据库加载完多边形区域数据的同时,在服务内存里面构建空间索引信息。空间索引基于RTree 结构进行构建。
- POI服务提供接口,接收一个坐标,并判断该坐标属于一个多边形区域。此处判断就是基于前面在内存构建的RTree数据结构实现。能够实现快速检索功能。
此方案目前存在的问题有:
- 针对Service Area的问题2、以及问题3都不能很好解决
- 需要人工导入数据,效率比较慢
- 不支持多区域复杂的业务场景
2.第二步:实现完整的Service Area功能(目前开发中),主要增加或优化以下功 能:
- 提供专属Service Area管理功能,可以自定义属性,多边形区域。
- 提供专属接口,满足不同服务类型的对区域的要求。
- 详细流程图如下:

- 接口核心字段定义如下:
请求参数:
参数名 | 含义 | 规则说明 |
cityCode | 城市编码 | |
lon | 经度 | 小数点后不得超过6位 |
lat | 纬度 | 小数点后不超过6位 |
serviceType | 服务类型 | 服务类型 如果有定义针对定义的多边形服务范围则返回定义的多边形,如果没有则返回默认多边形 |
响应参数:
名称 | 含义 |
ret | 状态码 0:访问成功1:访问失败 |
msg | 状态说明 成功:success 失败:错误信息 |
data | 服务区域信息列表 |
count | 返回区域的数量 |
areas | 服务区域列表 如果多个服务区域则返回多个列表 |
├ name | 名称 |
├ allowStart | 是否允许作为起点 |
├ allowMiddle | 是否允许作为途径点 |
├ allowFinish | 是否允许作为终点 |
├ cityCode | 城市编码 |
├serviceType | 服务类型 |
├remarks | 备注 |
└ extField | 扩展属性 |
2.4 路径规划国际化
2.4.1 里程计算解决方案
由于里程计算的接口比较标准,国际化接入相对比较容易。我们使用Google Directions API来实现。代替HLL依赖的百度、高德接口。我们只需要对Google Directions API 进行接入即好。类结构如下:

GoogleDrivingServiceImpl即为本次国际化实现类。
2.4.2 EDA(预估到达里程)解决方案
EDA我们使用高德进行批量计算实现;在LLM中,我们使用OSM数据来提供EDA功能。
2.5 轨迹服务介绍
2.5.1 轨迹服务介绍
司机轨迹在我们整个业务生态中,属于非常重要的一环,是我们建立实时风控、智能分单、数据监控重要的数据。
轨迹服务设计是相对通用的业务,在支持整个LLM过程中,轨迹服务无需做调整,即可满足LLM对轨迹的需求。为了让大家了解轨迹服务,我们在此也对轨迹服务做一个简单的介绍,轨迹服务一共由3部分组成:
- 轨迹上报(包括单个上报、批量上报、批量预上报)
- 轨迹查询(历史轨迹查询)
- 轨迹数据异步存储(最新位置存储、历史轨迹存储)
2.5.2 轨迹服务存储方案
按照业务特点,我们将轨迹进行分级存储,满足不同的业务需求:
- 司机最新位置:主要用于司机圈选,使用Mongodb集群存储,按司机ID进行分片。同时建立好空间索引,支持业务系统通过GeoNear方法对司机最新位置进行检索)
- 司机最近的历史轨迹(最近45天):用于业务系统订单轨迹查询、AP2、LBS后台订轨迹查询,主要满足实时轨迹查询需求。使用HBase存储,可以根据Hbase的RowKey对数据进行快速检索。为了均衡司机轨迹数据,防止部分司机轨迹数据过于集中,我们使用以下方式生成 HBase的RowKey:
RowKey = Hex(MD5(driverId)) + locTime
- 离线的历史轨迹:用于轨迹离线数据分析,报表、以及轨迹指标。此部分数据我们依托公司的大数据平台,统一存储到大数据平台的Hive中。
2.5.3 轨迹服务主要流程
轨迹服务设计主要流程如下:

- App端通过http接口协议,向后台位置上报服务上报当前位置信息,App每10s(时间间隔可以配置) 报送一次位置信息,如果因为网络或服务原因,导致上报失败,会写入本地缓存,下次进行批量上报。
- 服务端接收到轨迹数据后,会对司机的Token进行校验,校验通过后会将轨迹数据写入Kafka。
- 消费服务异步消费kafka,将最新位置数据更新到Mongodb,将轨迹数据写入Hbase以及Hive。
3. 消息解决方案
3.1 消息中心介绍
消息中心主要为HLL&LLM提供统一的消息服务,主要包括
- SMS:实现短信功能(业务通知短信、验证码短信等)
- Email:邮件服务(通知、行程单等)
- Push:实现所有应用系统向App的消息推送
同时,消息中心按开放平台的架构进行设计,各应用系统以open api的方式接入消息中心;提供消息中心管理平台(信鸽)用于应用接入管理、消息统计、报表查看、策略调整等功能。
3.2 消息中心总体规划
消息中心服务规划如下:
序号 | 项目 | 主要功能 | 备注 |
1 | 短信服务 | 短信服务api、短信发送服务、短信回执服务、短信上行服务 | |
2 | 邮件服务 | 邮件服务api、邮件发送服务、附件上传服务 | |
3 | Push服务 | Push api网关、push service、回执上报、token上报 | |
4 | 信鸽平台 | 应用管理,系统配置,数据统计及报表 | 消息中心管理平台 |
消息中心总体架构如下:

3.3 短信服务国际化
3.3.1 短信服务介绍
SMS 提供以下两种短信方式:
- 基于模板的通知类短信
- 基于内容的营销类短信
短信服务实现功能:
- 多通道冗余备份,故障自动转移,失败重试
- 黑白名单支持
- 支持短信上行
- 模板管理
- 批量短信任务(支持基于名单和基于大数据的画像)
- 支持频率控制,短信记录存储,数据报表统计
短信服务架构如下:

3.3.2 短服务国际化方案
1.短信服务国际化主要解决以下问题
- LLM 短信通道问题
- 手机号码国家码问题(当前HLL系统没有存储手机号码的国家码,在LLM中会存在问题)
2.通道问题
短信服务规划之初,是按多通道来设计的。针对LLM的场景,我们是通过接入海外的短信服务商(Nexmo)来解决。
ChannelNexmoImpl就是本次国际化实现的,主要功能是接入海外的短信供应商Nexmo通道:

3.国家码问题解决
- 前端将国家码、手机号,组装合并传递给接入层,接入层透传号码给短信服务,不改动号码格式
- 号码格式采用 e.164 标准,+[国家或地区码][手机号] ,示例如:+8618811112222, 其中前面有一个+号 ,86为国家码,18811112222为手机号,无缝组合。
e.164参考:https://zh.wikipedia.org/wiki/%E5%9B%BD%E9%99%85%E7%94%B5%E8%AF%9D%E5%8C%BA%E5%8F%B7%E5%88%97%E8%A1%A8
4.短信校验
- 短信后台会调用libphonenumber包,校验国家码、手机号的合法性前端输入需要支持选择国家码,并且对手机号码进行弱校验
5.手机号码存储
- 使用一个字段存储手机号码(包括国家码以及手机号码)
- 需要检查数据库字段是否满足要求
- 需要检查判断手机号码格式的逻辑是否要调整
3.4 Push服务国际化
3.4.1 Push服务介绍
Push 服务提供后台服务到手机App之间消息推送的功能。主要实现新订单信息推送,订单状态变更推送,优惠券推送,活动营销推送等功能。
目前主要有两种实现方式:
- 基于第三方推送供应商实现,自研的TCP长连接服务
- 接入手机厂商的push通道(APNs, 以及中国大陆android 厂商的push;LLM接入Google FCM)

3.4.2 Push服务国际化解决方案
- Push服务国际化主要是解决通道问题
- App端需要集成FCM的SDK
- 后端需要集成FCM服务端SDK
- Push Token上报调整
App接入的Push SDK需要在手机设备上生成设备的唯一标识码,不同厂商的叫法不一样。我们可以通过这个设备唯一码给手机终端进行消息推送。业务方推送消息时,一般根据业务的ID来推送,push后台服务会维护push cid与业务id的关联关系。
- App端解决方案
App端需要集成Google FCM的SDK,包括 ios,android版本。并且需要根据业务要求,实现通知栏弹出消息,或者程序内部处理通知。
- 服务端决方案
从Push服务的总体构架上我们可以看出,后端push服务设计之初,已经考虑了国内众多android厂商的push服务以及Apple APNs。我们当前就已经接入了华为、小米、OPPO、VIVO、APNs等厂商推送。因此,我们在LLM场景,支持Google FCM服务,也只需要在当前系统上进行一定的扩展就可以实现,具体以下两部分工作:
- Token上报(内部也叫cid上报)
Token上报协议如下(部分字段未显示):
请求参数:
参数名 | 必选 | 类型 | 说明 |
os | 是 | int | 操作系统, 1 安卓 2 IOS |
pushCid | 否 | string | 各个厂商推送渠道的推送token |
cidType | 是 | int | 厂商token类型 3-apple 4-xiaomi 5-huawei 6-meizu 7-oppo 8-vivo 10-hw快应用 13-fcm通道上报的,为国际化项目增加部分 |
- Push服务
Push服务类结构图如下:

从上图可以看出,push服务后端,目前已经实现了多个通道支持,本次主要实现FCM通道,通过FcmPushImpl类来完成对应功能,主要实现方法如下:

3.5 邮件服务国际化
邮件服务使用统一的SMTP协议,实现与第三方邮件服务商的对接。实现邮件发送。由于邮件服务使用的SMTP是全球邮件的事实标准,因此,邮件服务在国际化接口及邮件发送服务不用做任何调整,就可以满足需求。
但邮件服务在国际化情况下,也存在一个问题:那就是邮件大附件的发送。目前在HLL场景下,我们是使用阿里去提供的OSS存储,来实现附件的临时存储。考虑到海外网络问题,我们系统是部署在AWS,如果此时附件使用OSS存储,则会导致邮件附件存储非常慢,因此,我们需要解决海外邮件附件存储问题。
因此,我们在LLM下,邮件附件存储服务通过接入S3的存储解决跨网络的附件存储问题。
4. 总结
本次地图和消息的国际化,处于比较初始阶段,以满足功能业务功能需求为主;基于HLL原有系统架构基础上,快速的扩展接入第三方应用通道,完成国际化系统的落地。
后续,我们会基于同一套系统,针对全球化的业务场景,不断的迭代和优化。从通道冗余备份、熔断降级、数据深度挖掘融合、数据链路追踪等多方面提升消息和地图的国际化支撑能力。
作者介绍:
王少平(wiley.wang),货拉拉研发经理