面试官问“如果让你设计一个 LLM Gateway,你会怎么做?”
一、技术架构

配套基础设施可以选择:
-
MySQL:保存虚拟 Key、模型配置、路由规则、调用记录和账单。
-
Redis:分布式限流、短期缓存、配额状态和热点配置缓存。
-
Resilience4j:熔断、超时隔离及有条件的重试。
-
Micrometer + Prometheus + Grafana:监控请求量、错误率、Token 用量、延迟和成本。
-
Docker + Nginx:多实例部署和负载均衡。
这里有一个关键点:Spring AI 负责统一模型调用,但完整的 LLM Gateway 还需要自己实现认证、路由、配额和计量等治理能力
二、结合技术名词,面试应该怎么说?
1. 请求预处理:怎么鉴权、限流、缓存?
虚拟 Key 认证:Spring Security + MySQL
不直接把 OpenAI 或 Claude 的真实 API Key 暴露给业务方,而是生成自己的虚拟 Key。通过 Spring Security Filter 提取 Key,校验状态、所属用户、权限和有效期;真实 Provider Key 放在服务端安全配置中,生产环境可使用密钥管理服务。
分布式限流:Redis + Lua
例如按照 API Key + 模型 维度限流,同时限制请求频率和 Token 预算。使用 Redis Lua 脚本将检查与更新合并为原子操作,避免多个网关实例并发放行。对于 Token 数量,可以先按输入长度和 max_tokens 预估,再根据实际用量结算。
语义缓存:Redis + Embedding
将 Prompt 转换为向量,检索相似问题,再结合相似度阈值、模型、系统提示词版本和业务上下文判断是否复用结果。也可以使用 Spring AI 的 ChatMemory、Vector Store 相关能力辅助实现,但要区分:聊天记忆不等于语义缓存。对于实时数据、工具调用和高风险场景,不应直接复用旧答案。
面试时可以总结为:我会通过 Spring Security 实现统一鉴权,Redis Lua 实现原子限流,再根据业务特征决定是否开启语义缓存,尽量在调用模型之前拦截不必要的请求
2. 智能路由:怎么选模型?
可以设计一个 ModelRouter 接口,根据请求特征选择模型:
|
判断维度
|
具体实现
|
| --- | --- |
|
用户指定模型
|
优先遵循显式指定,并校验权限
|
|
任务类型
|
简单分类走低成本模型,复杂推理走高能力模型
|
|
成本预算
|
根据输入、输出 Token 预估成本,过滤超预算模型
|
|
模型健康度
|
跳过已熔断或不可用的 Provider
|
|
响应延迟
|
结合历史 P95 延迟和当前负载进行选择
|
例如,假设系统接入了一个低成本模型和一个高能力模型:
-
普通文本总结:优先选择低成本模型
-
复杂代码分析:选择高能力模型
-
首选模型连续超时:在请求允许切换的情况下,尝试备用模型
Java 策略模式:
public interface ModelRouter {
ModelConfig route(LLMRequest request);
}
不同路由策略可以实现这个接口,例如 CostBasedRouter、LatencyBasedRouter 和 RuleBasedRouter,再由路由管理器统一选择。
面试加分点: 不要一开始就说使用机器学习训练路由模型。规则路由更容易解释、调试和验证;有足够的调用数据后,再考虑动态权重或更复杂的路由算法。
3. 模型调用:怎么统一不同厂商?
可以使用 Spring AI + 自定义 Provider Adapter + Resilience4j
-
Spring AI:统一聊天模型调用、消息结构及流式交互等能力
-
Provider Adapter:封装不同模型的请求参数、认证方式、错误码和响应格式。
-
Resilience4j:实现熔断、超时和有限重试
-
HTTP 客户端:处理连接池、连接超时、读取超时和连接释放
比如业务层只调用统一的 ChatService,不需要关心底层到底是 OpenAI 兼容接口还是 Claude 的原生接口
这里有两个细节值得主动提出来:
-
流式响应不能简单地套用普通 HTTP 重试逻辑。 一旦已经向客户端输出部分 Token,再切换模型可能导致重复内容或输出格式破坏
-
超时不代表模型一定没有执行。 对非幂等请求,不能因为超时就无限重试;应根据错误类型、是否已输出响应以及 Provider 能力决定是否重试或切换
4. 计费与配额:如何保证数据正确?
可以采用以下方案:
-
MySQL 保存持久化调用记录和账单
-
Redis Lua 原子预扣或预留预算,避免并发请求同时突破配额
-
请求完成后根据 Provider 返回的实际 Token 用量进行结算
-
使用请求 ID、唯一约束和状态机保证结算幂等
-
对超时、断连等无法立即确定最终用量的请求,保留待确认状态,再根据 Provider 返回数据或用量查询能力补齐
注意:接口幂等只能保证本地结算不重复,不能天然保证上游模型只执行一次。 这句话很适合面试时主动补充
5. 高可用:组件挂了怎么办?
网关实例故障
Spring Boot 无状态多实例部署,通过 Nginx 或云负载均衡分发请求;健康检查失败时摘除异常实例。
模型供应商故障
使用 Resilience4j Circuit Breaker 统计失败率,达到阈值后熔断,并结合备用模型路由,避免持续请求故障供应商。
Redis 故障
对严格配额采用保守降级,必要时拒绝请求;非关键缓存可以降级,但要设置并发保护,避免缓存失效后请求瞬间涌向模型供应商。
监控与告警
使用 Micrometer、Prometheus 和 Grafana 监控成功率、P95 延迟、超时率、各模型 Token 用量及成本。
三、面试回答
如果让我设计一个 LLM Gateway,我会基于 Spring Boot 构建统一模型接入层,使用 Spring AI 统一模型调用能力,并通过自定义 Provider Adapter 屏蔽不同厂商的 API 差异。
请求进入网关后,我会通过 Spring Security 校验虚拟 Key,再使用 Redis 和 Lua 脚本实现分布式限流与原子额度控制。对于适合缓存的请求,可以结合 Embedding 和向量相似度实现语义缓存。
路由层采用策略模式,综合用户指定模型、任务类型、Token 预算、调用成本和模型健康度选择目标模型。初期采用可配置的规则路由,后续再根据实际调用数据优化策略。
调用层统一支持流式和非流式响应,通过 Resilience4j 实现熔断、超时和有限重试,并针对已经输出部分内容的流式请求制定单独的故障处理策略。
在计费方面,我会结合 Redis 原子额度控制、MySQL 持久化调用记录以及请求 ID 和唯一约束,实现用量结算与幂等处理。对于调用超时但执行状态不确定的情况,则保留待确认记录,避免直接重复扣费或盲目重试。
最后,网关采用无状态多实例部署,通过负载均衡保障实例级高可用,使用 Micrometer、Prometheus 和 Grafana 监控调用成功率、延迟、Token 消耗和成本。
整体设计会优先保证调用链路可控、计费准确和故障可恢复,再逐步增加语义缓存和动态路由能力,避免一开始就引入过多组件,增加系统复杂度。
四、技术名词要能解释
面试官很可能沿着技术名词继续追问,因此至少要能解释清楚:
1. Redis Lua 为什么能解决并发限流问题?
Redis Lua 脚本可以将限流检查与计数更新作为一个原子操作执行,避免并发请求交错执行导致超额放行。
2. 为什么语义缓存不能只比较 Prompt 字符串?
因为用户表达相同语义时可能使用不同措辞,需要结合 Embedding 向量相似度判断,并考虑模型、上下文和参数等因素避免错误复用结果。
3. Resilience4j 熔断和重试分别解决什么问题?
重试用于处理暂时性故障,熔断则在故障率达到阈值时暂时阻止请求访问异常服务,避免故障持续扩散。
4. Spring AI 能否直接替代完整的 Gateway?
不能,Spring AI 主要提供大模型调用的抽象与集成能力,而 Gateway 还需要自行实现鉴权、限流、智能路由、配额计量和成本治理等功能。
5. 流式输出开始后,为什么不能随意切换备用模型?
因为部分 Token 已经发送给客户端,切换模型可能导致内容重复、上下文不一致或输出格式错乱。
6. 如何防止并发请求突破 Token 配额?
可以通过 Redis Lua 原子预占预算、请求完成后按实际 Token 用量结算,并结合请求幂等和异常补偿控制并发超额与重复扣费。

浙公网安备 33010602011771号