14.1 服务发现、网关、BFF 与配置边界
服务拆开以后,调用方首先面对的不是“怎么发 HTTP”,而是实例地址会变化、实例可能尚未就绪、入口需要统一治理、不同客户端需要不同数据形状。
本课先建立调用拓扑。超时、重试、断路器和过载保护留到下一课。
服务发现回答两个问题
服务发现需要回答:
- 哪些实例属于目标服务?
- 哪些实例此刻可以接收请求?
常见方式有两类:
- 客户端发现:调用方从注册表取得实例并负载均衡;
- 服务端发现:调用方访问稳定地址,由负载均衡器或平台代理选择实例。
以带选择器的普通 Kubernetes ClusterIP Service 为例:DNS 返回 Service 的虚拟 IP,平台根据 EndpointSlice 中的端点状态把流量转发给就绪 Pod。下面假设 Service 名为 scoring,位于 default 命名空间,集群域为 cluster.local。应用代码不应缓存 Pod IP 当成永久地址。
caller
↓ DNS
scoring.default.svc.cluster.local
↓ Service / EndpointSlice
ready scoring Pods无头 Service 的 DNS 可以直接返回端点地址,不能照搬虚拟 IP 这条路径。publishNotReadyAddresses: true 还会让 EndpointSlice 的 ready 条件视为真;“经 Service 访问”并不无条件等于“只访问就绪实例”。就绪状态传播也有延迟,已建立的连接和在途请求仍需排空。Service 与 EndpointSlice 条件说明了这些差异。
存活探针持续失败会触发容器重启,就绪探针失败则让实例退出常规流量路由,本身不触发重启。配置启动探针后,存活和就绪探针会等它成功才开始检查。把依赖数据库短暂失败直接当作存活失败,可能导致所有实例同时重启并放大故障。
API Gateway 是边界,不是业务中心
网关适合处理跨入口的一致性职责:
- TLS 终止、身份验证和基础授权;
- 路由、协议转换和版本迁移;
- 请求大小限制、速率限制和入口观测;
- 少量稳定的响应聚合。
网关不应拥有核心业务流程。若“结算比赛、更新积分、发送奖励”的规则写在网关里,内部调用、批处理和消息入口会绕过它,网关也会成为高耦合发布瓶颈。
网关还必须传播客户端身份、追踪上下文和取消信号。身份头不能照单转发:应移除客户端伪造的内部身份头,传递下游可以验证的身份凭据;下游仍需检查用户能否操作指定对象。经过网关,不等于拿到所有比赛的管理员通行证。
BFF 为客户端体验负责
Web、移动端和第三方 API 往往有不同需求:
- 移动端更在意请求次数和弱网;
- Web 端可能需要更细粒度、可并行的数据;
- 第三方 API 需要长期兼容和严格配额。
BFF(Backend for Frontend)可以为一种客户端组合稳定的展示模型:
Mobile App → Mobile BFF ─┬→ Player Service
├→ Ranking Service
└→ Reward ServiceBFF 可以裁剪和聚合数据,但不应成为另一个共享企业总线。每增加一次扇出,都要计算最慢依赖、部分失败和总延迟预算。
假设同一统计窗口中,三个必需依赖的成功率均为 99.9%,失败相互独立,且暂不计 BFF 自身失败和额外超时,那么页面全部取得结果的概率约为:
0.999 × 0.999 × 0.999 ≈ 99.70%并行调用不代表失败独立;共享数据库故障就会破坏这个假设。并行等待通常取最慢依赖的耗时,成功却仍需要三者都成功。若推荐只是可选内容,可以显式返回“暂无推荐”,避免把它算作整页失败。
配置与密钥不是一回事
配置可以按敏感性和变更方式分类:
| 类型 | 示例 | 要求 |
|---|---|---|
| 普通部署配置 | 端口、依赖地址、功能参数 | 版本化、可审计、环境覆盖 |
| 动态运行配置 | 限流阈值、实验比例 | 有范围校验、回滚和生效观测 |
| 密钥 | 数据库口令、签名密钥 | 专用密钥存储、最小权限、轮换 |
| 业务数据 | 商品价格、比赛规则 | 走业务模型和审批,不伪装成配置 |
不要把密钥提交到仓库,也不要因为“动态配置方便”就允许任意运行时修改核心业务规则。配置变更同样需要所有者、验证、审计和回滚。
Kubernetes Secret 的 data 使用 Base64 编码;编码不提供保密性,仍需配置存储加密和访问权限。轮换数据库口令时,还要确认连接池何时重建连接,不能只看配置中心显示“已更新”。动态配置也不保证所有实例同时生效,应记录配置版本,检查每个实例的实际生效状态。
拓扑越短,故障越容易推理
一个入口路径如果是:
Gateway → BFF → Aggregator → Domain Service → Internal Proxy → Database每一跳都会消耗延迟、连接和可用性预算。新增中间层前应说明它承担的稳定职责;只为“以后可能复用”增加代理,会把未来成本提前变成当前故障面。
设计检查表
- 服务通过稳定名称发现,还是依赖易变实例地址?
- 就绪与存活是否表达了不同语义?
- 网关只做边界治理,还是藏有业务流程?
- BFF 的每个扇出依赖是否有部分失败策略?
- 配置和密钥是否具有不同的存储、权限与轮换机制?
- 是否能从入口追踪到所有下游,并传播截止时间与取消?
下一课为这张拓扑增加失败控制:先限制等待,再限制重试,最后限制故障对资源池的占用。
参考资料
- Kubernetes, Service
- Kubernetes, Liveness, Readiness, and Startup Probes
- Kubernetes, Secrets
- Microsoft, Backends for Frontends pattern