Ray Serve DeploymentHandle 的异步批处理
Ray Serve 里,异步批处理要把两个概念接起来:调用方通过 DeploymentHandle.remote() 一次提交一个对象,部署内部用 @serve.batch 把多个对象聚合成列表。remote() 返回的是可等待的 DeploymentResponse,因此可以先把请求全部发出,再统一等待结果;真正的批处理函数则必须是异步函数,并返回与输入等长的结果列表。
官方地址:https://docs.ray.io/en/latest/serve/
下面的例子只讨论 Ray Serve 的 DeploymentHandle 和动态请求批处理,不把 HTTP 网关、模型版本路由或其他推理框架混进来。
先分清句柄调用和批处理函数
句柄负责把单次调用送到某个部署,批处理装饰器负责在部署内部收集这些单项输入。调用方看到的是一个个响应对象,批处理方法看到的则是一个列表。两层不要互换:不要让调用方手工拼一个“大列表”再把它当成每次请求的唯一参数,也不要让批处理方法只返回一个聚合值。

用 async def 接住一批输入
@serve.batch 装饰的方法要接收一个列表,并返回同样长度的列表。下面把每个输入乘以一个固定系数,故意保持逻辑简单,重点是展示批处理的边界:
from typing import List
from ray import serve
@serve.deployment
class ScoreService:
@serve.batch(max_batch_size=8, batch_wait_timeout_s=0.02)
async def score_batch(self, values: List[float]) -> List[float]:
# 每个输入对应一个输出,不能把一批结果压成单个数字。
return [value * 1.2 for value in values]
async def __call__(self, value: float) -> float:
# 单项调用交给批处理方法,Serve 负责在内部聚合。
return await self.score_batch(value)
app = ScoreService.bind()
这里的 __call__ 仍然以单个 value 为入口,score_batch 才是批量函数。批大小达到 max_batch_size,或者首个请求等待时间达到 batch_wait_timeout_s,Serve 就会把当前积累的输入交给批处理函数。
先提交多个 remote,再等待响应
从另一个部署调用时,关键动作是不要在提交循环里立刻等待每个结果。每次 remote() 都会返回一个 DeploymentResponse;把这些响应先收集起来,调用已经排入 Serve 的路由与批处理队列,再按原顺序取回结果:
from typing import List
from ray import serve
from ray.serve.handle import DeploymentHandle
@serve.deployment
class BatchCaller:
def __init__(self, scorer: DeploymentHandle):
self.scorer = scorer
async def __call__(self, values: List[float]) -> List[float]:
# 先发出全部单项请求,让 Serve 有机会把它们放进同一批。
responses = [self.scorer.remote(value) for value in values]
# 逐个 await 仍保持结果顺序;请求已经在上一行全部提交。
results = [await response for response in responses]
return results
app = BatchCaller.bind(ScoreService.bind())
这段写法的重点不是把 DeploymentResponse 当作普通 asyncio.Future 改造,而是利用它本身的可等待语义。调用方可以按输入顺序读取结果;批处理是否凑满、由哪个副本处理,则由 Serve 的路由和批处理机制决定。
四个参数怎么和负载约束对齐
max_batch_size 是一次交给底层函数的最大批量度量,默认按列表长度计算;batch_wait_timeout_s 是首个请求到达后等待凑批的最长时间。前者偏大通常有利于吞吐但会增加内存压力和首批等待,后者偏大可能提高低流量下的批利用率,却会抬高延迟。
max_concurrent_batches 控制一个副本同时执行的批次数。只有底层异步计算确实能并发、并且资源允许时才适合增大它。若每个输入的成本差异很大,可以提供 batch_size_fn,让 Serve 按总 token 数、图节点数等自定义度量计算批大小,而不是只数请求个数。

from typing import List
from ray import serve
def token_count(texts: List[str]) -> int:
# 用输入 token 总量作为批大小度量,而不是字符串条数。
return sum(len(text.split()) for text in texts)
@serve.deployment
class TokenService:
@serve.batch(
max_batch_size=512,
batch_wait_timeout_s=0.05,
max_concurrent_batches=2,
batch_size_fn=token_count,
)
async def infer_batch(self, texts: List[str]) -> List[int]:
# 返回值仍必须与 texts 一一对应,长度不能变化。
return [len(text.split()) for text in texts]
自定义度量时,max_batch_size 的单位也随之改变:示例中它表达 token 总量,不是最多 512 条文本。参数命名仍然是批大小,但判断条件已经由 batch_size_fn 定义。
用结果长度和指标判断是否正确
最直接的结果判断是:输入多少项,就返回多少项,并且响应顺序与提交顺序一致。若批处理函数漏掉一项、合并了多项或返回一个标量,Serve 会把它视为批量结果不匹配,而调用方也无法把响应准确对应回原请求。
参数调优不要只盯着总吞吐。Ray Serve 的批处理指标包括批等待时间、批执行时间、批队列长度和批利用率。等待时间持续偏高,说明等待窗口或队列压力可能过大;利用率偏低,可能是流量不足或等待窗口设置不合适;执行时间偏高,则要回到模型计算和副本资源本身。
落地时可以按这个顺序判断:
- 先确认每个
remote()调用只提交一个业务对象,并保存每个响应。 - 再确认批处理函数是
async def,参数是列表,返回列表长度不变。 - 最后结合延迟目标选择等待窗口,并用批等待、队列长度和利用率观察实际效果。
常见问题
DeploymentHandle.remote() 会直接返回模型结果吗?
不会。它返回一个代表进行中调用的 DeploymentResponse;在异步代码中需要等待它,才能得到部署方法的实际结果。
为什么 max_batch_size 设为 8,却没有每次都得到 8 条输入?
批处理会在达到上限或等待窗口到期时发送当前批次。低流量下通常会先到达超时条件,因此实际批量可以小于上限;使用 batch_size_fn 时,判断依据还会从条数变成自定义度量。
批处理函数能返回一个总结果吗?
不适合这样做。动态请求批处理要求输出与输入逐项对应,应该返回与输入列表等长的结果列表,再由调用方按响应顺序取回。
归纳起来,DeploymentHandle 负责异步提交和响应句柄,@serve.batch 负责把单项请求聚成批次。先提交、后等待,配合等长返回和批处理指标,就能把吞吐、延迟与资源边界放在同一个可解释的模型里。
ONNX Runtime IOBinding 的设备内存复用
- 上一篇
- ONNX Runtime IOBinding 的设备内存复用
- 下一篇
- MLflow signature required 字段的输入收窄
-
- 科技周边 · 人工智能 | 1小时前 |
- Label Studio webhook 任务完成回调的签名处理
- 459浏览 收藏
-
- 科技周边 · 人工智能 | 1小时前 | mcp · MCP 资源订阅 notifications/resources/updated subscriptions/listen
- MCP 资源订阅变更通知的客户端处理
- 369浏览 收藏
-
- 科技周边 · 人工智能 | 1小时前 | mcp · AI工程 · MCP 长任务 progress token 进度通知 智能体协议
- MCP progress token 长任务进度回报的实现
- 367浏览 收藏
-
- 科技周边 · 人工智能 | 1小时前 |
- Gemini API 预估 token 与使用量元数据的区分
- 254浏览 收藏
-
- 科技周边 · 人工智能 | 1小时前 |
- MLflow signature required 字段的输入收窄
- 202浏览 收藏
-
- 科技周边 · 人工智能 | 1小时前 | 人工智能 · ONNX Runtime IOBinding 设备内存复用
- ONNX Runtime IOBinding 的设备内存复用
- 412浏览 收藏
-
- 科技周边 · 人工智能 | 1小时前 | JAX PyTree register_pytree_node register_dataclass jax.jit
- JAX PyTree 自定义数据结构的注册方法
- 156浏览 收藏
-
- 科技周边 · 人工智能 | 1小时前 |
- PyTorch DistributedSampler 每轮 shuffle 的复现配置
- 241浏览 收藏
-
- 科技周边 · 人工智能 | 1小时前 | 文本对齐 Transformers tokenizer offset_mapping Fast tokenizer
- Transformers tokenizer offset mapping 的原文区间对齐
- 423浏览 收藏
-
- 科技周边 · 人工智能 | 1小时前 |
- Hugging Face Datasets unique 怎样列出字段的不同取值
- 469浏览 收藏
-
- 科技周边 · 人工智能 | 19小时前 |
- ONNX Runtime SessionOptions 怎样控制推理会话线程数
- 351浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 754次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 752次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 833次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 798次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 633次使用
-
- 量化模型本地推理的显存估算方法
- 2026-10-17 501浏览
-
- 本地大模型反复输出同一句话怎么调整生成参数
- 2026-09-06 501浏览
-
- Python 调用大模型时如何用结构化输出校验 JSON:从解析失败到可重试
- 2026-08-29 501浏览
-
- AI写作工具免费版安装教程(含豆包Clawdbot)
- 2026-05-30 501浏览
-
- WPS AI能自动生成PPT吗?输入主题一键制作演示文稿
- 2026-05-27 501浏览

