第 9 / 22 篇

第 8 章:先获得准入,再消耗共享资源

一个租户同时发来几百条长回答请求,会占据连接、线程和上游额度。请求次数限流只能控制其中一个维度;不同长度的提示和输出,成本可能差很多。准入层需要把身份、并发和预算放到同一个决策流程。

8.1 身份决定归属,正文表达需求

随书接口用 Bearer 凭证确定租户。请求正文可以描述 prompt 和能力需求,但不能自己声明“把费用记到另一个租户”。入口若接受客户端提交的 tenant_id,应把它当作需要授权校验的目标,而非已经可信的身份。

密钥映射和权限检查要在实际调用上游前完成。认证错误不应启动模型请求;权限拒绝也不应通过错误详情泄露其他租户的资源名称与余额。

8.2 准入检查需要原子性

先检查“余额足够”,释放锁,随后才扣预留,会留下竞争窗口。两个请求可能同时看到相同余额,然后都通过。并发槽位也有同样问题。

在单进程实验里,可以把检查与状态变化放在同一个受保护临界区。多进程部署需要数据库事务、原子存储操作或其他能够提供相应保证的机制。把 Python 锁替换为另一个机器上的锁,不能自动得到持久化与故障恢复能力。

一个概念流程如下:

验证凭证与输入
计算本次预留
在同一准入事务中:检查重复标识、并发和可用预算,再建立运行
事务成功后才联系上游
停止运行时,按结束证据分别处理并发与预算

8.3 拒绝通常比无限排队更诚实

队列不是免费容量。排队请求仍保存上下文并占据等待预算;客户端超时后,它可能已经失去继续执行的价值。若要排队,需要限定长度、最长等待时间、租户公平性和取消行为。

实验采用明确的并发限制和拒绝策略,没有实现公平排队器。这样可以直接检查“准入失败没有联系上游”。生产系统可以增加排队,但应把等待时间记入总期限,也要验证一个大租户不会长时间挤占所有位置。

8.4 重复标识的范围需要说明

request_id 适合关联日志和防止同一租户重复提交同一个实验运行。它不能天然替代业务任务标识或供应商幂等键。一个 ID 被拒绝为重复,也不表示先前任务已经成功,调用者仍需要查询原任务状态。

单进程保存的 ID 与运行记录需要容量和保留策略。教学服务运行时间短,记录保存在内存里;生产实现要处理进程重启、历史结果查询和记录清理,避免“永远保留”导致内存增长,或“过早删除”导致重复执行。

练习。 租户最大并发为 2,第一条请求已输出但断流,用量未知,本地线程已经结束。它应该继续占据并发槽位吗?预算预留呢?请分别回答并说明依据。