微服务架构设计模式-第八章
第8章 外部API模式
8.1 外部API的设计难题
FTGO 使用服务 API 的客户端一共有四种:
- web 应用程序,如 consumer web 应用程序(基于浏览器的用户界面),以及供内部管理员使用的用户界面
- 移动应用程序,一个供消费者,一个供送餐员
- 第三方开发人员编写的程序
API 的一种设计思路是让客户端直接调用服务。
- 细粒度服务 API 要求客户端发出多个请求以检索所需的数据,这样做效率低,而且可能导致糟糕的用户体验
- 由于客户端了解每项服务以及服务的 API 从而导致封装不足(紧耦合),因此今后很难更改服务的架构和 API
- 服务可能使用对客户端而言不便或不能使用的进程间通信机制,尤其是防火墙外的客户端

8.1.1 FTGO移动客户端API的设计难题
多次客户端请求导致用户体验不佳
- 移动端必须发出多个请求来检索它想要显示给用户的数据
- 网络延迟
- 要求移动开发人员编写复杂的 api组合代码,前端开发人员的首要任务应该是创建优质的用户体验
- 繁琐的 API 会更快地耗尽移动设备的电池电量
缺乏封装导致前端开发做出的代码修改影响后端
直接访问服务的移动应用程序的另一个弊端是缺少封装。随着应用程序的发展,服务开发人员有时会以破坏现有客户端的方式更改 API。他们甚至可能将系统分解为服务。开发人员可以添加新服务并拆分或合并现有服务。但是,如果将有关服务的知识融入到移动应用程序中(导致客户端和服务的过度耦合),则可能很难更改服务的 API
与更新服务器端应用程序不同,退出移动应用程序的新版本需要数小时甚至数天。apple 或 google 必须批准移动应用的升级,并使其在应用商店可供下载。在这种情况下,用户可能无法理解下载升级。而你又不能强迫不愿升级的用户。因此,将服务 api暴露给移动设备的策略为这些 API 今后的变更带来了重大障碍。
你提出的这个问题,确实是移动应用开发中一个非常核心且现实的挑战。
简单来说:**是的,公司通常需要同时维护多个版本,但这不意味着要无限期地维护所有版本。** 行业里有一套成熟的方法论来应对这个问题,核心思路是“**在保证旧版本基本可用的情况下,引导用户向新版本迁移**”。
下面是应对这个挑战的几个关键策略:
### 🗓️ 策略一:接受“版本碎片化”的现实
首先要认识到,**多版本并存是移动端的常态,而非异常**。用户不升级的原因多种多样:
* 关闭了自动更新。
* 设备存储空间不足。
* 不常打开应用。
* 对升级弹窗感到厌倦而选择“稍后再说”。
* 应用商店的审核和分阶段发布也会造成延迟。
因此,在设计之初就需要为这种“碎片化”做好准备。
### 🧬 策略二:通过“API版本管理”实现后端兼容
这是最核心的技术策略。它的原则是:**新老版本客户端可以调用不同的API,或者同一个API能兼容处理新旧两种请求**。
* **核心原则**:将API视为客户端与服务器之间的一份“长期合同”。后端更新时,不能破坏这份合同对旧客户端的承诺。
* **常见实现方式**:
* **URL路径版本化**:如 `/api/v1/user` 和 `/api/v2/user`。优点是直观易调试,缺点是可能长期维护多套并行逻辑。
* **请求头版本化**:客户端在HTTP请求头中携带版本信息。优点是URL保持简洁,缺点是版本信息不够直观,调试稍麻烦。
* **如何进行兼容性变更**:
* **安全的变更**:添加新的可选字段、新增API端点、放宽输入校验等。旧客户端可以安全地忽略这些新增内容。
* **破坏性变更**:删除或重命名现有字段、改变字段类型、将可选字段变为必填等。这类变更必须通过创建新版本的API来实现。
### 📱 策略三:客户端版本控制与升级引导
在客户端层面,通过策略性地引导用户升级,可以逐步淘汰旧版本。
* **分级更新策略**:
* **可选更新(推荐)**:弹窗提示更新,但提供“稍后”按钮。这是最主流、用户体验最好的方式。
* **强制更新(谨慎使用)**:用户若不更新则无法继续使用App。**这通常是最后手段**,仅在修复严重安全漏洞、重大法律合规问题或核心功能完全不可用时采用。粗暴使用会严重伤害用户体验。
* **设置“最低支持版本”**:服务端可配置一个“最低支持版本号”。当客户端版本低于此值时,启动App即触发强制更新。这相当于设定了一个“服务终止”的日期,是管理版本生命周期的有效工具。
* **渐进式淘汰**:对于低于“最低支持版本”但仍需维护的旧版,可增加弹窗频率或采用更强烈的引导方式,促使用户升级。
### 🚀 策略四:热更新 (OTA) 与审核的博弈
热更新(OTA)允许开发者在不经过应用商店审核的情况下,直接向用户推送代码更新。
* **技术原理**:多见于React Native、Flutter等跨平台框架,其业务逻辑代码(如JS Bundle)与原生壳子是分离的,可以单独替换。
* **平台政策差异**:
* **Apple (iOS)**:政策极为严格。原则上禁止下载可执行代码改变App功能。但在实践中,苹果会根据**功能影响**来判断,允许用其修复Bug或进行内容更新,但**严禁用其引入全新功能或改变App的核心目的**。
* **Google (Android)**:政策相对宽松。
**结论**:热更新是补充手段,不能完全替代应用商店发版。任何涉及**核心功能或重大UI变更**的更新,仍需走商店审核流程。
### 🗄️ 策略五:数据迁移的考量
当数据结构发生重大变化时,需要考虑数据迁移:
* **数据库版本化**:使用工具管理数据库Schema的变更脚本,确保升级过程平滑。
* **保持向后兼容**:数据表结构变更时,尽量新增字段而非修改或删除旧字段。
* **用户数据迁移**:对于复杂迁移,可能需要在App启动时执行数据转换逻辑,并保证迁移过程的可逆或失败回滚。
### 💎 总结
总的来说,移动端多版本维护是一项系统性工程,需要多维度配合:
1. **技术上**,通过**API版本管理**保证后端服务能同时服务新旧客户端。
2. **产品上**,通过**分级更新策略**和**最低版本控制**,引导用户向新版本迁移。
3. **流程上**,合理利用**热更新**处理紧急修复,同时规划好需要**应用商店审核**的重大版本发布。
这套组合拳做下来,就能在“维护多版本”和“向前演进”之间找到一个良好的平衡点。
你这个问题问得非常精准,直接触及了移动开发中一个更深的痛点:**“非不愿也,实不能也”**。
如果用户是因为手机硬件性能差(如内存不足、系统版本过低)而无法升级,那么之前提到的“引导或强制升级”策略就完全失效了。这种情况下,**强行要求升级等于直接流失用户**。
针对这个困境,行业内的通用策略是:**战略性放弃,但提供“终身维护”的兜底服务**。具体可以从技术、产品和商业三个层面来看:
### 1. 技术层面:为老设备“划界”和“瘦身”
- **设定“最低系统版本”门槛**:每个App都有`minSdkVersion`(最低安卓版本)或`deploymentTarget`(最低iOS版本)。当新版本因引入新库而需要更高系统时,旧设备用户根本看不到强制更新弹窗,他们只能停留在旧版本。这相当于系统自动帮你做了“隔离”。
- **提供“轻量版”或“兼容模式”**:对于无法升级硬件的用户,很多大厂会推出“Lite版”(如 Facebook Lite、淘宝特价版)。如果不想单独维护一个App,可以在代码里做**降级渲染**——检测到低端机型时,自动关闭动画、高斯模糊、复杂特效,仅加载文字和核心数据。
- **后端接口永久兼容**:对于这些“钉子户”版本,后端必须保证**基础读写功能**(如浏览、下单)的API永不删除。哪怕V10已经重构了架构,后端也要保留一套兼容V5的接口逻辑,直到最后一个V5用户自然流失。
### 2. 产品层面:划分核心功能与增强功能
维护多个版本的核心在于**分类**,而不是平均用力:
- **核心功能(必须维护)**:登录、支付、核心数据展示。这部分在旧版本上必须保证100%可用。
- **增强功能(选择性提供)**:AI识别、AR试妆、高清视频流。这部分在旧版本上直接**降级为静态页面或文字提示**(例如,在V5版本里隐藏入口,或点击后提示“当前版本过低,无法使用此功能,建议在电脑端操作”)。
**关键是**:只要支付和浏览能用,用户就不会有强烈投诉;新增的炫酷功能他们用不了,他们也能理解是因为自己手机太老。
### 3. 商业与运维层面:设定“生命周期”与“安全底线”
公司不能无限期维护所有版本,但也不能简单丢弃用户,需要设定明确的规则:
- **设定“停止服务”日期(EOL)**:在App内提前半年公告:“尊敬的用户,V5版本将于202X年X月X日停止服务,届时登录可能异常,请更换设备。” 给予用户充足的缓冲期来换手机或转移数据。
- **安全补丁的“强制覆盖”**:**唯一的例外是安全漏洞**。如果V5存在高危漏洞,即使手机性能差,也必须通过热修复(Patch)强制修复。如果热修复都加载不了,那只能通过短信或推送引导用户去网页端(H5)处理紧急事务,App端则直接阻断高风险操作(如修改密码、大额转账)。
### 4. 针对“机皇钉子户”的特殊处理
如果用户用的是iPhone 6s或安卓低端机,他们往往对手机依赖性较低,属于“轻度用户”。对于这部分用户:
- **不必维护最新API**:V5版本不需要接入最新的广告系统或用户画像系统,只要能读取基础数据即可。
- **利用“网页套壳”**:在极端情况下,如果V5的Native(原生)代码实在跑不动新逻辑,可以把V5的首页改成一个大大的**WebView(网页容器)**,直接加载移动端网页。后端只要保证网页版的兼容性,Native层只需要一个浏览器内核即可。
---
**总结一下:**
你不需要为V5用户提供V10的所有功能,**你只需要为V5用户保证V5当时承诺的基础服务能稳定运行**。当手机性能成为瓶颈时,**这不是开发能解决的问题,而是产品策略问题**——要么接受这部分用户只能使用基础功能,要么主动引导他们转向PC端或网页端。
如果这些旧版本用户占比很高(比如超过15%),那公司甚至需要专门成立“遗留系统维护组”,专门处理这些旧设备的兼容性Bug,但不再为它们开发新功能。
你问到这个问题,是不是正在评估某个旧版本用户群的维护成本?如果能告诉我你们的用户画像(比如是海外低端机为主,还是国内老年机为主),我可以帮你判断是应该“放弃”还是“专门适配”。😊
服务可能选用对客户端不友好的进程间通信机制
比如 gRPC 或者 AMQP 消息传递协议
8.1.2 其他类型客户端API的设计难题
为第三方应用程序设计 API
第三方开发人员需要一个稳定的 API。组织应该拥有一个由独立团队开发的独立公共 API,而不是直接向第三方开发人员公开服务的 API。
8.2 API Gateway模式
实现一个服务,该服务是外部 API 客户端进入基于微服务应用程序的入口点
api gateway 是一种服务,它是外部世界进入应用程序的入口点。负责请求路由、API 组合和身份验证等各项功能。
8.2.1 什么是API Gateway模式

API Gateway 封装了应用程序的内部架构,并为其客户端提供 API。它还可能具有其他职责,例如身份验证、流量监控和速率限制。
API Gateway 负责请求路由、API组合和协议转换。
你提到的这个“功能颗粒度”问题,确实是多端设计中**最核心、也最容易被忽视**的体验难题。它不仅仅是“屏幕大小”的问题,更深层次的原因是**使用场景**和**用户心智**的截然不同。
对于“以谁为主”,行业内的主流共识是:**“内容与数据以Web端为依托,但交互与体验以移动端为优先”(Mobile-First, Content-Rich for Web)**。
但这并不是一个简单的二选一,而是需要在设计上做好“分层”与“克制”。具体可以从以下几个维度来看:
### 1. 核心原则:场景决定功能,而非屏幕决定功能
在确定功能颗粒度之前,首先要明确用户分别在Web端和移动端**想干什么**:
- **移动端(碎片化、高频、意图明确)**:用户通常在通勤、排队等场景下使用,使用时间短,容易被中断。他们的核心需求是**“快速完成任务”**,比如扫码、看消息、下单、签到。
- **Web端(沉浸式、低频、复杂操作)**:用户通常在办公室或家里,有整块时间,注意力集中。他们的核心需求是**“深度处理信息”**,比如数据分析、批量上传、复杂报表导出、长文编辑。
**设计策略**:移动端做**“决策器”**,Web端做**“仪表盘”**。移动端负责展示最关键的数据和快捷入口,而把复杂的分析、配置和批量操作留给Web端。
### 2. 功能裁剪的“721法则”
在实际落地时,可以遵循这个经验比例来分配功能:
- **70% 的基础功能(全平台一致)**:登录、浏览、搜索、下单、支付、消息。这些是产品的核心价值,无论屏幕大小,必须保证体验的一致性和流畅性。
- **20% 的增强功能(Web端独有或优先)**:
- **复杂的数据分析**:比如运营后台、BI报表、多维透视表,这些在手机上看不清,也操作不了。
- **批量处理**:比如批量导入联系人、批量编辑商品SKU、批量下载文件。
- **专业创作工具**:如视频剪辑、复杂文档排版、代码编辑。
- **10% 的移动端特色功能(App独有)**:
- **硬件调用**:如扫码、NFC支付、语音输入、拍照识图。
- **LBS(基于位置的服务)**:如签到、附近的人、路线导航。
- **轻量化交互**:如下拉刷新、摇一摇、滑动操作。
### 3. 信息架构的差异化设计
你提到“网页屏幕大展示更多”,但如果直接把Web端密密麻麻的菜单搬到手机上,用户体验会很差。正确的做法是**“信息分层”**:
- **Web端:利用“富导航”**。可以使用顶部多级菜单、侧边栏、卡片式布局,把所有的功能入口和次级信息平铺展示,让用户一目了然。
- **移动端:利用“渐进式披露”**。将复杂功能收进“更多”或“我的”菜单里,利用手势操作(如左滑删除、下拉搜索)来减少界面负担。用户需要时才去找,而不是让所有功能都摆在眼前。
### 4. 数据同步的一致性
这是保证“哪个为主”不产生混乱的基础。**不管在哪个端操作,数据必须实时同步**。
- **举个例子**:用户在Web端配置了一个复杂的“自动化工作流”,在移动端不需要去编辑这个流程,但**必须能收到**该流程执行后产生的“任务待办”通知,并能够点击“通过”或“驳回”。
- **设计原则**:Web端负责“设置规则”,移动端负责“接收通知并执行轻量审批”。这样既保证了功能的完整性,又保证了使用的便捷性。
### 5. 开发策略的折中
从开发角度看,如果移动App和Web是两套独立代码,维护两个“功能颗粒度”是可行的。但如果是跨平台方案(如React Native/Flutter),可能会倾向于共享逻辑层,而在UI层做“响应式断点”判断,根据屏幕宽度显示不同的组件。
---
### 总结建议
回到你关心的问题:“哪个为主”?答案是**“互为补充”**。
- 如果你们的产品是**工具型/生产力型**(如办公软件、设计工具、数据后台),**Web端是“主战场”**,移动端是辅助和通知中心。
- 如果你们的产品是**消费型/生活型**(如电商、社交、内容阅读),**移动端是“主战场”**,Web端更多是品牌展示和深度管理后台。
**比较好的实践是:在需求评审时,专门增加一个“多端适配”的环节,针对每个功能灵魂三问:**
1. 用户真的需要在外出时用手机操作这个功能吗?
2. 这个功能是不是必须依赖大屏或键鼠?
3. 如果只做一端,另一端会有多大的抱怨?
通过这三个问题,你就能清晰地界定功能颗粒度,做出让用户觉得“顺理成章”的设计。你现在是在规划一款新产品的多端架构,还是在优化现有产品的功能分配呢?如果方便,可以说说你们的产品类型,我可以给你更具体的建议。😊
API Gateway能为每个客户端提供他们专用的 api
单一 API 的问题在于不同的客户端通常具有不同的需求。给每个客户端单一一个单独的API Gateway,进一步实现为每个客户端提供独立拍的想法
实现边缘功能
虽然API Gateway 的主要职责是 API 路由和 API 组合,但它也可以实现所谓的边缘功能。边缘功能,顾名思义,是在应用程序边缘实现的请求、处理功能。应用程序可能实现的边缘功能包括:
- 身份验证:验证客户端身份
- 访问授权:验证客户端是否有权执行该特定操作
- 速率限制:限制特定客户或所以客户端每秒的请求数
- 缓存:缓存响应以减少对服务的请求数
- 指标收集:收集有关 API 使用情况的指标,以进行计费分析
- 请求日志:记录请求历史
应用程序中有三个不同的位置可以实现这些边缘功能。
- 在后端服务中实现他们。
- 在API Gateway 上游的边缘服务中实现
- 好处一:分隔问题;API Gateway 侧重于 API 路由和组合
- 好处二:集中关键边缘功能的职责,例如身份验证
- 弊端:额外的请求跳跃从而增加了网络延迟
- 在API Gateway 中实现边缘功能。网络跳跃减少,改善延迟
API Gateway 的架构
分两层,API 和公共层。API 层由一个或多个独立的 API模块组成。每个 API模块都为特定客户端实现 API,公共层实现共享功能,包括边缘功能,如身份验证。

API Gateway 的所有者模式
谁负责API Gateway 的开发和运维?
netflix 推出的方法是,让客户端团队(包括移动、web 和公共 API)拥有与他们相关的 API 模块并公开 API。API Gateway 团队负责开发公共模块和API Gateway 的运维。
使用后端前置模式
API Gateway 的一个问题是它的职责不明确。多个团队为相同的代码库做贡献。API Gateway 负责运维,虽然没有 SOA ESB 那么糟糕,但这种职责模糊与 "如果你构建它,你就拥有它" 的微服务架构哲学背道而驰
解决方案是为每个客户端提供一个 API Gateway,即所谓的后端前置(backends for front-ends,bff)模式。每个 API 模块都成为自己的独立API Gateway,由对应的客户端团队开发和运维。

公共 API 团队拥有并运维他们的 API Gateway,移动团队拥有并运维属于他们的 API等等。
除了明确职责外,后端前置还有其他好处。API 模块彼此隔离,从而提高了可靠性。还能提高可观测性,不同的 API 模块是不同的进程。每个API 都是可独立扩展的。还能减少启动时间,每个API Gateway 都是更小、更简单的应用程序。
8.2.2 API Gateway模式的好处和弊端
- 好处:封装了应用程序的内部结构,客户端不必调用特定服务,而是与API Gateway 通信;简化客户端代码
- 弊端:它还是一个必须开发、部署和管理的高可用组件,存在开发瓶颈的风险。
8.2.3 以Netflix为例的API Gateway
略
8.2.4 API Gateway 的设计难题
- 性能和可扩展性
- 使用响应式编程抽象编写可维护的代码
- Java & CompletableFutures
- Project Reactor Monos
- 由 netflix 创建的 rxJava Observable,专门用于在其 API Gateway 中解决此问题
- scala futures
- 处理局部故障
- 成为应用程序架构中的好公民
8.3 实现一个API Gateway
8.3.1 使用现成的API Gateway
AWS API Gateway
弊端:
- 不支持 API 组合
- 仅支持 HTTP(S),非常依赖 JSON
AWS Application Load Balancer
使用产品化的API Gateway
比如 kong 或者 traefik 之类的产品
8.3.2 开发自己的API Gateway
本质上是一个代理其他服务请求的 web 应用程序。可以使用自己喜欢的 wen 框架构建一个。但是,你需要解决两个关键问题:
- 实现定义路由规则的机制以简化复杂的代码
- 正确实现 HTTP 代理行为,包括如何处理 http 标头
netflix zuul
pivotal spring cloud gateway
API Gateway 包含以下包:
- ApiGatewayMain 包:定义API Gateway 的主程序
- 一个或多个 API 包:一个 API 包实现一组 API 端点
- 代理程序包:由 API 程序包用于调用服务的代理类组成

浙公网安备 33010602011771号