电商平台移动网关实践:月活数千万App如何摆脱客户端拼装
我在一家大型电商平台工作了六年,那是一家月活跃用户达数千万、拥有数百个后端服务,而且必须依靠一款 Android 和 iOS App 将所有功能整合在一起的平台。在最初的几年里,我是一名 Android 开发人员,负责开发用户能够看到和操作的功能。后来,我转到了另一边。我不再编写移动端代码,转而开始构建团队中每位移动开
我在一家大型电商平台待了六年。那家平台的月活跃用户有数千万,后端服务数百个,所有功能最终都靠一款 Android 和 iOS App 整合到一起。头几年我是 Android 开发,做的是用户能看见、能上手的功能。后来我换到了另一边:不再写移动端代码,转而搭建团队里每个移动开发都离不开的后端基础设施,也就是夹在移动应用和其余所有组件中间的那层网关。
这次转身把我对移动开发的看法整个翻了过来。聊移动应用,多数人盯的是界面、框架和设计系统。可一款应用跑得顺不顺、能不能高频发版、撑到大规模之后稳不稳,起决定作用的并不是应用本身,而是它背后那套支撑体系。下面就说这一层:它干什么、为什么要有、怎么设计才算对,以及把一个流量正猛的在跑产品往这层上搬、手里又没有安全网时,会发生什么。
智能应用存在的问题
移动应用跑在用户的设备上。这话听着像废话,但真到设计系统的时候,它的影响很容易被看轻。设备电池有限,内存有限,网络还经常不稳、慢,或者又慢又不稳。你往应用里塞的每一段逻辑,都在抢这些资源。应用小的时候还扛得住。可一旦它服务数百万用户、还要跟数百个由不同团队维护的后端服务打交道,客户端逻辑那些隐性开销就开始堆了:应用变慢,崩溃率往上走,耗电增加。而且每次修复都得发新版本,意味着要过应用商店审核、等推送,还得指望用户肯更新。
为了说得更直观,下面是网关建起来之前我们的架构:
那时候移动应用认识所有下游服务。每显示一屏就要发好几次网络请求,响应结果在客户端合并,功能开关在本地判断,每个服务的错误模式也各处理各的。光一个产品详情页就要调五到六个服务,应用里还塞着把这些响应拼成可渲染视图的逻辑。
代价很直观。在占我们用户基数约 60% 的中端 Android 设备上,产品详情页的渲染时间是 2.8 秒。其中约 1.1 秒是网络时间,包含多次顺序和并行调用;0.9 秒花在客户端的数据转换上。应用的冷启动时间已经悄悄超过 4 秒。APK 大小每季度涨约 2 MB,部分原因就是客户端服务集成代码越积越多。一款移动应用如果主要扮演远程服务客户端的角色,就该尽量少揽后端协调的活。这类逻辑在客户端占得越重,应用越容易出故障。
网关模式:一个接口,数以百计的服务
于是我们搭了一个代理层,位置在所有移动客户端和平台后端系统之间。它主要干两件事。第一件,替移动开发人员把数据预处理掉,让他们不用懂底下的复杂机制就能直接用。大型电商平台的后端系统又杂又多:库存、定价引擎、配送物流、推荐服务、支付处理等等。每个系统归单独的团队管,有时甚至就是独立的业务单元,各有各的代码库和部署管道。一个做产品详情页的移动开发,本来不需要知道怎么调五项不同的服务、怎么合并它们的响应、怎么处理局部失败、怎么把结果转成 UI 能渲染的形式。网关把这些活接了过去。
第二件,网关把原本该待在应用里的逻辑吸了过来。数据转换、功能标志评估、响应聚合,还有下游故障的错误处理,全都挪到了服务器端。
现在应用每显示一屏只发一次请求。网关判断该调哪些后端服务,按最优顺序去调(能并行就并行),把响应组合起来,套上功能开关和实验变体,返回一份预先格式化好的载荷,应用几乎不用处理就能渲染出来。
迁移完成后的影响评估:
网关模式的权衡
这套架构不是白得的。引入网关就多了一个关键依赖,多了一跳网络(通常在同一数据中心内为 5–15 毫秒),还得有个团队专门管它的运维。先说延迟。网关确实加了一跳,但它把多次客户端到服务端的顺序调用消掉了,换成一次客户端到网关的调用,后面跟着并行进行的服务器到服务器调用。服务器之间的调用走低延迟的数据中心链路,不走移动网络,所以净延迟几乎总是更低。就我们的情况而言,省掉 4–6 次移动网络往返带来的收益,远大于多出来的这一跳网关。
再说运维复杂性。现在你手里有了一项要扛全部移动流量的服务。这要求从第一天起就在冗余、自动扩展和断路器上投入。我们把网关部署在多个可用区,目标可用性定为 99.99%。这个要求比任何单个下游服务都更严,因为网关一挂,应用就是彻底中断。
还有耦合风险。不留神的话,网关很容易长成一个单体应用。为了避开这个问题,我们尽量让网关靠配置而不是靠堆代码。界面定义写在配置文件里声明,不写进应用程序代码。加新界面或改现有界面都不用部署新的网关代码,只要更新配置文件,指定调哪些服务、响应怎么组合、回退行为怎么走。
团队边界也得算进去。网关团队会变成每个移动端功能的依赖方。我们的缓解办法是自助服务:移动端团队自己定义界面配置,网关负责执行。网关团队管运行时环境和框架,产品团队管各自的界面定义。
我进这一行的路不太寻常。起点是 Android 开发,花了好几年给这款应用做功能。之后转到 Android 基础设施,优化其他移动开发在用的内部工具。这段经历让我看清了开发中的痛点:开发人员在哪里卡住,应用在哪些地方背得太重,客户端和服务器之间的边界划得合不合理。等到我终于转去做后端、设计网关时,这些移动端的经验全被我带了过去。没先在移动端攒够实战经验,很难做出好的移动后端抽象层。
后端驱动的 UI:把开发速度从应用商店手里拿回来
大部分逻辑搬进网关之后,一件关键的变化出现了:产品要改点什么,多数情况下已经不必再发一版移动应用。
在移动产品开发里,发布周期这个瓶颈被低估得厉害。要出新版本,先得走 Google Play 或 App Store 的审核;推送还得花上几天甚至几周慢慢铺开;就算铺完了,仍有相当比例的用户还在用旧版本,因为大家并不总会及时升级。研究反复说明一件事:用户不喜欢频繁更新。每次更新都占带宽、耗时间,更新太密还会消磨用户信任。
对企业来说,这就成了一个难受的矛盾。产品团队想的是快速迭代——做实验、调布局、照数据优化流程。可应用代码里动任何一处都得发版,发版又慢。一个两天开发完的功能,要两周才能让大多数人用上。
网关架构把应用的角色从决策层改成了渲染层,问题就解开了。服务器说清楚应用该显示什么——渲染哪些组件、顺序如何、用哪些数据——应用照着显示就行。改用户看到的内容,只需更新网关配置,不用碰应用代码,也不用穿过数据存储层,用户那边毫无察觉。
想让产品行为变一变却又不发新版,那管这件事的业务逻辑就得放在服务器端,不能留在客户端。
引入网关前后的发布速度对比:
逻辑一旦搬到后端,应用就成了一张画布,画什么由你决定。
后端驱动的 UI:几种模式怎么选
后端驱动的 UI 实现路径不止一条,选哪条要看自己的限制条件。
纯服务器驱动渲染(SDUI)。服务器把整棵组件树发过来:渲染哪些组件、各自什么属性、怎么排列。应用只当通用渲染器。这种做法服务器端控制力最强,前提是客户端组件库足够成熟,版本管理也得细致——旧版应用碰到不认识的组件类型,要能优雅处理。
混合方案。数据和布局配置由服务器管(显示哪些部分、什么顺序、用什么数据),单个组件怎么渲染仍归应用。这种方案便于分阶段落地,对 UI 组件定制复杂、已经成熟的应用效果更好。
纯数据网关。服务器只做数据聚合和整形,布局与渲染决策全留给应用。性能上的好处能拿到(网络调用更少、数据包更小),又不会因为服务器接管布局而拖慢产品迭代。
我们选的是混合方案。纯 SDUI 灵活性最大,但要有通用渲染系统、严格的模式版本控制,还要跨应用版本做健壮的兼容处理。纯数据网关简单些,可布局和实验变更带来的发布瓶颈它解决不了。混合模型是个折中:界面结构归网关管——版块、排序、可见性规则、实验变体——渲染控制权留在应用手里。代价是多了一份服务器-客户端契约,得给它做版本管理、验证,还要保证对旧版应用向后兼容。新增版块类型仍然要发应用更新;服务器端能做的,是对已有版块重新排序、隐藏或者彻底重新配置。
生产系统迁移,没有彩排
这个网关是从零设计的。但真正难的不是设计,是迁移。
平台是个跑着的高流量应用,服务数千万用户。原有架构里,移动应用直接跟各种后端服务通信,大部分逻辑在客户端跑。我们要把所有流量引到新网关,在服务器端重新实现数据准备逻辑,还得让用户完全感觉不到变化。没有任何预发布环境能真实复现这种规模的生产流量——数百万真实用户、真实设备、真实网络,模拟不出来。能测的只有力所能及的部分,把可观测性建起来,然后直接上生产。
迁移按阶段增量推进。界面和流程一个一个来,走的是下面这套流程:
-
影子模式。网关和原有的直接调用并行跑,两条路径都执行,应用只用旧路径的响应。我们在服务器端比对两组响应,把差异记下来。
-
金丝雀发布。差异率降到 0.1% 以下后,把 1% 的流量切到新路径。页面加载时间、错误率、下游服务调用模式都在监控范围内,至少盯 48 小时。
-
分阶段部署。每个界面在 1 至 2 周内把流量依次提到 5%、25%、50% 和 100%,并配上自动回滚触发。某个界面的错误率比基准值高出 0.5 个百分点以上,流量就自动退回旧路径。
-
移除旧路径。以 100% 流量跑满四周、指标保持稳定之后,旧的客户端集成代码被删掉。
最麻烦的是那些只有系统跑到大规模才露头的“长尾”边际情况。比如某个服务在高负载下返回的数据略有出入;某个超时在第 95 百分位还过得去,到第 99.9 百分位就成了灾难;再比如下游团队没打招呼就部署了变更,你的网关突然给一部分用户发出了格式错误的数据。这些问题,单元测试做得再多也准备不好,只能靠性能监控、渐进式发布,外加保持适度的警惕。
整个迁移是在应用运行期间完成的,架构整体换了过去。生产环境的条件没法事先完全复现,我们走的是精心规划的分阶段迁移。
保证系统在大规模部署下依然可靠
迁移收尾之后,我把注意力放到了系统韧性上。网关横在整个移动用户群和所有后端服务之间,按定义它就是个单点故障。它一挂,应用就跑不起来。
要让它扛得住这个角色,就得持续做有针对性的投入。
基本套路大家都清楚:冗余、断路器、超时、带退避的重试,以及下游服务出故障时的优雅降级。我觉得更有意思的,是那些不那么显眼的难题。
在产品层面把“功能受限”的状态定清楚。推荐服务不可用时,产品页面该显示一块空白的推荐位、一个缓存版本,还是干脆不显示推荐位?技术上看,正确答案取决于具体产品场景。推荐位空着,用户可能以为是程序出了错。推荐位内容过时,可能推出不相关的商品。整个区块拿掉或许最干净,但产品团队可能不答应,因为这会拉低用户参与度。这类决定不能只由基础设施团队拍板,需要工程、产品和设计三方紧密配合,还要以可配置的方式明确写下来,让网关能自动执行。
我们做了一份依赖关键度矩阵,把每个界面里的下游服务调用分成“必需”“推荐”“可选”三类:
-
必需:缺了这项数据,界面就渲染不出来。调用失败时,网关向应用返回错误(比如产品详情页里的价格和库存信息)。
-
推荐:缺了这项数据,界面功能会受限,但有备用方案。网关会给出缓存数据或简化响应(比如个性化推荐回退成基于流行度的推荐)。
-
可选:没有这项数据,界面照样完整显示。调用失败就省掉这一块(比如“顾客还买了”轮播图)。
这份矩阵由工程团队和产品团队共同维护。每定义一个新界面,都得把它填好。这样出了事就不会含糊,值班工程师可以专心恢复服务,不用在凌晨 3 点去做产品层面的判断。
摸清实际容量。负载测试有用,但合成负载没法完全复现生产环境的流量模式。真实用户会围着促销活动、限时抢购和不同时段的流量高峰聚集,形成你事先想不到的热点。我们的做法是广泛部署监控工具,把容量规划当成一项持续性的工作。每个界面、每个下游服务、每个可用区,分别追踪 p50、p95 和 p99 延迟指标,每周审查一次。
我们想要的是一个在生产环境里“平淡无奇”的系统。如果系统跑起来你还得为它操心,那就说明出问题了。
这项工作的前提
在这个层级上搭建和维护移动基础设施,对网关工程师的技能有特定要求。你得对移动端的限制理解得足够深,才能替那些永远看不到你代码的移动开发人员做出合理取舍。你得对后端系统理解得足够深,才能预判故障、管理依赖关系,并为几十个服务推导出合理的延迟预算。你得有足够的产品意识,才能判断哪些性能退化模式可以接受。此外,你还得能跨组织边界沟通,因为网关依赖的服务归各个团队管,而这些团队有各自的优先级、发布计划和对“正常运行”的定义。
实操要点
如果你正在开发或运营一款已经超出原始架构承载能力的移动应用,按我的经验,建议如下:
-
先审核业务逻辑的分布。理清哪些决策在应用端做,哪些在服务器端做。如果应用正在做数据聚合、功能开关评估或复杂的条件渲染,那这些都属于可以提取到网关层的候选场景。
-
从用户流失率最高的界面入手。那些因产品实验而更新最频繁的界面,正是后端驱动用户界面见效最快的地方。优先迁移这些界面。
-
做任何迁移之前,先投资可观测性。你需要按界面、按流程收集延迟、错误率和有效载荷大小这些指标。没有这些指标,迁移过程就跟“盲飞”一样。
-
通过协作把降级行为明确下来。为产品和设计构建依赖关键度矩阵,把每个下游调用归入“必需”“推荐”或“可选”。别把这事留给凌晨 3 点的值班人员去判断。
-
SDUI 的深度要审慎选择。纯服务器驱动渲染、混合模式或纯数据模式——每种方案的采用成本和收益都不一样。应该结合团队成熟度和产品迭代节奏来挑合适的方法。
-
把网关当成一款产品,而不是公用设施。它有用户(你的移动端开发人员),有需求(可靠性、性能、开发体验),需要长期投入。如果给它的人财物不够,代价会体现在应用质量上。
-
配上既懂前端又懂后端的人。最优秀的网关工程师,是那些既做过移动端开发又参与过后端开发的人,因为他们能全面考量边界两侧的限制条件。找不到这种双重背景的人才,就投资于跨领域培训。
小结
移动应用是用户看得见的那层。但它背后的基础设施会影响应用的加载速度、崩溃频率、产品团队的迭代速度,以及应用出故障时处理得平不平稳。这一层用户看不见,甚至对更广泛的工程团队来说往往也是隐形的。怎么把它搭好,是移动产品开发里技术要求最高、对业务也最重要的工作之一。
按我的经验,这也是最让人满足的工作之一。你构建的东西,是团队里每位工程师都依赖的基础。它运行正常时,大家效率都高;它出故障时,每个人都能切身感受到。这种责任感是一种殊荣,能让你始终保持诚实。