一、核心概念
1. 大模型推理队列
大模型推理队列是承载用户各类生成请求的缓冲调度载体。不同于普通Web接口,大模型单次推理消耗大量GPU显存与算力,无法瞬时并发处理海量请求。
队列的核心作用是把突发涌入的请求进行暂存、排队、有序分发,成为接入层与推理引擎之间的中间缓冲层。无论是对话问答、文案创作、代码生成、知识库问答,所有线上大模型接口请求,都必须经过推理队列流转调度。队列既可以基于单机内存实现,也可借助Redis、消息中间件搭建分布式全局推理队列,适配集群多节点场景。
- 本质:内存、分布式存储的请求缓冲区
- 作用:削峰填谷、保护模型推理引擎不被突发流量压垮
- 场景:对话问答、文本生成、代码补全等所有大模型接口请求
2. 队列积压
队列积压本质是请求入队速率持续超过模型推理出队处理速率。业务流量突发暴涨、长耗时请求占用资源、GPU算力瓶颈、无合理调度策略,都会引发积压。一旦形成积压,最直观表现就是用户侧响应延迟飙升、接口超时报错。严重情况下会造成推理进程负载过高、内存持续占用溢出、GPU利用率异常抖动。更会向上游链路传导压力,引发服务雪崩、接口级连锁故障,直接影响整体业务可用性。
一旦队列中等待请求持续堆积,最终导致:
- 请求响应超时,用户端504错误或等待超时
- 模型推理引擎过载、GPU 利用率异常
- 服务内存溢出、进程崩溃
- 级联故障,上游服务雪崩
3. 请求排队治理
请求排队治理是一套覆盖请求接入、排队、调度、执行、容错的全链路管控体系,针对队列积压的全流程优化手段。
核心涵盖推理队列限流、多级别优先级调度、长短请求拆分、阻塞队列溢出防护、大模型集群负载均衡五大核心模块。
治理的核心目标:
- 一是保证高可用、低延迟、公平调度、资源最大化利用,保障服务平稳不宕机;
- 二是区分请求优先级保障核心业务体验。
4. 大模型推理的核心特性
- 计算极度密集:大模型百亿、千亿参数特性,每一次token生成都伴随大量矩阵运算,远高于常规接口耗时。
- 请求耗时差异悬殊:短句问答仅需几百token,长文生成、报告撰写可达数千token,耗时差距可达数十倍。
- GPU显存资源独占:单卡可承载的并发推理数有严格上限,超出上限会直接出现显存溢出、推理崩溃。
- 业务无状态特征:单次推理任务相互独立,不依赖会话上下文绑定特定节点,天然适配集群负载均衡与任务迁移。
- 流式生成特性:大模型多采用SSE流式返回,长连接会占用连接资源,进一步加剧队列排队与资源占用压力。
5. 队列治理对大模型的意义
- 筑牢服务稳定性底座:通过限流、排队、溢出防护,从源头隔绝突发流量洪峰,避免推理集群被瞬间打垮。
- 分层保障业务体验:通过优先级调度,让付费业务、核心业务请求优先获得算力资源,不被普通流量挤占。
- 提升硬件资源利用率:合理拆分长短请求、均衡节点负载,让GPU始终处在合理负载区间,避免空闲或过载。
- 支撑业务弹性扩展:依托集群负载均衡架构,可横向新增推理节点,无需改造业务即可承接增长流量。
- 构建故障隔离能力:队列溢出、限流熔断可自动隔离异常流量,避免局部故障扩散为全局服务故障。
二、队列的成因
1. 队列核心数据结构
生产环境大模型服务普遍采用线程安全阻塞队列作为底层结构。阻塞队列具备两大核心特性:
- 队列已满时新请求入队阻塞或直接拒绝;
- 队列为空时推理工作线程自动阻塞等待新任务。
天然适配多线程并发消费场景,无需手动加锁即可保证请求读写安全。分布式场景下会基于Redis List、RabbitMQ、Kafka实现分布式阻塞队列,实现多节点共享排队任务。
简而言之,大模型服务主流使用阻塞队列,支持:
- 阻塞入队:队列满时,新请求阻塞等待或直接拒绝
- 阻塞出队:队列空时,推理线程阻塞等待新请求
- 线程安全:支持多线程并发读写,适配高并发场景
2. 队列积压的根本成因
- 流量层面:热点事件、营销活动、批量批量导入任务,会造成短时间请求量几倍飙升,超出常态处理能力。
- 请求层面:大量长文本生成任务持续占用推理算力,阻塞后续短请求,拉低整体队列吞吐。
- 资源层面:GPU卡数量不足、单卡显存偏小、模型量化程度低,导致单节点推理处理能力天生受限。
- 架构层面:采用简单FIFO无差别排队,无优先级、无长短拆分、无限流防护,流量来了只能被动堆积。
- 运维层面:节点负载不均、部分机器高负载部分空闲,整体集群算力没有被统一调度利用。
三、队列的执行流程
每一个环节都承担独立管控作用,接入层做前置过滤,队列做缓冲排队,调度器做资源分配,推理引擎做计算执行。
重要过程说明:
- 1. 请求接入:接收 HTTP/gRPC 请求,完成参数校验、鉴权
- 2. 入队操作:合法请求加入队列尾部,记录请求唯一 ID、创建时间
- 3. 等待调度:队列按规则管理等待请求
- 4. 推理执行:调度器从队列取出请求,发送给模型推理节点
- 5. 结果返回:推理完成后,结果回传给用户,请求从队列移除
整个流程闭环运行,保证请求有序流转,不会出现乱序、重复执行、资源抢占等问题。
四、核心治理技术
1. 推理队列限流
1.1 限流核心定义
限流是在请求接入队列之前,对单位时间内允许进入系统的请求总量做硬性管控。属于队列积压治理的第一道防线,从入口拦截超额流量,从根源避免队列被无限撑满。不是单纯拒绝请求,而是通过可控的流量整形,让进入队列的请求量匹配后端推理集群的处理能力。
1.2 主流限流算法
1.2.1 令牌桶算法
- 原理:固定速率生成令牌,请求必须获取令牌才能入队
- 优势:支持突发流量,平滑限制请求速率
- 适用:大模型对话、生成类接口,完美适配大模型忽高忽低的业务流量
1.2.2 漏桶算法
- 原理:请求匀速进入推理环节,固定速率流出到推理引擎
- 优势:严格匀速,保护模型不被瞬时流量冲击
- 适用:适合对延迟不敏感、追求平稳处理的离线生成任务
1.2.3 固定窗口/滑动窗口限流
- 原理:按时间窗口统计请求数,超过阈值直接拒绝
- 适用:接口级QPS硬性限制,适合对外公开接口做硬性访问频次管
1.3 关键技术细节
- 限流阈值不能随意设置,需要依据单GPU最大并发数、模型平均推理耗时、可容忍排队时长综合计算得出。
- 部署形态分为单机本地限流和集群分布式限流,单机适合小规模部署,分布式适合多节点集群统一控流。
- 限流触发后不建议直接返回原始报错,应配置友好降级提示、排队提示,提升用户使用感知。
- 同时配套监控告警,当限流触发频次飙升时,及时人工扩容或调整限流阈值。
- 固定窗口算法实现简单但存在临界流量突增问题,一般不建议用于大模型核心推理接口。
1.4 示例:令牌桶限流
基于令牌桶算法实现大模型API限流,通过容量控制突发流量、速率控制持续压力,演示突发请求拦截、令牌恢复、稳定流量三种场景下的限流效果与通过率统计。
import time import threading class TokenBucket: def __init__(self, capacity: int, rate: float): self.capacity = capacity # 令牌桶容量(最大并发) self.rate = rate # 每秒生成令牌数 self.tokens = capacity # 当前令牌数 self.last_time = time.time() self.lock = threading.Lock() self.total_requests = 0 # 总请求数 self.passed_requests = 0 # 通过请求数 self.rejected_requests = 0 # 拒绝请求数 def acquire(self, tokens=1) -> bool: """获取令牌,成功返回True,失败返回False""" with self.lock: now = time.time() # 计算时间差,生成新令牌 delta = now - self.last_time new_tokens = delta * self.rate self.tokens += new_tokens self.tokens = min(self.tokens, self.capacity) self.last_time = now self.total_requests += 1 if self.tokens >= tokens: self.tokens -= tokens self.passed_requests += 1 return True else: self.rejected_requests += 1 return False def get_stats(self): """获取限流统计信息""" return { "current_tokens": round(self.tokens, 2), "total_requests": self.total_requests, "passed": self.passed_requests, "rejected": self.rejected_requests, "pass_rate": f"{self.passed_requests / max(self.total_requests, 1) * 100:.1f}%" } # 大模型队列限流实例:每秒生成2个令牌,桶容量5(模拟GPU显存限制) llm_rate_limiter = TokenBucket(capacity=5, rate=2) def handle_llm_request(request_id: str, prompt: str = "", simulate_process: bool = True): """处理大模型请求""" timestamp = time.strftime("%H:%M:%S", time.localtime()) # 请求先获取令牌 if llm_rate_limiter.acquire(): print(f"[{timestamp}] 请求{request_id}:✓ 获取令牌成功 | 当前令牌: {llm_rate_limiter.tokens:.1f} | 进入推理队列") # 模拟推理耗时(可选,用于演示令牌消耗) if simulate_process: time.sleep(0.05) # 缩短处理时间,让限流效果更明显 return True else: print(f"[{timestamp}] 请求{request_id}:✗ 限流触发 | 当前令牌: {llm_rate_limiter.tokens:.1f} | 排队等待或拒绝") return False def burst_test_concurrent(): """并发突发测试:多线程同时请求""" import concurrent.futures print("=" * 60) print("【场景1】并发突发流量测试:20个请求同时到达") print("=" * 60) print(f"配置:令牌桶容量={llm_rate_limiter.capacity}, 生成速率={llm_rate_limiter.rate}/秒") print("-" * 60) results = {"passed": 0, "rejected": 0} def worker(i): return handle_llm_request(f"concurrent_{i}", simulate_process=False) # 使用线程池并发执行20个请求 with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor: futures = [executor.submit(worker, i) for i in range(20)] for future in concurrent.futures.as_completed(futures): if future.result(): results["passed"] += 1 else: results["rejected"] += 1 print("-" * 60) stats = llm_rate_limiter.get_stats() print(f"统计:总请求={stats['total_requests']}, 通过={stats['passed']}, 拒绝={stats['rejected']}, 通过率={stats['pass_rate']}") print(f"说明:并发请求中仅前{llm_rate_limiter.capacity}个获取令牌,其余被拒绝") # ===================== 测试场景 ===================== # 场景1:并发突发测试 burst_test_concurrent() print("\n" + "=" * 60) print("【场景2】令牌恢复测试:等待3秒后再次请求") print("=" * 60) # 先清空令牌 llm_rate_limiter.tokens = 0 print(f"当前令牌数已清空: {llm_rate_limiter.tokens:.1f}") time.sleep(3) # 等待令牌恢复 print(f"等待3秒后,当前令牌数: {llm_rate_limiter.tokens:.1f} (应恢复约{min(3 * llm_rate_limiter.rate, llm_rate_limiter.capacity)}个)") for i in range(8): handle_llm_request(f"recover_{i}") time.sleep(0.05) print("\n" + "=" * 60) print("【场景3】持续流量测试:请求速率 > 令牌生成速率") print("=" * 60) # 重置计数器 llm_rate_limiter.total_requests = 0 llm_rate_limiter.passed_requests = 0 llm_rate_limiter.rejected_requests = 0 llm_rate_limiter.tokens = llm_rate_limiter.capacity # 重置令牌 print(f"配置:令牌生成速率={llm_rate_limiter.rate}/秒,请求间隔=0.3秒(即约3.3请求/秒)") print("-" * 60) # 模拟20个请求,间隔0.3秒(请求速率 > 令牌生成速率) for i in range(20): handle_llm_request(f"steady_{i}") time.sleep(0.3) print("-" * 60) stats = llm_rate_limiter.get_stats() print(f"最终统计:总请求={stats['total_requests']}, 通过={stats['passed']}, 拒绝={stats['rejected']}, 通过率={stats['pass_rate']}") print(f"说明:当请求速率({1/0.3:.1f}/秒) > 令牌生成速率({llm_rate_limiter.rate}/秒)时,部分请求被拒绝") print("=" * 60)
输出结果:
============================================================
【场景1】并发突发流量测试:20个请求同时到达
============================================================
配置:令牌桶容量=5, 生成速率=2/秒
------------------------------------------------------------
[22:09:03] 请求concurrent_0:✓ 获取令牌成功 | 当前令牌: 4.0 | 进入推理队列
[22:09:03] 请求concurrent_1:✓ 获取令牌成功 | 当前令牌: 3.0 | 进入推理队列
[22:09:03] 请求concurrent_3:✓ 获取令牌成功 | 当前令牌: 1.0 | 进入推理队列
[22:09:03] 请求concurrent_2:✓ 获取令牌成功 | 当前令牌: 2.0 | 进入推理队列
[22:09:03] 请求concurrent_5:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_6:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_4:✓ 获取令牌成功 | 当前令牌: 0.0 | 进入推理队列
[22:09:03] 请求concurrent_9:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_8:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_11:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_7:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_13:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_15:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_16:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_10:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_19:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_12:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_18:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_17:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
[22:09:03] 请求concurrent_14:✗ 限流触发 | 当前令牌: 0.0 | 排队等待或拒绝
------------------------------------------------------------
统计:总请求=20, 通过=5, 拒绝=15, 通过率=25.0%
说明:并发请求中仅前5个获取令牌,其余被拒绝
============================================================
【场景2】令牌恢复测试:等待3秒后再次请求
============================================================
当前令牌数已清空: 0.0
等待3秒后,当前令牌数: 0.0 (应恢复约5个)
[22:09:06] 请求recover_0:✓ 获取令牌成功 | 当前令牌: 4.0 | 进入推理队列
[22:09:06] 请求recover_1:✓ 获取令牌成功 | 当前令牌: 3.2 | 进入推理队列
[22:09:06] 请求recover_2:✓ 获取令牌成功 | 当前令牌: 2.4 | 进入推理队列
[22:09:06] 请求recover_3:✓ 获取令牌成功 | 当前令牌: 1.6 | 进入推理队列
[22:09:07] 请求recover_4:✓ 获取令牌成功 | 当前令牌: 0.8 | 进入推理队列
[22:09:07] 请求recover_5:✓ 获取令牌成功 | 当前令牌: 0.1 | 进入推理队列
[22:09:07] 请求recover_6:✗ 限流触发 | 当前令牌: 0.3 | 排队等待或拒绝
[22:09:07] 请求recover_7:✗ 限流触发 | 当前令牌: 0.4 | 排队等待或拒绝
============================================================
【场景3】持续流量测试:请求速率 > 令牌生成速率
============================================================
配置:令牌生成速率=2/秒,请求间隔=0.3秒(即约3.3请求/秒)
------------------------------------------------------------
[22:09:07] 请求steady_0:✓ 获取令牌成功 | 当前令牌: 4.0 | 进入推理队列
[22:09:07] 请求steady_1:✓ 获取令牌成功 | 当前令牌: 3.7 | 进入推理队列
[22:09:08] 请求steady_2:✓ 获取令牌成功 | 当前令牌: 3.4 | 进入推理队列
[22:09:08] 请求steady_3:✓ 获取令牌成功 | 当前令牌: 3.1 | 进入推理队列
[22:09:08] 请求steady_4:✓ 获取令牌成功 | 当前令牌: 2.8 | 进入推理队列
[22:09:09] 请求steady_5:✓ 获取令牌成功 | 当前令牌: 2.5 | 进入推理队列
[22:09:09] 请求steady_6:✓ 获取令牌成功 | 当前令牌: 2.2 | 进入推理队列
[22:09:09] 请求steady_7:✓ 获取令牌成功 | 当前令牌: 1.9 | 进入推理队列
[22:09:10] 请求steady_8:✓ 获取令牌成功 | 当前令牌: 1.6 | 进入推理队列
[22:09:10] 请求steady_9:✓ 获取令牌成功 | 当前令牌: 1.3 | 进入推理队列
[22:09:10] 请求steady_10:✓ 获取令牌成功 | 当前令牌: 1.0 | 进入推理队列
[22:09:11] 请求steady_11:✓ 获取令牌成功 | 当前令牌: 0.7 | 进入推理队列
[22:09:11] 请求steady_12:✓ 获取令牌成功 | 当前令牌: 0.4 | 进入推理队列
[22:09:11] 请求steady_13:✓ 获取令牌成功 | 当前令牌: 0.2 | 进入推理队列
[22:09:12] 请求steady_14:✗ 限流触发 | 当前令牌: 0.9 | 排队等待或拒绝
[22:09:12] 请求steady_15:✓ 获取令牌成功 | 当前令牌: 0.5 | 进入推理队列
[22:09:12] 请求steady_16:✓ 获取令牌成功 | 当前令牌: 0.2 | 进入推理队列
[22:09:13] 请求steady_17:✗ 限流触发 | 当前令牌: 0.9 | 排队等待或拒绝
[22:09:13] 请求steady_18:✓ 获取令牌成功 | 当前令牌: 0.5 | 进入推理队列
[22:09:13] 请求steady_19:✓ 获取令牌成功 | 当前令牌: 0.2 | 进入推理队列
------------------------------------------------------------
最终统计:总请求=20, 通过=18, 拒绝=2, 通过率=90.0%
说明:当请求速率(3.3/秒) > 令牌生成速率(2/秒)时,部分请求被拒绝
============================================================
2. 请求优先级调度
2.1 优先级调度核心定义
摒弃传统FIFO先进先出的单一规则,为不同业务、不同用户类型的请求设置等级权重。调度器优先从高优先级队列取任务执行,保障高价值业务优先占用算力资源。解决普通请求挤占核心业务、低价值批量任务阻塞实时对话请求的痛点。
2.2 大模型请求优先级划分标准
- P0 最高优先级:企业付费客户、核心生产业务接口、实时人机交互、高可用刚需场景。
- P1 中等优先级:普通注册用户、内部办公系统、常规文案生成、非紧急业务请求。
- P2 最低优先级:免费游客用户、离线批量生成、模型测试任务、后台异步数据处理任务。
可根据业务实际场景扩展更多优先级等级,适配复杂业务分层架构。
2.3 多级队列调度运行原理
- 系统为每一级优先级单独创建独立队列,物理隔离不同等级请求。
- 调度器按照优先级从高到低依次轮询队列,高优队列有任务就优先执行。
- 只有高优队列为空时,才会调度低优先级队列任务。
- 同时加入饥饿避免机制,低优请求等待超时后自动临时提升优先级,防止永久得不到调度。
2.4 技术应用价值
- 实现算力资源按业务价值倾斜,付费与核心业务体验不受普通流量影响。
- 隔离离线批量任务与实时交互任务,避免批量任务拖垮整体服务延迟。
- 便于运营侧做差异化服务,基于优先级等级实现用户权益分层。
2.5 示例:优先级队列
基于多级队列实现大模型请求优先级调度,VIP用户请求优先于普通用户和普通用户优先于免费用户处理,确保高价值用户在突发流量下仍能获得及时响应。
import queue import time import threading from enum import Enum class PriorityLevel(Enum): """优先级等级""" VIP = 0 # 最高优先级:VIP用户/付费用户 NORMAL = 1 # 普通优先级:普通用户 FREE = 2 # 最低优先级:免费试用用户 class PriorityLLMQueue: def __init__(self, maxsize: int = 100): """ 多级优先级队列 maxsize: 每级队列最大容量 """ # 多级队列:0=VIP, 1=普通, 2=免费 self.queues = { PriorityLevel.VIP: queue.Queue(maxsize=maxsize), PriorityLevel.NORMAL: queue.Queue(maxsize=maxsize), PriorityLevel.FREE: queue.Queue(maxsize=maxsize) } self.stats = { PriorityLevel.VIP: {"enqueued": 0, "dequeued": 0}, PriorityLevel.NORMAL: {"enqueued": 0, "dequeued": 0}, PriorityLevel.FREE: {"enqueued": 0, "dequeued": 0} } self.lock = threading.Lock() def enqueue(self, request: dict, priority: PriorityLevel) -> bool: """ 请求入队 priority: PriorityLevel.VIP/NORMAL/FREE """ try: self.queues[priority].put_nowait(request) with self.lock: self.stats[priority]["enqueued"] += 1 return True except queue.Full: print(f"[警告] 优先级{priority.name}队列已满,请求{request['id']}被拒绝") return False def dequeue(self) -> tuple: """ 优先调度高优先级请求 返回: (request, priority) 或 (None, None) """ for priority in [PriorityLevel.VIP, PriorityLevel.NORMAL, PriorityLevel.FREE]: try: request = self.queues[priority].get_nowait() with self.lock: self.stats[priority]["dequeued"] += 1 return request, priority except queue.Empty: continue return None, None def get_queue_sizes(self) -> dict: """获取各级队列当前大小""" return { priority.name: self.queues[priority].qsize() for priority in PriorityLevel } def get_stats(self) -> dict: """获取统计信息""" return { priority.name: { "队列长度": self.queues[priority].qsize(), "已入队": self.stats[priority]["enqueued"], "已处理": self.stats[priority]["dequeued"] } for priority in PriorityLevel } # ===================== 测试场景 ===================== def test_basic_priority(): """基础优先级调度测试""" print("=" * 60) print("【场景1】基础优先级调度测试") print("=" * 60) print("说明:VIP用户优先于普通用户,普通用户优先于免费用户") print("-" * 60) llm_queue = PriorityLLMQueue() # 模拟混合请求入队(按乱序入队) requests = [ {"id": "free_user_1", "prompt": "你好"}, {"id": "vip_user_1", "prompt": "紧急问题"}, {"id": "normal_user_1", "prompt": "普通咨询"}, {"id": "free_user_2", "prompt": "谢谢"}, {"id": "vip_user_2", "prompt": "重要业务"}, {"id": "normal_user_2", "prompt": "一般问题"}, ] priorities = [ PriorityLevel.FREE, PriorityLevel.VIP, PriorityLevel.NORMAL, PriorityLevel.FREE, PriorityLevel.VIP, PriorityLevel.NORMAL ] print("【入队阶段】") for req, prio in zip(requests, priorities): llm_queue.enqueue(req, prio) print(f"\n当前队列状态: {llm_queue.get_queue_sizes()}") print("\n【调度执行阶段】") processed = [] while True: req, prio = llm_queue.dequeue() if not req: break print(f"[处理] {req['id']} (优先级: {prio.name}) - 提示: {req['prompt']}") processed.append((req['id'], prio.name)) time.sleep(0.2) print(f"\n处理顺序: {' -> '.join([p[0] for p in processed])}") print(f"优先级顺序: {' -> '.join([p[1] for p in processed])}") print("-" * 60) def test_burst_priority(): """突发流量下的优先级保障测试""" print("\n" + "=" * 60) print("【场景2】突发流量下的优先级保障") print("=" * 60) print("说明:大量免费用户请求涌入时,VIP用户请求仍能被优先处理") print("-" * 60) llm_queue = PriorityLLMQueue() # 模拟突发:10个免费用户 + 2个VIP用户 print("【突发入队】10个免费用户请求 + 2个VIP用户请求") for i in range(10): llm_queue.enqueue({"id": f"free_user_{i}", "prompt": f"免费问题{i}"}, PriorityLevel.FREE) # VIP用户稍后到达 time.sleep(0.1) llm_queue.enqueue({"id": "vip_user_urgent", "prompt": "紧急业务"}, PriorityLevel.VIP) llm_queue.enqueue({"id": "vip_user_important", "prompt": "重要咨询"}, PriorityLevel.VIP) print(f"\n当前队列状态: {llm_queue.get_queue_sizes()}") print("\n【调度执行】") processed = [] for i in range(12): req, prio = llm_queue.dequeue() if req: print(f"[{i+1:2d}] {req['id']:20s} | 优先级: {prio.name:6s}") processed.append(prio.name) # 统计 vip_count = processed.count("VIP") free_count = processed.count("FREE") print(f"\n统计:VIP请求处理{vip_count}个(占比{vip_count/len(processed)*100:.1f}%)") print(f" 免费请求处理{free_count}个(占比{free_count/len(processed)*100:.1f}%)") print("说明:虽然免费请求先入队,但VIP请求被优先处理") print("-" * 60) def test_queue_full(): """队列满时的处理测试""" print("\n" + "=" * 60) print("【场景3】队列容量限制测试") print("=" * 60) print("说明:每级队列容量有限,防止无限堆积") print("-" * 60) # 创建小容量队列 llm_queue = PriorityLLMQueue(maxsize=3) print("【填满VIP队列】") for i in range(5): success = llm_queue.enqueue({"id": f"vip_user_{i}"}, PriorityLevel.VIP) print(f" VIP请求{i}: {'入队成功' if success else '入队失败(队列满)'}") print(f"\n最终队列状态:\n{llm_queue.get_stats()}") print("-" * 60) # ===================== 运行所有测试 ===================== if __name__ == "__main__": test_basic_priority() test_burst_priority() test_queue_full() print("\n" + "=" * 60) print("测试完成!") print("=" * 60)
输出结果:
============================================================
【场景1】基础优先级调度测试
============================================================
说明:VIP用户优先于普通用户,普通用户优先于免费用户
------------------------------------------------------------
【入队阶段】
当前队列状态: {'VIP': 2, 'NORMAL': 2, 'FREE': 2}
【调度执行阶段】
[处理] vip_user_1 (优先级: VIP) - 提示: 紧急问题
[处理] vip_user_2 (优先级: VIP) - 提示: 重要业务
[处理] normal_user_1 (优先级: NORMAL) - 提示: 普通咨询
[处理] normal_user_2 (优先级: NORMAL) - 提示: 一般问题
[处理] free_user_1 (优先级: FREE) - 提示: 你好
[处理] free_user_2 (优先级: FREE) - 提示: 谢谢
处理顺序: vip_user_1 -> vip_user_2 -> normal_user_1 -> normal_user_2 -> free_user_1 -> free_user_2
优先级顺序: VIP -> VIP -> NORMAL -> NORMAL -> FREE -> FREE
------------------------------------------------------------
============================================================
【场景2】突发流量下的优先级保障
============================================================
说明:大量免费用户请求涌入时,VIP用户请求仍能被优先处理
------------------------------------------------------------
【突发入队】10个免费用户请求 + 2个VIP用户请求
当前队列状态: {'VIP': 2, 'NORMAL': 0, 'FREE': 10}
【调度执行】
[ 1] vip_user_urgent | 优先级: VIP
[ 2] vip_user_important | 优先级: VIP
[ 3] free_user_0 | 优先级: FREE
[ 4] free_user_1 | 优先级: FREE
[ 5] free_user_2 | 优先级: FREE
[ 6] free_user_3 | 优先级: FREE
[ 7] free_user_4 | 优先级: FREE
[ 8] free_user_5 | 优先级: FREE
[ 9] free_user_6 | 优先级: FREE
[10] free_user_7 | 优先级: FREE
[11] free_user_8 | 优先级: FREE
[12] free_user_9 | 优先级: FREE
统计:VIP请求处理2个(占比16.7%)
免费请求处理10个(占比83.3%)
说明:虽然免费请求先入队,但VIP请求被优先处理
------------------------------------------------------------
============================================================
【场景3】队列容量限制测试
============================================================
说明:每级队列容量有限,防止无限堆积
------------------------------------------------------------
【填满VIP队列】
VIP请求0: 入队成功
VIP请求1: 入队成功
VIP请求2: 入队成功
[警告] 优先级VIP队列已满,请求vip_user_3被拒绝
VIP请求3: 入队失败(队列满)
[警告] 优先级VIP队列已满,请求vip_user_4被拒绝
VIP请求4: 入队失败(队列满)
最终队列状态:
{'VIP': {'队列长度': 3, '已入队': 3, '已处理': 0}, 'NORMAL': {'队列长度': 0, '已入队': 0, '已处理': 0}, 'FREE': {'队列长度': 0, '已入队': 0, '已处理': 0}}
------------------------------------------------------------
============================================================
测试完成!
============================================================
3. 长短请求拆分
3.1 长短请求拆分核心定义
依据输入Prompt长度、最大生成Token数、预估推理耗时,自动将请求划分为短请求与长请求两类。分别送入独立队列、分配专属推理资源,彻底解决长请求阻塞短请求的经典问题。
3.2 拆分判定依据
以Token数量为核心判定标准,结合模型推理速度预估整体耗时。
- 短请求:输出Token控制在512以内,推理耗时通常1秒以内,适用于问答、短句摘要、简单咨询。
- 长请求:输出Token超过2048,推理耗时可达数秒至十几秒,适用于长文创作、报告生成、知识库长篇问答。
系统支持动态阈值,根据集群实时负载自动调整长短请求划分边界,适配流量波动。
3.3 拆分完整执行流程
- 1. 请求接入后自动解析Prompt输入长度与用户设定max_tokens参数。
- 2. 通过内置公式预估整体推理耗时,自动打上长、短请求标签。
- 3. 短请求进入低延迟专属队列,优先调度空闲轻负载推理节点。
- 4. 长请求进入独立长任务队列,分配专属GPU资源,不挤占实时短请求算力。
- 5. 调度中心实时监控两类队列堆积情况,动态调整节点分配比例。
3.4 技术应用价值
- 大幅缩短普通短问答请求的响应延迟,优化绝大多数用户的实时体验。
- 长请求集中批量调度,提升大显存GPU的资源利用率。
- 实现任务物理隔离,长短请求互不干扰,整体集群吞吐能力显著提升。
3.5 示例:长短请求拆分
基于双队列架构实现大模型请求长短拆分,短请求(≤512 token)进入快速通道优先处理,长请求进入慢速通道独立执行,避免长文本生成阻塞短文本响应,提升整体服务吞吐量与用户体验。
import queue import threading import time from dataclasses import dataclass # 定义请求结构体(大模型标准请求格式) class LLMRequest: req_id: str prompt: str max_tokens: int create_time: float = None def __post_init__(self): self.create_time = time.time() class LLMLongShortRequestSplitter: def __init__(self, short_token_threshold=512): """ 长短请求拆分器 :param short_token_threshold: 短请求最大token数,超过即为长请求 """ self.threshold = short_token_threshold # 两个独立队列:短请求队列、长请求队列 self.short_queue = queue.Queue(maxsize=50) self.long_queue = queue.Queue(maxsize=30) # 启动独立消费者 self._start_consumers() def _estimate_time(self, max_tokens): """模拟大模型推理耗时预估:token越多耗时越长""" base_time = 0.2 token_time = max_tokens * 0.015 return round(base_time + token_time, 2) def classify_request(self, req: LLMRequest): """请求分类:自动打入长/短队列""" estimated_cost = self._estimate_time(req.max_tokens) is_short = req.max_tokens <= self.threshold if is_short: try: self.short_queue.put_nowait(req) print(f"[短请求] 入队 | ID={req.req_id} | tokens={req.max_tokens} | 预估耗时={estimated_cost}s") except queue.Full: print(f"[短请求队列溢出] 拒绝请求 {req.req_id}") else: try: self.long_queue.put_nowait(req) print(f"[长请求] 入队 | ID={req.req_id} | tokens={req.max_tokens} | 预估耗时={estimated_cost}s") except queue.Full: print(f"[长请求队列溢出] 拒绝请求 {req.req_id}") def _consume_short_request(self): """短请求消费者:低延迟、快速处理""" while True: try: req = self.short_queue.get(timeout=1) cost = self._estimate_time(req.max_tokens) print(f"[短请求执行] ID={req.req_id} | 推理耗时={cost}s") time.sleep(cost) self.short_queue.task_done() except queue.Empty: continue def _consume_long_request(self): """长请求消费者:慢速、不影响短请求""" while True: try: req = self.long_queue.get(timeout=1) cost = self._estimate_time(req.max_tokens) print(f"[长请求执行] ID={req.req_id} | 推理耗时={cost}s") time.sleep(cost) self.long_queue.task_done() except queue.Empty: continue def _start_consumers(self): """启动消费者线程""" # 短请求用2个线程(低延迟、高吞吐) threading.Thread(target=self._consume_short_request, daemon=True).start() threading.Thread(target=self._consume_short_request, daemon=True).start() # 长请求用1个线程(避免占用过多资源) threading.Thread(target=self._consume_long_request, daemon=True).start() # ====================== 测试 ====================== if __name__ == "__main__": # 初始化拆分器:<=512 token 为短请求 splitter = LLMLongShortRequestSplitter(short_token_threshold=512) # 模拟一批混合长短请求 test_requests = [ LLMRequest("req_001", "你好", max_tokens=64), LLMRequest("req_002", "写一篇人工智能综述", max_tokens=2048), LLMRequest("req_003", "什么是大模型", max_tokens=128), LLMRequest("req_004", "生成1万字产品文档", max_tokens=10000), LLMRequest("req_005", "介绍Python", max_tokens=256), LLMRequest("req_006", "写一份年度总结报告", max_tokens=3000), ] print("=== 开始提交请求 ===") for req in test_requests: splitter.classify_request(req) time.sleep(0.2) # 保持程序运行 input("\n按回车退出\n")
输出结果:
=== 开始提交请求 ===
[短请求] 入队 | ID=req_001 | tokens=64 | 预估耗时=1.16s
[短请求执行] ID=req_001 | 推理耗时=1.16s
[长请求] 入队 | ID=req_002 | tokens=2048 | 预估耗时=30.92s
[长请求执行] ID=req_002 | 推理耗时=30.92s
[短请求] 入队 | ID=req_003 | tokens=128 | 预估耗时=2.12s
[短请求执行] ID=req_003 | 推理耗时=2.12s
[长请求] 入队 | ID=req_004 | tokens=10000 | 预估耗时=150.2s
[短请求] 入队 | ID=req_005 | tokens=256 | 预估耗时=4.04s
[长请求] 入队 | ID=req_006 | tokens=3000 | 预估耗时=45.2s
[短请求执行] ID=req_005 | 推理耗时=4.04s
[长请求执行] ID=req_004 | 推理耗时=150.2s
[长请求执行] ID=req_006 | 推理耗时=45.2s
4. 阻塞队列溢出防护
4.1 溢出防护核心定义
为阻塞推理队列设置最大容量上限,当排队请求达到阈值时,执行预设保护策略。防止队列无限制堆积占用内存,避免内存泄漏、进程 OOM 崩溃、服务不可用等严重故障。是大模型服务兜底稳定性的关键机制。
4.2 主流溢出防护策略
- 固定容量限制:预先设定队列最大排队数,达到上限不再接受新请求入队。
- 快速拒绝策略:队列已满直接返回排队过载提示,不占用额外连接与内存资源。
- 请求超时丢弃:请求在队列中等待超过指定时长,自动丢弃并返回超时,避免无效常驻排队。
- 低优优先丢弃:队列溢出时优先剔除P2低优先级请求,保全高价值业务正常排队。
- 联动告警熔断:队列使用率达到80%触发预警,100%触发接口局部熔断,限制流量接入。
4.3 关键技术细节
- 队列容量根据平均推理耗时、可容忍最大等待人数综合测算设定,不宜过大也不宜过小。
- 队列内部只存储请求核心元数据,不存放完整Prompt与大体积参数,节约内存占用。
- 溢出防护需和限流、降级联动,单一防护手段无法抵御大流量冲击。
- 全程埋点监控队列长度、排队时长、溢出次数,为后续阈值调优提供数据依据。
4.4 示例:阻塞队列溢出防护
基于阻塞队列实现大模型请求溢出防护,队列满时低优先级请求直接丢弃、高优先级请求限时等待,配合80%容量预警与双线程消费,确保服务高负载下核心请求仍能被处理。
import queue import threading import time from dataclasses import dataclass # 定义大模型请求结构 class LLMRequest: req_id: str priority: int # 0最高 1中等 2最低 prompt: str create_time: float = None def __post_init__(self): self.create_time = time.time() class LLMBlockQueueOverflowProtect: def __init__(self, max_queue_size=30, wait_timeout=5): """ 阻塞队列溢出防护初始化 :param max_queue_size: 队列最大容量 :param wait_timeout: 请求队列最大等待超时(秒) """ self.max_queue_size = max_queue_size self.wait_timeout = wait_timeout # 线程安全阻塞队列 self.block_queue = queue.Queue(maxsize=self.max_queue_size) # 启动推理消费线程 self._start_consumer() def get_queue_usage(self): """获取队列使用率""" current_size = self.block_queue.qsize() usage = current_size / self.max_queue_size return current_size, round(usage * 100, 2) def alert_warning(self): """队列告警:使用率80%预警,100%溢出告警""" _, usage = self.get_queue_usage() if usage >= 100: print(f"【严重告警】队列已满!使用率:{usage}%,触发溢出防护") elif usage >= 80: print(f"【预警】队列即将积压,使用率:{usage}%") def try_enqueue(self, req: LLMRequest) -> bool: """ 尝试入队 + 溢出防护 返回True:入队成功 False:入队被拒绝/超时 """ self.alert_warning() current_size, _ = self.get_queue_usage() # 策略1:队列已满,低优先级直接丢弃 if current_size >= self.max_queue_size: if req.priority >= 1: print(f"【溢出丢弃】队列已满,低优请求丢弃 ID:{req.req_id} 优先级:{req.priority}") return False else: # 高优请求尝试限时等待入队 try: self.block_queue.put(req, timeout=self.wait_timeout) print(f"【高优插队】高优先级请求限时等待入队成功 ID:{req.req_id}") return True except queue.Full: print(f"【溢出拒绝】高优请求等待超时,入队失败 ID:{req.req_id}") return False # 队列未满,正常入队 try: self.block_queue.put(req) print(f"【正常入队】ID:{req.req_id} 优先级:{req.priority} 队列当前:{current_size+1}/{self.max_queue_size}") return True except Exception as e: print(f"【入队异常】ID:{req.req_id} 错误:{str(e)}") return False def _consumer_loop(self): """推理消费线程:模拟LLM推理耗时""" while True: try: # 从阻塞队列取任务,空则阻塞 req = self.block_queue.get(timeout=2) # 模拟推理耗时 infer_cost = 0.5 + (req.priority * 0.3) time.sleep(infer_cost) print(f"【推理完成】ID:{req.req_id} 耗时:{infer_cost}s") self.block_queue.task_done() except queue.Empty: continue def _start_consumer(self): # 启动2个推理消费线程 for _ in range(2): t = threading.Thread(target=self._consumer_loop, daemon=True) t.start() # ====================== 测试演示 ====================== if __name__ == "__main__": # 初始化:队列最大容量30,请求最大等待超时5秒 protect_queue = LLMBlockQueueOverflowProtect(max_queue_size=30, wait_timeout=5) # 模拟连续涌入40个请求,超过队列容量,触发溢出防护 for i in range(40): # 随机优先级 0/1/2 prio = i % 3 req = LLMRequest( req_id=f"req_{i:03d}", priority=prio, prompt=f"测试请求内容{i}" ) protect_queue.try_enqueue(req) time.sleep(0.1) # 保持程序运行观察消费过程 input("\n回车退出程序\n")
输出结果:
【正常入队】ID:req_000 优先级:0 队列当前:1/30
【正常入队】ID:req_001 优先级:1 队列当前:1/30
【正常入队】ID:req_002 优先级:2 队列当前:1/30
【正常入队】ID:req_003 优先级:0 队列当前:2/30
【正常入队】ID:req_004 优先级:1 队列当前:3/30
【推理完成】ID:req_000 耗时:0.5s
【正常入队】ID:req_005 优先级:2 队列当前:3/30
【正常入队】ID:req_006 优先级:0 队列当前:4/30
【正常入队】ID:req_007 优先级:1 队列当前:5/30
【正常入队】ID:req_008 优先级:2 队列当前:6/30
【推理完成】ID:req_001 耗时:0.8s
【正常入队】ID:req_009 优先级:0 队列当前:6/30
【正常入队】ID:req_010 优先级:1 队列当前:7/30
【正常入队】ID:req_011 优先级:2 队列当前:8/30
【正常入队】ID:req_012 优先级:0 队列当前:9/30
【正常入队】ID:req_013 优先级:1 队列当前:10/30
【推理完成】ID:req_003 耗时:0.5s
【正常入队】ID:req_014 优先级:2 队列当前:10/30
【正常入队】ID:req_015 优先级:0 队列当前:11/30
【推理完成】ID:req_002 耗时:1.1s
【正常入队】ID:req_016 优先级:1 队列当前:11/30
【正常入队】ID:req_017 优先级:2 队列当前:12/30
【正常入队】ID:req_018 优先级:0 队列当前:13/30
【正常入队】ID:req_019 优先级:1 队列当前:14/30
【正常入队】ID:req_020 优先级:2 队列当前:15/30
【正常入队】ID:req_021 优先级:0 队列当前:16/30
【推理完成】ID:req_004 耗时:0.8s
【正常入队】ID:req_022 优先级:1 队列当前:16/30
【正常入队】ID:req_023 优先级:2 队列当前:17/30
【正常入队】ID:req_024 优先级:0 队列当前:18/30
【正常入队】ID:req_025 优先级:1 队列当前:19/30
【正常入队】ID:req_026 优先级:2 队列当前:20/30
【推理完成】ID:req_006 耗时:0.5s
【推理完成】ID:req_005 耗时:1.1s
【正常入队】ID:req_027 优先级:0 队列当前:19/30
【正常入队】ID:req_028 优先级:1 队列当前:20/30
【正常入队】ID:req_029 优先级:2 队列当前:21/30
【正常入队】ID:req_030 优先级:0 队列当前:22/30
【正常入队】ID:req_031 优先级:1 队列当前:23/30
【正常入队】ID:req_032 优先级:2 队列当前:24/30
【预警】队列即将积压,使用率:80.0%
【正常入队】ID:req_033 优先级:0 队列当前:25/30
【预警】队列即将积压,使用率:83.33%
【正常入队】ID:req_034 优先级:1 队列当前:26/30
【推理完成】ID:req_007 耗时:0.8s
【预警】队列即将积压,使用率:83.33%
【正常入队】ID:req_035 优先级:2 队列当前:26/30
【预警】队列即将积压,使用率:86.67%
【正常入队】ID:req_036 优先级:0 队列当前:27/30
【预警】队列即将积压,使用率:90.0%
【正常入队】ID:req_037 优先级:1 队列当前:28/30
【推理完成】ID:req_008 耗时:1.1s
【预警】队列即将积压,使用率:90.0%
【正常入队】ID:req_038 优先级:2 队列当前:28/30
【预警】队列即将积压,使用率:93.33%
【正常入队】ID:req_039 优先级:0 队列当前:29/30
【推理完成】ID:req_009 耗时:0.5s
【推理完成】ID:req_010 耗时:0.8s
【推理完成】ID:req_012 耗时:0.5s
【推理完成】ID:req_011 耗时:1.1s
..........(以后省略)
5. 集群负载均衡
5.1 集群负载均衡核心定义
将全局推理队列中的排队请求,按照节点负载状态智能分发到多台推理服务器。实现多GPU节点算力统一调度,避免单节点负载过高、部分节点空闲浪费资源。是大模型服务横向扩容、支撑高并发流量的核心架构能力。
5.2 大模型专属负载均衡算法
- 最小连接数调度:优先把请求分发到当前正在执行推理任务最少的节点,完美适配大模型推理耗时不均的特点。
- 权重轮询调度:为性能更强、显存更大的节点设置更高权重,承担更多请求分发量。
- GPU显存感知调度:实时采集各节点显存使用率,优先分发到显存剩余充足的节点,规避显存溢出风险。
- 故障转移调度:定时做节点健康检查,自动剔除宕机、卡死、负载异常的节点,请求转发至正常节点。
5.3 集群架构运行流程
- 调度中心统一管理所有推理节点的在线状态、负载、显存、任务数。
- 定时心跳检测节点健康状况,异常节点自动下线隔离。
- 请求从全局队列取出后,由负载均衡算法筛选最优目标节点。
- 任务分发至对应节点执行推理,节点故障时自动重试分发至其他健康节点。
5.4 技术应用价值
- 支持服务无感横向扩容,业务流量增长只需新增GPU推理节点即可承接。
- 单节点硬件故障、进程异常不会影响整体服务可用性,实现高可用架构。
- 全局算力统一调度,抹平节点负载差异,整体资源利用率最大化。
五、总结
大模型服务和普通Web接口完全不是一个逻辑,它最核心的痛点就是算力稀缺、推理耗时不均,一旦没有队列治理体系,稍微一波流量峰值就容易直接拖垮服务。其实队列治理本质就是给大模型请求做有序管控、分层调度、兜底防护。从限流守住入口流量,再到优先级调度区分业务价值,配合长短请求拆分隔离任务类型,再加上阻塞队列溢出防护做兜底,最后靠集群负载均衡横向扩容,整套链路环环相扣,缺一不可。
做大模型除了懂模型调用,更要懂流量治理和资源调度。很多线上宕机、响应超时问题,根源都不是模型本身,而是排队机制混乱、没有限流和溢出防护导致的队列积压。看似简单的队列、限流、调度,实则是保障大模型服务高可用的底层基石。