> 背景:笔者10年Java Web开发经验,目前负责MES生产执行系统。近半年因业务需要接入多家大模型API,从最早的直接HttpClient调用,到Nginx反向代理,最终自研了一套API路由网关。本文记录踩坑过程与架构思路,供同样在多模型接入层挣扎的开发者参考。

## 一、为什么需要中间层?直接调官方接口不行吗?

刚开始接AI功能时,我和大部分开发者一样:拿到API Key,直接上`RestTemplate`发请求。上线第一周就吃了大亏:

1. **官方接口偶发502/524**:某厂商高峰期响应时间从2s飙到30s,我们没有任何熔断机制,线程池直接被打满
2. **密钥散落在10个微服务里**:GPT、Claude、文心、通义...每个服务各自管理Key,轮换Key时要改10处代码
3. **不知道钱花哪了**:月底看账单傻眼,但无法精确到"哪个业务模块、哪次调用"消耗了多少Token

这时候才意识到:**AI能力不能直连,必须有一层自己的"API路由网关"**。

## 二、踩坑记录

### 坑1:Nginx反向代理的流式响应断裂

最早想的很简单:Nginx `proxy_pass` 到官方地址,前端统一走我的域名。配置如下:

```nginx
location /v1/chat/completions {
    proxy_pass https://api.xxx.com/v1/chat/completions;
    proxy_buffering off;  # 关闭缓冲支持SSE
    proxy_cache off;
}

问题:Nginx默认的proxy_read_timeout是60s,大模型推理长文本时经常超时断开。更坑的是,某些厂商返回的SSE格式不标准(比如data: [DONE]后多一个空行),Nginx转发后前端EventSource直接报错。

解决:放弃纯Nginx方案,必须上应用层网关,自己控制SSE帧的解析与转发。

坑2:HttpClient连接池被"慢请求"耗尽

用Java的RestTemplateHttpClient时,默认连接池大小很小(比如PoolingHttpClientConnectionManager默认每个Route才2个连接)。大模型接口是典型的长连接+慢响应场景,并发稍高就出现:

org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool

解决:单独为AI网关配置高连接池,且必须开启异步非阻塞模式。我们最终用WebClient(Spring WebFlux)替换了RestTemplate,一个请求占用连接的时间从平均15s降到毫秒级(因为WebClient是事件驱动,等待响应时不占线程)。


坑3:多厂商接口格式"方言"问题

各家API看似都兼容OpenAI格式,实则魔鬼在细节:

差异点 厂商A 厂商B
鉴权头 Authorization: Bearer xxx Authorization: xxx(不要Bearer)
流式结束标志 data: [DONE] data: [DONE]\n\n
错误码 429限流返回JSON 429直接断开TCP连接
模型名 gpt-4 GPT-4(大小写敏感)

后果:前端代码里写满了if-else适配逻辑,极难维护。

解决:在网关层做协议标准化。无论底层接的是谁,对外统一暴露OpenAI标准格式。网关负责"翻译"方言。


坑4:Token用量监控的盲区

官方后台只能看到账户级总用量,但我们需要回答:

  • 业务线A今天调了多少次?花了多少Token?

  • 哪个Prompt平均消耗最长?

  • 哪段时间是调用高峰?

解决:在网关层拦截请求/响应,解析usage字段(或自行计算),写入Redis做实时聚合,再异步刷入MySQL。核心逻辑:

@Component
public class UsageInterceptor implements GatewayInterceptor {
    
    @Override
    public void postHandle(RequestContext ctx, Response response) {
        // 解析响应体中的usage字段
        Usage usage = extractUsage(response.getBody());
        
        // 实时计数:按分钟、按业务线、按模型名维度聚合
        String key = String.format("usage:%s:%s:%s", 
            ctx.getBizLine(), 
            ctx.getModel(), 
            LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")));
            
        redisTemplate.opsForHash().increment(key, "prompt_tokens", usage.getPromptTokens());
        redisTemplate.opsForHash().increment(key, "completion_tokens", usage.getCompletionTokens());
        
        // 异步落库,避免阻塞响应
        usageLogQueue.offer(new UsageLog(ctx, usage));
    }
}

坑5:故障时无感知切换

某天凌晨,某厂商接口大规模超时,我们的告警群里炸了。当时架构是单通道直连,没有备用方案。

解决:引入多通道+健康检查。网关维护一个可用通道列表,定时探测(每30s发一个轻量请求)。主通道连续失败3次,自动标记为DOWN,流量切到备用通道。前端完全无感知。

@Component
public class ChannelHealthChecker {
    
    @Scheduled(fixedRate = 30000)
    public void check() {
        channelRegistry.getAll().forEach(channel -> {
            try {
                // 发一个cheap的模型请求探测
                probe(channel);
                channel.setStatus(UP);
            } catch (Exception e) {
                log.warn("通道[{}]探测失败,标记为不可用", channel.getName());
                channel.setStatus(DOWN);
            }
        });
    }
}

坑6:高并发下的内存溢出

网关要缓存请求体用于日志记录和重试,一开始直接用byte[]读入内存。压测时发现:一个10MB的上下文请求,并发100时直接堆内存爆炸。

解决:大请求体走磁盘溢出策略。超过阈值(比如1MB)的请求体,先写入临时文件,网关只保留文件句柄。日志系统异步读取文件,避免堆内存被大JSON撑爆。

三、架构演进图

最终架构如下(文字版):

[前端/客户端] 
    ↓ 统一HTTPS + AK/SK鉴权
[API网关层]  ← 自研,Java + WebFlux
    ├── 路由分发(按模型名/权重)
    ├── 协议适配(方言翻译)
    ├── 流式转发(SSE/WebSocket)
    ├── 用量采集(Redis聚合)
    └── 通道健康检查(定时探测)
    ↓
[多后端通道]  ← 各厂商接口

四、性能数据对比

上线后跑了2周,和直连官方对比:

指标 直连官方 经网关转发
平均延迟(P50) 1.2s 1.35s(+150ms网关损耗)
99分位延迟 8.5s(偶发超时) 2.1s(自动切备用通道)
可用性 97.5% 99.9%
故障恢复时间 人工介入15分钟 自动切换<<3秒

结论:牺牲150ms平均延迟,换来的是稳定性从97.5%到99.9%的质变,以及可观测性、多通道弹性的架构能力。对于生产环境,这买卖划算。

五、写在最后

如果你也在做AI应用,面临多模型管理、稳定性、用量监控的问题,没必要重复踩我踩过的坑

可以看看我之前的踩坑记录,可以为开发自己的API统一网关做个参考

适合谁用

  • 有服务器资源的独立开发者,想统一管理自己的模型接口

  • 小团队技术负责人,需要给多个业务线分配不同配额

  • 学习API网关设计的后端开发(源码未混淆,可二开)

不是谁都需要

  • 只是偶尔调一次API的个人玩家,直接curl就行,别折腾

  • 想要"免费无限额度"的,这玩意儿是网关不是魔法,底层该付的钱一分不少

有部署问题或者架构疑问,欢迎在评论区交流。毕竟这行最大的成本不是服务器,是凌晨3点被告警叫醒

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐