【Token限流计费系列】第2讲大模型把公司带宽跑爆记一次多租户网关架构重构前言企业级大模型服务上线后单个租户的突发流量可能迅速耗尽带宽、连接池和模型调用配额。如果网关缺少租户隔离、限流和动态路由能力大客户活动、批量任务或异常脚本都会影响其他租户的稳定性。本文聚焦多租户大模型网关的架构重构拆解鉴权、路由、限流和资源隔离如何协同避免一个租户拖垮整个平台。一、底层原理1.1 核心机制大模型网关其实就是个“超级前台”。它要干三件事。第一认人。你是谁有没有权限第二分流。别挤在一起去不同的模型池。第三限速。别一家独大把资源吃干抹净。咱们用个比喻。这就好比高档小区的会所。租户 A 是 VIP租户 B 是普通住户。网关就是保安亭。保安先看你的门禁卡鉴权。然后看会所里哪个泳池人少负载均衡。最后看你是不是在泼水玩限流熔断。架构核心是“令牌桶 动态路由”。每个租户分配独立的令牌桶。流量进来先扣令牌。没令牌直接拒绝不废话。路由表是动态的。哪个模型响应快流量就切过去。graph TD Client[客户端请求] -- Gateway[API 网关] Gateway -- Auth[鉴权中心\n(租户身份识别)] Auth -- RateLimiter[限流器\n(令牌桶算法)] RateLimiter -- Router[动态路由引擎] Router -- ModelPoolA[模型池 A\n(高并发专用)] Router -- ModelPoolB[模型池 B\n(高精度专用)] ModelPoolA -- Response[返回结果] ModelPoolB -- Response这个设计的优势很明显。物理隔离互不干扰。逻辑隔离灵活配置。哪怕模型 A 挂了模型 B 还能接着跑。这就是高可用的底气。1.2 与同类方案的对比市面上方案不少。咱们挑两个主流的比比。一个是纯 Nginx 转发。一个是 Service Mesh。方案隔离能力扩展性开发成本适用场景纯 Nginx弱只能靠 IP低改配置要重载低简单内部测试Service Mesh强但太重高但运维难极高超大型微服务自研网关强细粒度高代码可控中企业级 SaaS说实话Service Mesh 是好东西。但对于咱们这种要快速迭代的业务太重了。自研网关虽然多写点代码。但逻辑全在自己手里。出问题好排查改需求快。这才是实战派的选择。二、快速上手别整那些复杂的部署。咱们先搞个最小可运行示例。用 Spring Cloud Gateway 做个 Demo。三分钟让你看到效果。首先引入依赖。不用多就网关和限流那两个包。然后配置个简单的路由。重点是这个 Filter。它是实现多租户隔离的关键。// 这是一个简化的全局过滤器示例 // 实际生产中要配合 Redis 使用 Component public class TenantIsolationFilter implements GlobalFilter { // 定义超时时间防止大模型卡死整个线程 private static final long TIMEOUT_MS 30000; Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. 从请求头里拿租户 ID // 别指望前端一定传要有默认值兜底 String tenantId exchange.getRequest().getHeaders().getFirst(X-Tenant-ID); if (tenantId null) { tenantId default_tenant; } // 2. 检查租户是否被封禁 // 这里模拟查数据库实际要查缓存 if (isTenantBanned(tenantId)) { // 直接返回 403别浪费资源 exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); return exchange.getResponse().setComplete(); } // 3. 执行限流逻辑 // 每个租户独立的令牌桶 if (!rateLimiter.tryAcquire(tenantId)) { // 令牌没了返回 429 exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); return exchange.getResponse().setComplete(); } // 4. 记录日志方便后续排查 log.info(租户 {} 发起请求路径{}, tenantId, exchange.getRequest().getPath()); // 5. 继续执行后续过滤器链 // 加上超时控制这是生产级代码的底线 return chain.filter(exchange).timeout(Duration.ofMillis(TIMEOUT_MS)); } // 模拟租户封禁检查 private boolean isTenantBanned(String tenantId) { // 实际逻辑return redisTemplate.opsForValue().get(banned: tenantId) ! null; return false; } }这段代码看着简单。但全是坑里长出来的经验。比如那个超时控制。大模型生成文本慢起来几十秒很正常。你不设超时线程池瞬间爆满。整个服务就僵死了。三、核心 API / 深水区3.1 核心方法速查网关里几个核心接口你得记牢。这是以后调优的抓手。方法名作用关键参数注意事项getTenantQuota获取租户额度租户 ID必须走缓存别查库routeToModel模型路由选择请求内容、负载支持权重配置recordUsage记录 Token 用量消耗数量异步写入别阻塞主线程circuitBreak熔断开关控制错误阈值半开状态要谨慎3.2 生产级配置配置这东西看着不起眼。出了事全是它的问题。超时时间别设太短。大模型不是秒杀接口。建议首包响应设 5 秒总时长设 60 秒。重试机制要有但不能无脑重。遇到 503 可以重遇到 400 别重。那是你参数错了重也没用。3.3 高级定制有些客户要私有化部署。模型就在他们本地。网关得支持“混合路由”。公网流量走云端模型。敏感数据走本地模型。这得靠请求内容分类。比如检测到“身份证号”关键词。直接路由到本地隐私模型。别为了省几个 Token把客户数据传出去了。那是违法的。四、实战演练来咱们模拟个真实场景。某教育公司有 1000 个学校租户。有的学校只有 10 个学生。有的学校有 10 万学生。如果不隔离大校把小校的资源抢光了。小校的用户体验极差。投诉电话能被打爆。我们用上面的网关架构。给每个学校配不同的 QPS 限制。大校给 1000 QPS。小校给 50 QPS。突发流量来了。大校虽然多但被限住了。小校虽然少但保证能用到。// 模拟路由分发逻辑 public ModelInstance selectModel(String tenantId, RequestContent content) { // 1. 获取租户等级 TenantLevel level tenantService.getLevel(tenantId); // 2. 根据等级选择模型池 // 金卡用户走高精度模型普通用户走快速模型 ModelPool pool (level TenantLevel.GOLD) ? highPrecisionPool : fastPool; // 3. 负载均衡算法 // 这里用加权轮询别用随机随机负载不均匀 ModelInstance instance pool.getWeightedRoundRobin(); // 4. 健康检查 // 如果这个实例最近报错多直接跳过 if (instance.getErrorCount() 5) { pool.markUnhealthy(instance); return selectModel(tenantId, content); // 递归选下一个 } return instance; }结果分析。实施后小租户的投诉率下降了 90%。大租户虽然偶尔被限流。但他们能接受因为整体稳定性高了。这就是架构的价值。不是让所有人跑得最快。是让所有人都不掉队。五、避坑指南与最佳实践这几年踩过的坑都给你列出来。省得你再去撞墙。技巧Token 计数要提前别等模型返回了再算 Token。请求发出去之前先估算一下。如果超过租户剩余额度直接拒绝。别浪费网络带宽去传个大文件结果返回个“额度不足”。⚠️警告长连接别复用太久大模型接口很多是 SSE服务器发送事件。长连接保持太久网关内存会爆。设置个最大连接时长比如 5 分钟。强制断开重连刷新一下状态。✅推荐全链路追踪请求进来给个 TraceID。贯穿网关、业务层、模型层。出了问题拿着 ID 一查就知道卡在哪。别靠猜猜是解决不了问题的。六、综合实战演示最后给个闭环的代码结构。这是咱们核心网关的服务类。包含了鉴权、限流、路由、熔断。你可以直接拿去改改就能用。Service public class LLMGatewayService { // 注入各个组件解耦 Autowired private AuthManager authManager; Autowired private RateLimiter rateLimiter; Autowired private ModelRouter modelRouter; Autowired private CircuitBreakerFactory breakerFactory; /** * 统一入口方法 * 处理大模型代理请求 */ public CompletableFutureResponse proxyRequest(Request request) { // 1. 异步处理别阻塞主线程 return CompletableFuture.supplyAsync(() - { // 2. 租户鉴权 // 失败直接抛异常被全局异常处理器捕获 String tenantId authManager.validate(request.getHeader(Authorization)); // 3. 额度检查 // 用 Redis 原子操作防止超卖 if (!rateLimiter.checkQuota(tenantId, request.getTokenCount())) { throw new BusinessException(租户额度不足请充值); } // 4. 模型路由 // 获取可用的模型实例 ModelInstance instance modelRouter.route(tenantId, request); // 5. 熔断保护 // 如果模型服务不稳定自动熔断返回降级结果 CircuitBreaker breaker breakerFactory.create(model-circuit); return breaker.run( () - instance.call(request), // 正常调用 throwable - fallbackResponse() // 降级逻辑 ); }).exceptionally(ex - { // 统一异常处理记录日志 log.error(网关处理异常, ex); return errorResponse(ex.getMessage()); }); } // 降级返回别让用户干等 private Response fallbackResponse() { return Response.builder() .code(503) .message(服务繁忙请稍后再试) .build(); } }这段代码看着清爽。但背后是无数个夜班的积累。每个注释都是血泪史。比如那个exceptionally。以前我们没加一个空指针异常整个线程池挂掉。加了之后虽然请求失败了。但服务还活着。这就是区别。七、总结架构设计没有银弹。只有最适合当下业务的方案。高并发和多租户隔离核心就两点。一是资源配额要切分清楚。二是故障边界要隔离开来。别追求一开始就完美。先跑通再优化。监控报警比代码逻辑更重要。看不见的问题才是最大的问题。这套方案我们已经在生产环境跑了半年。扛住了双 11 的压力。目前看来还算稳定。希望能帮到你。如果有问题欢迎在评论区留言。咱们一起交流。
【Token限流计费系列】第2讲:大模型把公司带宽跑爆!记一次多租户网关架构重构
【Token限流计费系列】第2讲大模型把公司带宽跑爆记一次多租户网关架构重构前言企业级大模型服务上线后单个租户的突发流量可能迅速耗尽带宽、连接池和模型调用配额。如果网关缺少租户隔离、限流和动态路由能力大客户活动、批量任务或异常脚本都会影响其他租户的稳定性。本文聚焦多租户大模型网关的架构重构拆解鉴权、路由、限流和资源隔离如何协同避免一个租户拖垮整个平台。一、底层原理1.1 核心机制大模型网关其实就是个“超级前台”。它要干三件事。第一认人。你是谁有没有权限第二分流。别挤在一起去不同的模型池。第三限速。别一家独大把资源吃干抹净。咱们用个比喻。这就好比高档小区的会所。租户 A 是 VIP租户 B 是普通住户。网关就是保安亭。保安先看你的门禁卡鉴权。然后看会所里哪个泳池人少负载均衡。最后看你是不是在泼水玩限流熔断。架构核心是“令牌桶 动态路由”。每个租户分配独立的令牌桶。流量进来先扣令牌。没令牌直接拒绝不废话。路由表是动态的。哪个模型响应快流量就切过去。graph TD Client[客户端请求] -- Gateway[API 网关] Gateway -- Auth[鉴权中心\n(租户身份识别)] Auth -- RateLimiter[限流器\n(令牌桶算法)] RateLimiter -- Router[动态路由引擎] Router -- ModelPoolA[模型池 A\n(高并发专用)] Router -- ModelPoolB[模型池 B\n(高精度专用)] ModelPoolA -- Response[返回结果] ModelPoolB -- Response这个设计的优势很明显。物理隔离互不干扰。逻辑隔离灵活配置。哪怕模型 A 挂了模型 B 还能接着跑。这就是高可用的底气。1.2 与同类方案的对比市面上方案不少。咱们挑两个主流的比比。一个是纯 Nginx 转发。一个是 Service Mesh。方案隔离能力扩展性开发成本适用场景纯 Nginx弱只能靠 IP低改配置要重载低简单内部测试Service Mesh强但太重高但运维难极高超大型微服务自研网关强细粒度高代码可控中企业级 SaaS说实话Service Mesh 是好东西。但对于咱们这种要快速迭代的业务太重了。自研网关虽然多写点代码。但逻辑全在自己手里。出问题好排查改需求快。这才是实战派的选择。二、快速上手别整那些复杂的部署。咱们先搞个最小可运行示例。用 Spring Cloud Gateway 做个 Demo。三分钟让你看到效果。首先引入依赖。不用多就网关和限流那两个包。然后配置个简单的路由。重点是这个 Filter。它是实现多租户隔离的关键。// 这是一个简化的全局过滤器示例 // 实际生产中要配合 Redis 使用 Component public class TenantIsolationFilter implements GlobalFilter { // 定义超时时间防止大模型卡死整个线程 private static final long TIMEOUT_MS 30000; Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. 从请求头里拿租户 ID // 别指望前端一定传要有默认值兜底 String tenantId exchange.getRequest().getHeaders().getFirst(X-Tenant-ID); if (tenantId null) { tenantId default_tenant; } // 2. 检查租户是否被封禁 // 这里模拟查数据库实际要查缓存 if (isTenantBanned(tenantId)) { // 直接返回 403别浪费资源 exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); return exchange.getResponse().setComplete(); } // 3. 执行限流逻辑 // 每个租户独立的令牌桶 if (!rateLimiter.tryAcquire(tenantId)) { // 令牌没了返回 429 exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS); return exchange.getResponse().setComplete(); } // 4. 记录日志方便后续排查 log.info(租户 {} 发起请求路径{}, tenantId, exchange.getRequest().getPath()); // 5. 继续执行后续过滤器链 // 加上超时控制这是生产级代码的底线 return chain.filter(exchange).timeout(Duration.ofMillis(TIMEOUT_MS)); } // 模拟租户封禁检查 private boolean isTenantBanned(String tenantId) { // 实际逻辑return redisTemplate.opsForValue().get(banned: tenantId) ! null; return false; } }这段代码看着简单。但全是坑里长出来的经验。比如那个超时控制。大模型生成文本慢起来几十秒很正常。你不设超时线程池瞬间爆满。整个服务就僵死了。三、核心 API / 深水区3.1 核心方法速查网关里几个核心接口你得记牢。这是以后调优的抓手。方法名作用关键参数注意事项getTenantQuota获取租户额度租户 ID必须走缓存别查库routeToModel模型路由选择请求内容、负载支持权重配置recordUsage记录 Token 用量消耗数量异步写入别阻塞主线程circuitBreak熔断开关控制错误阈值半开状态要谨慎3.2 生产级配置配置这东西看着不起眼。出了事全是它的问题。超时时间别设太短。大模型不是秒杀接口。建议首包响应设 5 秒总时长设 60 秒。重试机制要有但不能无脑重。遇到 503 可以重遇到 400 别重。那是你参数错了重也没用。3.3 高级定制有些客户要私有化部署。模型就在他们本地。网关得支持“混合路由”。公网流量走云端模型。敏感数据走本地模型。这得靠请求内容分类。比如检测到“身份证号”关键词。直接路由到本地隐私模型。别为了省几个 Token把客户数据传出去了。那是违法的。四、实战演练来咱们模拟个真实场景。某教育公司有 1000 个学校租户。有的学校只有 10 个学生。有的学校有 10 万学生。如果不隔离大校把小校的资源抢光了。小校的用户体验极差。投诉电话能被打爆。我们用上面的网关架构。给每个学校配不同的 QPS 限制。大校给 1000 QPS。小校给 50 QPS。突发流量来了。大校虽然多但被限住了。小校虽然少但保证能用到。// 模拟路由分发逻辑 public ModelInstance selectModel(String tenantId, RequestContent content) { // 1. 获取租户等级 TenantLevel level tenantService.getLevel(tenantId); // 2. 根据等级选择模型池 // 金卡用户走高精度模型普通用户走快速模型 ModelPool pool (level TenantLevel.GOLD) ? highPrecisionPool : fastPool; // 3. 负载均衡算法 // 这里用加权轮询别用随机随机负载不均匀 ModelInstance instance pool.getWeightedRoundRobin(); // 4. 健康检查 // 如果这个实例最近报错多直接跳过 if (instance.getErrorCount() 5) { pool.markUnhealthy(instance); return selectModel(tenantId, content); // 递归选下一个 } return instance; }结果分析。实施后小租户的投诉率下降了 90%。大租户虽然偶尔被限流。但他们能接受因为整体稳定性高了。这就是架构的价值。不是让所有人跑得最快。是让所有人都不掉队。五、避坑指南与最佳实践这几年踩过的坑都给你列出来。省得你再去撞墙。技巧Token 计数要提前别等模型返回了再算 Token。请求发出去之前先估算一下。如果超过租户剩余额度直接拒绝。别浪费网络带宽去传个大文件结果返回个“额度不足”。⚠️警告长连接别复用太久大模型接口很多是 SSE服务器发送事件。长连接保持太久网关内存会爆。设置个最大连接时长比如 5 分钟。强制断开重连刷新一下状态。✅推荐全链路追踪请求进来给个 TraceID。贯穿网关、业务层、模型层。出了问题拿着 ID 一查就知道卡在哪。别靠猜猜是解决不了问题的。六、综合实战演示最后给个闭环的代码结构。这是咱们核心网关的服务类。包含了鉴权、限流、路由、熔断。你可以直接拿去改改就能用。Service public class LLMGatewayService { // 注入各个组件解耦 Autowired private AuthManager authManager; Autowired private RateLimiter rateLimiter; Autowired private ModelRouter modelRouter; Autowired private CircuitBreakerFactory breakerFactory; /** * 统一入口方法 * 处理大模型代理请求 */ public CompletableFutureResponse proxyRequest(Request request) { // 1. 异步处理别阻塞主线程 return CompletableFuture.supplyAsync(() - { // 2. 租户鉴权 // 失败直接抛异常被全局异常处理器捕获 String tenantId authManager.validate(request.getHeader(Authorization)); // 3. 额度检查 // 用 Redis 原子操作防止超卖 if (!rateLimiter.checkQuota(tenantId, request.getTokenCount())) { throw new BusinessException(租户额度不足请充值); } // 4. 模型路由 // 获取可用的模型实例 ModelInstance instance modelRouter.route(tenantId, request); // 5. 熔断保护 // 如果模型服务不稳定自动熔断返回降级结果 CircuitBreaker breaker breakerFactory.create(model-circuit); return breaker.run( () - instance.call(request), // 正常调用 throwable - fallbackResponse() // 降级逻辑 ); }).exceptionally(ex - { // 统一异常处理记录日志 log.error(网关处理异常, ex); return errorResponse(ex.getMessage()); }); } // 降级返回别让用户干等 private Response fallbackResponse() { return Response.builder() .code(503) .message(服务繁忙请稍后再试) .build(); } }这段代码看着清爽。但背后是无数个夜班的积累。每个注释都是血泪史。比如那个exceptionally。以前我们没加一个空指针异常整个线程池挂掉。加了之后虽然请求失败了。但服务还活着。这就是区别。七、总结架构设计没有银弹。只有最适合当下业务的方案。高并发和多租户隔离核心就两点。一是资源配额要切分清楚。二是故障边界要隔离开来。别追求一开始就完美。先跑通再优化。监控报警比代码逻辑更重要。看不见的问题才是最大的问题。这套方案我们已经在生产环境跑了半年。扛住了双 11 的压力。目前看来还算稳定。希望能帮到你。如果有问题欢迎在评论区留言。咱们一起交流。