
我在一家大型电商平台工作了六年,那是一家月活跃用户达数千万、拥有数百个后端服务,而且必须依靠一款 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 的深度。纯服务器驱动渲染、混合模式或纯数据模式——每种方案的采用成本和收益各不相同。应该根据团队的成熟度以及产品的迭代节奏选择合适的方法。将网关视为一款产品,而非公用设施。它有用户(你的移动端开发人员),有需求(可靠性、性能、开发体验),并且需要长期投入。如果针对它的资源投入不足,其代价将体现在应用质量上。配备既懂前端又懂后端的人才。最优秀的网关工程师是那些既从事过移动端开发又参与过后端开发的人,因为他们能够全面地考量边界两侧的限制条件。如果找不到具备这种双重背景的人才,请投资于跨领域培训。
小结
移动应用是用户所看到的。但其背后的基础设施会影响应用的加载速度、崩溃频率、产品团队的迭代速度,以及应用故障处理的平稳程度。这一层对用户而言是不可见的,甚至对更广泛的工程团队来说也往往是隐形的。如何恰当地构建它,是移动产品开发中技术要求最高、对业务而言最重要的工作之一。
根据我的经验,这也是最令人满足的工作之一。你所构建的,是团队中每一位工程师都依赖的基础。当它运行正常时,大家的工作效率都会提升;当它出现故障时,每个人都会切身地感受到。这种责任感是一种殊荣,能够让你始终保持诚实。
原文链接:https://www.infoq.com/articles/mobile-app-infrastructure-at-scale/"



