1. 海外外卖平台和国内平台的根本差异:先想清楚再动手
这几年出海项目特别多,外卖、跑腿、生鲜到家的需求在东南亚、中东、拉美这些市场都涨得很快。你可能第一反应是"外卖平台嘛,国内都卷出花了,照搬一套过来就行",但实际上这个想法会浪费你至少三个月。海外版外卖平台的架构,和国内主流方案从根上就不是一回事。
先说一个最直观的例子:国内外卖依赖微信和支付宝的支付体系,几乎不需要你操心支付通道对接;而海外市场支付方式极度碎片化,信用卡、PayPal、本地电子钱包、货到付款不一样。再比如地图,国内用高德或腾讯地图,海外则主要依赖Google Maps或者当地的地图服务商,这直接决定了配送距离计算、ETA预估这些核心功能的实现方式。再比如短信验证码,国内一套短信通道价格低、到达率高,海外则要根据目标国家的运营商环境选择合适的通道服务商。这些都是你从第一天设计架构时就必须考虑进去的变量。
所以这篇攻略,我不会上来就给你画一张大而全的系统架构图然后让你照着抄,而是先掰开揉碎讲讲海外外卖平台的技术挑战在哪里,再逐一给出落地的架构决策和实操细节。内容适合准备出海的创业团队、做海外本地生活服务的技术负责人,以及想了解国际化业务架构设计的后端工程师。我会把接口设计、数据流、部署策略、合规要求、踩坑经验全部串起来,尽量让你看完之后能直接拿去评审和排期。
1.1 市场差异决定了架构的开放程度
做海外外卖,第一个要扭转的思路是:系统必须默认以"开放集成"为前提,而不是像国内那样可以闭门自洽。
什么叫开放集成?就是你从设计第一天就要假设:支付可能会接四五家渠道、地图可能在不同国家用不同厂商、短信通知在不同的区域可能由不同的供应商提供。你不能把某一家服务商硬编码到业务代码里,否则每进一个新市场就要改一遍代码甚至改一遍架构。我的建议是,在系统里从最开始就抽象出几个核心的"外部服务适配层",包括支付网关适配层、地图与地理编码适配层、消息通知适配层。每一层都定义好自己的接口规范,然后再去对接具体的服务商。
这个设计带来的直接好处是,当一个市场的支付到达率不理想时,你可以快速切换备选渠道而不用改核心订单逻辑。我在实际项目中见过太多团队把支付回调地址写死在订单服务里,导致后期每接一个本地钱包都要动订单表结构,非常痛苦。
1.2 用户端、商户端、骑手端、运营后台:四端不是四个项目
外卖平台的业务角色比较清晰:C端用户、B端商户、骑手、平台运营。很多团队在立项时会直接拆成四个独立系统来做,结果做了一半发现大量的业务逻辑重复,比如订单状态流转在四个端各写了一份,最后对不上账。
我的经验是,四个端共享同一套核心业务API,但每个端通过自己的聚合接口拿到最适合自己展示的数据。也就是说,底层只有一套订单服务、一套商户服务、一套骑手服务,而用户端App、商户端App、骑手端App、运营后台Web都只是这同一套核心服务的外层表现。
这里要重点设计的是数据权限模型。运营后台能看全量订单,商户只能看自己店铺的订单,骑手只能看派给自己的订单,用户只能看自己下的订单。如果做不好这个数据权限层,后面一定会出现越权访问的事故。建议在网关层面就统一做身份认证和资源归属校验,不要在每一个服务里各自实现权限判断,否则后期排查安全漏洞会非常麻烦。
2. 核心业务链路拆解:从用户下单到骑手送达的架构路径
我习惯在架构设计前先把一条完整的业务链路走一遍,标出所有涉及的服务、数据存储和外部依赖。这一步听起来简单,但很多团队跳过了它直接设计表结构和接口,导致后期出现"链路通但数据不对"的诡异Bug。下面是海外外卖平台一条最典型的下单配送链路,你可以照着这个路径去梳理自己的系统。
2.1 下单链路中的每个环节都依赖什么
一个完整的流程是:用户打开App搜索附近商家、浏览菜单、加购物车、创建订单、选择支付方式并完成支付、平台通知商家接单、商家确认并开始制作、平台调度骑手取餐、骑手取餐后送达、用户确认收货并评价。
在这条链路上,涉及的支撑系统包括:商户检索服务(LBS + 关键词搜索)、菜品菜单服务、购物车服务、订单服务、支付服务、通知服务、配送调度服务、骑手定位服务。这里最容易出错的是订单服务和支付服务之间的状态同步,涉及分布式事务问题。先说说我的答案:不要在分布式环境下强依赖数据库事务去保证多个服务的数据一致,而是用本地消息表 + 消息队列的最终一致性方案。订单服务先落库订单状态为"待支付",再发一条支付请求消息给支付服务,支付服务收到后创建支付单并调用第三方支付渠道。用户支付成功后,支付渠道回调支付服务,支付服务更新支付单状态并向订单服务发送支付成功消息。订单服务收到消息后把订单状态推进到"已支付"。
最终一致性方案比强一致方案好在哪?简单讲两点:一是用户体验上有更强的容错,用户可以先创建订单再慢慢支付,超时订单由定时任务自动取消;二是系统解耦,订单服务和支付服务不会因为彼此短暂的不可用而直接导致下单链路崩溃。
2.2 搜索与推荐:海外市场对"附近"的定义不太一样
海外外卖平台的搜索模块,技术上一般用Elasticsearch或者类型类似的开源检索引擎来实现,加上Redis作为缓存层。有一个容易忽略的细节:不同国家/地区对"附近"的感知和定义差别很大。在人口密集区域,用户可能希望看到1公里以内的商户;在郊区或地广人稀的区域,用户愿意接受5公里甚至更远的配送距离。所以商户检索服务需要根据区域密度动态调整搜索半径,而不是写死一个固定值。
菜品数据通常包含名称、描述、图片、价格、标签、起售时间等字段。多语言环境下,还要考虑不同语言的搜索支持。我的建议是提前规划好国际化字段结构,比如菜名在ES里同时存多个语言版本的分词结果,搜索时按照用户当前语言优先匹配,然后做评分降级。如果你一开始只支持一种语言,后面要扩展时再改索引结构,通常要重建整个索引,代价不小。
2.3 购物车与会话设计:不能依赖数据库行锁
购物车的合理做法是使用Redis来维护,因为购物车是高频读写、低持久化要求的数据。以用户ID作为key,菜品ID和数量作为field-value,这样无论是增删改查都非常高效。要注意的是,加入购物车时的库存校验不能实时依赖商户系统的库存实时数据,否则高并发下会对商户服务造成很大压力。折中方案是,在加购时只做一次"宽松校验"(即菜品是否在售),待到真正提交订单时再对库存、价格做一次"严格校验"。
价格也是一个需要小心的地方。海外外卖平台常常涉及折扣、满减、平台补贴、商家优惠券叠加,一旦价格计算逻辑和订单服务耦合过深,后期运营配置复杂活动时会非常痛苦。我建议把价格计算单独拆成一个计价服务,输入购物车快照和优惠信息,输出订单金额明细。注意,订单创建后所有价格都必须以快照形式入库,后续如果商家改价或者活动过期,已创建的订单不受影响。
3. 高并发与一致性:订单服务和支付服务的关键设计
外卖平台的流量特征是典型的"脉冲式":午高峰和晚高峰的订单量可能是平时的数倍,而在非饭点时间流量急剧下降。架构上为了应对这种脉冲流量,要注意资源弹性伸缩和削峰填谷能力的建设。
3.1 用消息队列削峰,还是靠限流硬扛
我的经验是把下单请求分成"读多写少"和"写多读少"两类。商户列表、菜品详情这类读请求,通过Redis和本地缓存尽量扛住;真正的下单写请求,则通过网关层限流 + 消息队列异步化来处理。
举个例子,假设日常每秒有200个订单请求,高峰期峰值到每秒1500个订单请求。如果订单服务直接用同步接口接住全部流量,数据库连接池和事务并发会立刻被打满,接口响应时间飙升,甚至导致雪崩。更务实的做法是:网关层对同一个用户的重复下单请求做拦截(防止手滑或恶意刷单),然后订单创建请求进入消息队列,订单服务按自己的消费速度处理。用户端看到的是"订单创建中"的过渡状态,当订单服务处理完后通过WebSocket或推送通知告诉用户订单创建结果。
这里要特别注意消息的幂等性。因为消息队列默认投递语义可能是一条消息被重复消费,所以订单服务在消费"创建订单"消息时,必须用业务唯一键(比如用户生成的前端请求ID)去重。我在实际项目中踩过这个坑:漏掉了幂等校验,结果高并发下用户瞬间下单量翻倍,最后靠清数据挽回了事故,但也折腾了很久。
3.2 支付回调的幂等与对账机制
支付模块最核心的是回调处理和每日对账。先说回调:第三方支付渠道通常通过Webhook通知你的后端"这笔支付成功了",但Webhook可能重复发送、可能延迟、甚至可能丢失。因此支付服务处理回调的逻辑必须是幂等的,即同一笔支付单不管你重试多少次回调,最终结果是一致的。
我的设计是:支付单表以支付单号作为唯一索引,回调处理时先查询支付单当前状态,如果已经是"成功"状态,直接返回成功应答,不再重复更新。如果是"处理中"状态,则更新为成功并触发后续业务消息。支付单状态流转必须用乐观锁或状态机约束,避免并发回调导致状态错乱。
每日对账是支付系统最后一道防线。你需要一个定时任务,每天从支付渠道拉取前一天的交易账单,和本地支付单表做比对,找出"本地已扣款但渠道无记录"、"渠道有记录但本地未收到回调"等异常单,然后进入人工处理队列。这个对账模块虽然不复杂,但一定得单独建,别想着省事不做,不然月底财务一哭你就要加班。
3.3 定价系统中的"钱"为什么不能直接存浮点数
涉及钱的字段必须用整数类型来存储。这是个基础常识,但我在评审代码时仍然见过不少用Double或者Float存金额的写法,这在汇率变动频繁、优惠计算多层叠加的海外外卖场景上会产生毛刺误差。建议用最小货币单位(分)存整数,展示层再根据货币类型做金额换算和格式化。多币种体系下,订单金额要同时记录本币金额和平台结算币种金额,并保留结算汇率快照,方便后续财务结算时追溯。
配送费的计算通常也放在计价服务里。配送费一般等于基础配送费加上距离加价、时段加价、天气加价等。距离数据来源于地图服务返回的实际骑行或驾车距离,而不是简单地用两点直线距离,否则骑手会因为你少给钱而拒单,用户会因为费用过高而流失。
4. 配送调度与实时定位:海外外卖的"下半场"难点
如果说订单和支付是外卖平台的"上半场",那么配送调度就是"下半场",也是真正拉开技术差距的地方。海外市场的道路状况、交通工具类型、城市密度差异巨大,直接照搬国内的骑手调度逻辑往往行不通。
4.1 订单派给谁:抢单模式还是指派模式
国内美团外卖跑通的主要是系统指派模式,系统根据骑手位置、负载、方向预测给性价比最高的骑手派单。但在很多海外市场,骑手群体相对分散,抢单模式会更符合当地的骑手习惯。所以调度服务的设计要同时支持抢单和指派两种模式,并且能够在后端配置中心里随时切换。
采用抢单模式时,系统向符合条件的骑手批量推送新订单通知,骑手在客户端抢单。这个模式的技术难点在于"批量推送"的实时性和"多人抢一单"的冲突控制。我建议用Redis的原子操作(比如SETNX或Lua脚本)来保证一个订单只能被一个骑手成功抢到:骑手点击抢单时,后端尝试写入订单与骑手的绑定关系,失败的骑手直接返回"手慢了"。
指派模式下,调度服务的核心是一个评分函数:综合考虑骑手当前是否顺路、距离取餐点的距离、预计完成上一单的时间、骑手当前的负载量。评分函数不是一个静态公式,需要不断根据真实配送数据进行调优。建议一开始用规则加简单加权评分,不要一上来就上机器学习模型,否则你根本没有足够的训练数据来证明模型比人工规则更可靠。
4.2 实时位置、ETA预估和地图偏移的坑
骑手的位置实时更新一般通过App上报GPS坐标,上报频率通常在5-15秒一次。这里有两个实际问题:一是高频上报会消耗手机电量并产生大量后端写入流量,所以务必在客户端做省电策略,比如在骑行状态才开始高频上传、静止状态下降低频率;二是不要相信原始GPS坐标,要对坐标做合理性校验,比如过滤掉漂移巨大的点。
ETA预估(预计送达时间)不要直接用地图API给的单次行驶时长,而是要加入商家出餐时长的预估。靠谱的做法是:ETA = 商家平均出餐时间 + 骑手从当前位置到商家的预计时间 + 骑手从商家到用户地址的预计时间 + 一些缓冲时间。出餐时间可以用历史数据滚动计算,按时间段和菜品数量做动态调整。
还有个小坑:海外地图厂商的坐标系统和国内不同,如果你的App同时支持国内地图和海外地图,后端最好统一用一种经纬度标准存储坐标,在地图渲染层再做坐标转换。否则会出现用户实际位置和商户距离显示不一致、骑手导航偏航等一连串体验问题。
4.3 离线容灾:骑手App断网了怎么办
海外很多区域的移动网络基础设施不如国内稳定,尤其在南亚、拉美的非核心城区,骑手在配送途中经常出现短暂断网。骑手App必须支持本地缓存关键任务状态:当前正在配送的订单信息、取餐和送达的扫单动作在本地暂存,等网络恢复后再批量上报。后端要能够容忍这些延迟上报的坐标点,并据此修正骑手的轨迹和位置展示。
这些离线逻辑必须在设计阶段就做好,而不是等上线后收到大量投诉才补。我在印度市场那边就吃过这个亏:骑手在偏远区域断网十分钟,用户看到的骑手位置一直停留在原地,客服被问爆了。
5. 全球化部署与基础设施:不要一开始就做全球多活
面对海外市场,部署策略上最容易犯的错误是:一开始就想做一个"全球多活"的大集群,结果成本和复杂度都控制不住。合理的路径是,先选择一个核心区域集中部署,验证业务模型跑通后,再逐步向其他区域扩展。
5.1 从单区域集中部署起步
以东南亚市场为例,初期完全可以把应用和数据都部署在新加坡或对应区域的主流云平台上,统一服务整个区域。这个阶段的核心目标是快速验证业务、控制成本,不需要考虑跨区域数据同步。一个订单从泰国到新加坡数据中心的访问延迟通常在50到100毫秒左右,对于外卖场景完全可接受。
等到业务增长到一定规模,或者某个国家的用户体量持续走高,再在那个国家或就近区域部署一套独立的实例。这时候的数据方案一般是"各区域独立数据库 + 平台级数据汇总",而不是追求跨区域实时的强一致。理由很实际:外卖平台的数据天然有很强的地域属性,用户不太可能频繁跨区域下单,平台级汇总报表也不需要实时精确到秒级。
5.2 CDN、对象存储与静态资源策略
App内的图片资源、商户菜单图片、活动页面等,都需要通过CDN分发加速。在海外市场,CDN节点的覆盖情况直接影响用户打开App和浏览菜品图片的体验。建议选型时重点考察目标市场的节点覆盖情况,而不是只看它在国内或全球的宣传声势。一些本地化的CDN服务商在特定国家和地区的加速效果,往往比全球性大厂更好,价格也更有优势。
菜单图片这类静态资源,建议在上传时做多尺寸压缩和WebP格式转换,CDN回源时根据客户端参数返回合适尺寸。这个方法能显著减少App加载图片的流量消耗,尤其在很多海外用户的套餐流量并不宽裕的情况下,体验差异会非常明显。
5.3 容器化、编排与持续交付
海外外卖平台后端推荐用Kubernetes来进行部署管理。原因很直接:外卖业务有明显的波峰波谷,Kubernetes配合HPA(水平Pod自动伸缩)可以实现饭点高峰自动扩容、闲时自动缩容,把云成本压下来。
一份合理的部署单元划分可以是:每个业务服务一个Deployment,接入层用Ingress统一暴露;数据库、Redis、消息队列等有状态组件,初期建议使用云厂商提供的托管服务,不要自己用Kubernetes去编排数据库。理由也简单,自运维数据库在高并发场景下的代价超出了大多数创业团队的承受能力,云数据库自带的高可用、自动备份和监控能力,能让你少踩非常多运维层面的坑。
可持续交付层面,比较常用的实践是开发分支和测试分支分开,通过CI流水线自动完成代码检查、镜像构建、安全扫描、部署到测试环境,打过标签的版本再部署到生产环境。这里有个细节:海外部署涉及多个时区的发布窗口,发布生产环境的时间应尽量错开目标市场的高峰时段,避免上线动作叠加高峰期流量,增加故障定位的难度。
6. 合规、数据安全与隐私:出海和国内最大的隐形差距
合规这种事,在国内做外卖可能被很多人忽略,但出海做外卖平台,这个问题从你注册公司、申请资质、准备上架应用商店的那一刻就开始影响你了。架构设计时必须给合规和隐私保护预留空间,否则后面改起来是伤筋动骨的。
6.1 用户数据隐私保护的核心要求
不同目标市场对用户数据隐私的要求不同,但方向基本一致:收集用户数据必须明确告知用途,存储和传输必须加密,用户有权删除自己的数据。这些要求落到技术层面,可以拆成几个具体动作:所有涉及用户个人信息的接口必须走HTTPS;用户手机号、邮箱等敏感字段在数据库中必须加密存储,至少也要做脱敏处理;用户注销账号时,系统需要提供完整的删除链路,包括核心数据库、缓存、日志和备份中的个人数据。这个删除链路必须在架构规划时就设计好,而不是等要求来了再临时做。
另外,分析追踪类的SDK要克制。不少出海团队习惯性在App里集成大量第三方统计SDK,但如果SDK采集了过多用户行为数据而没有在隐私政策里说明,应用商店审核时很可能直接被拒。架构上建议做一个统一的数据采集网关,让所有行为数据流经同一个出口,方便控制采集范围和隐私合规策略。
6.2 支付合规与数据安全管理
支付合规也是架构上需要提前考虑的硬约束。如果你直接处理信用卡号等敏感支付信息,就必须符合支付卡行业的数据安全标准,包括但不限于:存储的卡号需要加密、网络传输需要加密、系统需要定期安全扫描。实践上,绝大多数外卖平台不会自建支付处理能力,而是使用第三方支付服务商的托管方案:用户跳转到支付服务商的支付页面,或通过服务商提供的SDK完成支付,你的服务器完全不接触原始卡号。这样既降低了合规难度,也减少了自身系统的安全暴露面。
建议在对接支付时优先选择Stripe、Adyen以及在目标市场本地市场占有率高的支付服务商。每个市场的用户习惯不同,比如有的市场用户偏爱银行卡直接支付,有的市场用户则更习惯钱包类App,所以多条支付通道并行是常态。你的支付网关适配层要能够统一管理这些通道,支持按市场、按金额、按支付方式动态路由。
6.3 日志、审计与可追溯性
合规还有一个不太起眼但重要的需求:审计日志。运营后台的敏感操作、用户数据的访问、支付订单的修改,都必须记录操作者和操作内容。这个审计日志平时没什么存在感,但在遇到合规检查、财务审计、安全事件溯源时,它是唯一能靠得住的证据链。设计上建议所有内部运营后台的管理操作,都通过一个统一的操作日志中间件记录,不要依赖业务代码里逐个打点,因为业务代码里漏记是大概率事件。
7. 我踩过的坑和现在会坚持的几条经验
最后分享几个我实际踩出来的经验,不一定多高级,但每一条都是用事故换来的。
第一,任何时候都不要让外部服务直接修改你的核心订单状态。我们早期为了图省事,让支付渠道的回调直接更新订单状态,结果有一次支付渠道的测试Webhook打到生产环境,直接把几个订单状态打乱了,出现用户没支付订单却显示已支付的严重事故。之后把支付服务对订单状态的修改全部改成了事件消息驱动,才彻底解决问题。
第二,所有与外部渠道相关的调用都要有超时和熔断机制。海外第三方服务的稳定性参差不齐,尤其是本地化支付通道和短信通道,偶尔会出现长时间无响应的情况。如果你不做超时控制和之后的熔断降级,一个上游服务卡死可能直接拖垮整个下单链路。这里具体操作上,给每一条外部调用都设置合理的超时时间(通常1-3秒),并且用信号量或线程池隔离避免依赖耗尽。
第三,数据备份和恢复演练要做到常态。海外市场的合规要求里经常有数据留存期限的要求,而技术上最怕的是数据丢失。我们每个月至少做一次数据库恢复演练,确保备份文件真的能在规定时间内恢复出可用的库。很多团队备份是配置了,但从来没恢复过,到了火灾现场才发现灭火器是坏的。
第四,不要迷信"微服务"。外卖平台的核心链路适合拆分成几个服务,但不要为了微服务而微服务。很多业务逻辑耦合度高的模块,硬拆开只会增加分布式通信成本和排查问题的难度。一个几十人的技术团队,模块化单体或粗粒度服务往往比几十个微服务更高效。
海外外卖平台的搭建,说到底不是一蹴而就的事。架构设计只是第一步,落地过程中还要不断根据市场反馈调整系统策略。但只要把底层的开放性、一致性、容灾能力、合规边界这些骨架打扎实,后续的扩展和优化就都有章可循了。希望这篇复盘能给你一些参考,也欢迎有类似出海经历的朋友一起交流踩坑心得。