题目与使用场景
请设计 300 个后端服务的公网统一入口。系统在三个双活区域内承担每秒 50 万峰值请求,保存约 2 万条路由定义。网关负责 TLS 终止、调用方认证、粗粒度授权与配额、请求规范化、上游选择、按权重灰度以及指标和链路追踪。假设可用性目标为 99.99%,网关新增 p99 延迟低于 10 毫秒。这些数字是面试输入,并非任何产品的性能承诺。
普通路由与策略变更应在 30 秒内到达健康网关;紧急凭证吊销需要更快的通道。错误配置不能同时击穿所有区域,控制面不可用时,网关仍要依靠最后一个已知良好版本继续服务。后端业务逻辑、响应聚合、长流程编排和面向业务对象的细粒度授权不属于网关职责。
这是一道高级系统设计题。难点位于组件边界:定义请求热路径,隔离配置发布与在线流量,约束故障行为,并证明这个全局入口不会成为单点。
面试官在考察什么
高质量回答首先会区分数据面与控制面。各区域网关进程用本地不可变快照处理请求;控制面负责校验、版本化、存储和分发路由与策略。若每次请求都查询数据库或远程配置服务,延迟目标和控制面故障隔离都无法成立。
第二个考点是职责边界。TLS、身份校验、路由匹配、粗粒度策略、限流、请求头、超时和遥测属于横切能力;库存判断、支付决策、对象级权限属于业务服务。允许任意业务插件运行的“万能网关”很难测试,发布风险也会扩散到所有流量。
容量估算必须可复算。每秒 50 万请求、平均入站请求 2 KiB,对应 500,000 × 2 KiB ≈ 0.95 GiB/s,尚未计算协议开销。若完整策略链压测后,单实例在目标 p99 下可安全承载每秒 Q 个请求,至少需要 ceil(500,000 / Q) 个实例,再增加区域故障余量。三个等流量区域失去一个后,幸存区域会从约每秒 16.7 万升至 25 万,增幅 50%;容量规划必须覆盖这一状态。
完整回答还要交代配置安全、区域路由、过载传播、重试、可观测性和明确的降级策略。只说“部署到多个区域”并没有说明流量如何迁移、状态如何更新、网络分区时保留哪些能力。
回答前应澄清的问题
- 客户端是谁? 假设包括公网 Web、移动端与合作方的 HTTP API。内部服务间流量可使用独立入口或服务网格策略。
- 可用性单元是什么? 每个区域的网关跨至少三个故障域部署;三个区域双活,全局路由会摘除不健康区域。
- 路由如何区分? 按主机名、方法和规范化路径匹配;顺序必须确定,歧义定义在发布前被拒绝。
- 哪些策略同步执行? 签名校验、路由匹配、粗粒度授权、配额、请求限制和请求头变换;网关不查询业务数据作决策。
- 配置要求多新? 普通变更 30 秒内收敛,网关上报实际应用版本。安全吊销依靠短期令牌或独立分发的小型拒绝列表,不强迫重编全部快照。
- 跨区域配额多严格? 默认从全局预算向区域分配配额。严格全局计数会给每次请求增加跨区域依赖,需要单独证明必要性。
- 网关可以重试吗? 仅在确认首次尝试未被接受时重试幂等请求,并受很小的重试预算约束;非幂等写入需要业务幂等键或禁止自动重试。
- 哪些内容不在范围内? 网关前的 DDoS 清洗、服务实现、数据库复制和业务事务属于其他系统。
30 秒回答框架
“我会在三个双活区域分别部署无状态网关集群,并置于具备健康感知的全局流量路由之后。每个请求留在一个区域内:网关终止 TLS,用本地密钥缓存校验身份,从不可变路由快照匹配规则,应用粗粒度策略和区域配额,再选择同区域健康上游。独立控制面校验每次配置、分配版本、先对小批网关灰度,再原子激活;控制面故障时,网关继续使用最后一个已知良好快照。我会按三个等流量区域失去一个后幸存区域负载增加 50% 来配容量,限制重试和队列以免放大过载,并监控新增延迟、路由结果、上游健康、配置版本和区域切换。”
分步深入设计
系统分为四层。全局流量管理把客户端送到邻近且健康的区域;区域负载均衡器把流量分散到跨故障域的无状态网关;数据面代理到同区域服务端点;控制面存储期望配置,将其编译为带版本快照,并在请求路径之外分发。微软官方架构文档把网关定义为负责路由与横切能力的统一入口,其托管和自托管网关模式也体现了集中管理配置、分散承载运行时流量的边界。
正常请求流程如下:
- 全局 DNS 或 Anycast 选择健康区域,区域负载均衡器选择网关实例。
- 网关终止 TLS,执行连接数和请求体大小限制,并附加请求 ID。
- 它用缓存的签发方元数据和公钥校验签名令牌,不为每次请求调用身份服务。
- 编译后的匹配器按主机、方法和规范化路径,从当前快照中选择路由。
- 策略链应用粗粒度 scope、区域配额、请求头、超时和可选灰度分流。
- 网关从本地缓存的服务发现结果选择健康端点,携带有限截止时间转发。
- 它记录结果、延迟、路由 ID、上游集群、配置版本和追踪上下文,并把遥测异步送出热路径。
路由定义保持声明式:
Route {
id: string
host: string
methods: string[]
path_template: string
upstream_cluster: string
auth_policy_id: string
quota_policy_id: string
timeout_ms: uint32
retry_policy: { max_attempts, retryable_statuses }
traffic_split: [{ revision, weight }]
}控制 API 用幂等键接收目标修订并返回修订 ID。校验包括 schema、匹配冲突、上游引用、证书归属、策略上限和不安全的重试组合。编译器生成包含预构建匹配结构的不可变快照。发布依次经过校验、影子对比、小批实例、单故障域、单区域,再扩到所有区域。网关下载后校验摘要与签名,在请求线程之外构建对象,最后原子切换一个指针;在途请求继续使用旧快照。健康指标恶化时,小批实例回退到上一个版本。
最值得深入的瓶颈是大规模配置安全。逐条发送可变更新,可能让路由、策略与证书在不兼容版本间组合。版本快照明确了激活原子单位。网关把最新已验证快照持久化到本地磁盘或节点持久存储,至少保留一个旧版本,并暴露 desiredversion、downloadedversion、active_version。控制面故障只冻结变更,不停止流量;损坏或不完整的快照会被拒绝。紧急吊销不应等待 2 万条路由重编:短期凭证限制暴露时间,小型签名拒绝列表走独立快速通道并设置过期时间。
容量需要压测完整策略链,不能引用空代理吞吐。如果实测单实例在目标 p99 下承载每秒 8,000 请求,峰值至少为 ceil(500,000 / 8,000) = 63 个实例,尚未加入余量。三个等流量区域失去一个后,每个幸存区域承担每秒 25 万请求,按该基准需要 32 个实例;每区部署 40–45 个可留出运维空间。这个数字仅作演算,必须用真实 TLS、令牌校验、负载大小、日志和上游延迟重新测量。
网关要主动丢弃过载,而不是积压。限制并发请求、连接、请求体、每路由队列和遥测缓冲;把截止时间传递给上游;熔断器避免故障服务占满所有连接。只对少量幂等故障重试,加入抖动,并把每次尝试计入重试预算。上游饱和时迅速返回 503,通常比建立无界队列、耗尽共享网关更安全。
认证路径优先本地校验签名令牌。公钥异步刷新,在轮换期间重叠生效,并在有限时间内保留最后一个有效集合。若必须对不透明令牌做在线内省,就缓存短期成功结果,并按路由风险定义失败开放或失败关闭;这个同步依赖会改变可用性计算。对象归属和业务授权仍在服务中执行,因为网关没有权威业务状态。
配额在区域热路径执行。全局分配器把租户预算拆成短期区域租约,区域状态存储作原子决策。这样网络分区不会无限放大全局配额,但隔离区域只能花完现有租约。若各区域独立放行并事后对账,可用性更高,却会产生超额放行;产品负责人需要选择可接受上界。令牌桶细节交给现有分布式限流子系统,而不是在每个网关内重复实现。
AWS 官方文档给出了多区域网关的主备故障转移和双活加权路由。本题选择双活,以降低冷切换风险。健康检查分层处理:实例健康摘除一个进程,故障域信号排空一组实例,端到端合成探测与区域错误率共同决定是否摘除区域;条件允许时逐步迁移流量。区域后端与数据存储也必须就绪,只移动网关无法让不可用服务恢复健康。
可观测性至少包含网关新增 p50/p95/p99 延迟、各路由请求与错误率、TLS 与认证失败、配额结果、活动连接、队列深度、重试、熔断状态、上游延迟、端点健康和配置版本滞后。成功日志采样,安全与错误事件在有限预算内保留;追踪沿用入站上下文并创建网关 span。告警要区分网关失败与上游失败,避免因业务服务事故错误回滚健康网关。
主要替代方案是一个通用网关,或按客户端、业务域拆分多个网关/BFF。单集群简化公网入口与治理,却扩大故障半径,也容易堆积策略;多网关隔离团队和客户端差异,却增加运维重复与全局规则一致性成本。默认从一个轻量平台网关和业务服务开始,只有客户端确实需要聚合或不同契约时才增加 BFF。服务网格负责东西向流量,可与公网南北向网关互补,不能直接替代它。
验证包括:路由表确定性匹配的性质测试、快照兼容测试、正常峰值和单区域故障容量压测、新旧修订影子对比,以及对控制面失联、密钥陈旧、上游变慢、遥测反压、故障域丢失和区域撤离的故障注入。每种失败都有可观察状态与有限响应,设计才算闭环。
高质量示例回答
“我会在三个双活区域跨故障域部署无状态网关集群。全局路由把客户端送到健康邻近区域,区域负载均衡器再选择实例。热路径依次执行 TLS、从本地密钥缓存校验签名身份、匹配编译路由、应用粗粒度授权和区域配额、选择同区域健康端点,并用有限超时转发。业务授权留给服务。
请求路径不访问配置数据库。独立控制面校验目标路由与策略,拒绝歧义匹配和危险重试,编译不可变签名快照,再按影子、小批实例、故障域和区域逐步发布。实例在请求线程外构建新快照并原子切换,持久化最后一个已知良好版本。因此控制面故障会停止变更,但不会停止流量;版本滞后和自动回滚让部分发布可见且可逆。
每秒 50 万请求、平均 2 KiB 入站流量约为 0.95 GiB/s。我要压测完整策略链,得到单实例安全吞吐 Q,部署 ceil(500,000 / Q) 再增加故障余量。三个等流量区域失去一个后,幸存区域负载增加 50%,区域容量与压测都覆盖这一状态。
我会限制连接、并发、请求体、队列和重试次数。幂等请求使用很小的重试预算,非幂等写入需要幂等键或禁止自动重试。全局配额用短期区域租约分配,明确网络分区下的取舍。最后监控网关新增延迟、路由结果、上游健康、重试与熔断、活动配置版本和区域合成探测,并在上线前演练控制面失联、错误配置、故障域丢失和区域切换。”
常见错误
- 每次请求都从数据库读取路由或策略 → 延迟和可用性绑定控制面 → 使用已验证本地快照,并保留最后一个已知良好版本。
- 把业务逻辑放入网关插件 → 发布形成全局故障半径,领域归属模糊 → 保持网关声明式,把业务决策放到服务或确有必要的 BFF。
- 逐条发布可变更新 → 路由、策略与证书可能以不兼容版本激活 → 编译一个带版本快照并原子切换。
- 认为多实例就等于高可用 → 一个错误版本仍可同时击穿所有实例 → 按小批、故障域、区域逐步灰度,设置自动健康门禁与回滚。
- 只按正常峰值配容量 → 区域撤离时幸存区域过载 → 计算并压测三个等流量区域失去一个后的 50% 增幅。
- 重试所有失败 → 重试放大过载并可能重复写入 → 只重试有限的幂等场景,写入由业务提供幂等性。
- 每次请求都调用身份服务 → 认证继承同步远程依赖 → 本地校验签名令牌,异步刷新公钥并限制陈旧时间。
- 给每个区域完整全局配额 → 租户额度会按区域数倍增 → 分配区域租约,或明确接受的超额上界。
- 只迁移网关流量、不检查后端 → 目标区域可能没有健康服务或数据容量 → 把端到端探测和后端就绪纳入切换条件。
- 记录每个成功请求的完整负载 → 遥测消耗热路径资源并可能泄露敏感信息 → 异步记录结构化元数据,采样成功请求并按策略脱敏。
追问与回答
追问一:如何回滚错误路由配置而不中断请求?
保留不可变快照和至少一个已验证旧版本。网关在请求线程外构建候选版本,检查摘要和引用,再原子切换活动指针;在途请求持有旧快照直到结束。如果小批实例健康指标变差,控制面把修订标记为失败,实例切回旧指针。回滚只改变配置状态,不重启整个集群。
追问二:控制面不可用一小时会怎样?
流量继续使用持久化的最后一个已知良好快照,网关暴露快照年龄与版本,并拒绝未经验证的变更。证书和密钥轮换必须设置足够长的重叠有效期。紧急吊销依靠短期令牌或独立签名的小型拒绝列表。故障期间失去变更能力,因此分发延迟应在现有材料过期前很久触发告警。
追问三:网关重试时如何避免重复执行 POST?
上游连接失败并不代表业务未提交,网关不能自动重试非幂等请求。必须重试的操作由客户端提供幂等键,业务服务保存并返回第一次结果。网关只在请求截止时间和小额重试预算内尝试。如果传输层能证明对端尚未接收任何字节,可单独处理这种连接失败。
追问四:区域路由选择全局 DNS 还是 Anycast?
两者都能满足该架构。健康感知 DNS 运维更简单,但客户端缓存会让撤离逐渐发生;Anycast 可更快调度,却需要更强网络运维,也仍需应用健康信号。我会选择平台已经成熟运营的机制,实测切换时间,并让客户端容忍端点变化。区域网关设计不依赖“DNS 立即生效”的假设。
追问五:什么时候应该把一个网关拆成多个?
当隔离或契约确实不同,例如受监管流量、独立运营的业务域,或移动端与 Web 端需要显著不同的聚合时再拆分。不要只是按每个微服务拆,否则客户端重新感知内部拓扑,运维数量也会膨胀。即便运行时集群隔离,策略 schema、身份规则、遥测和发布安全仍可作为共享平台能力。