模型推理服务人工智能后端大模型MLOpsLLMOps【免费下载链接】BentoMLThe easiest way to serve AI apps and models - Build Model Inference APIs, Job queues, LLM apps, Multi-model pipelines, and more!项目地址https://gitcode.com/gh_mirrors/be/BentoML点击查看免费下载本篇以 BentoML 仓库内部的 BentoServer 开发文档 为主线讲解如何用一个最小示例 Service 跑起 BentoServer 并验证请求链路随后结合src/bentoml/_internal/server/下的源码深入剖析 HTTP/gRPC API 服务器的路由、中间件、系统端点与进程模型帮助读者从“会跑起来”进阶到“知其所以然”。一、五分钟跑通 BentoServer开发文档给出的最小实践由三步组成定义示例 Service、启动服务器、发送测试请求。以下完整继承原文档内容并结合源码补充了细节。1.1 创建示例 Service创建hello.py定义一个名为bento-server-test的 Service并声明一个同步 APIpredict和一个异步 APIclassify# hello.py import bentoml from bentoml.io import JSON svc bentoml.legacy.Service(bento-server-test) svc.api(inputJSON(), outputJSON()) def predict(input_json): return {input_received: input_json, foo: bar} svc.api(inputJSON(), outputJSON()) async def classify(input_json): return {input_received: input_json, foo: bar} # make sure to expose the asgi_app from the service instance app svc.asgi_app几个关键点每个svc.api装饰的函数都会成为 REST 服务器的一个端点路径默认与函数名一致/predict、/classify。这一点可以在 http_app.py 的routes属性中得到印证HTTPAppFactory遍历self.bento_service.apis为每个InferenceAPI创建一条 StarletteRoute方法集合由api.input.HTTP_METHODS决定。JSON()是 I/O 描述符负责在 HTTP 请求体与 Python 对象之间做序列化与反序列化。运行时_create_api_endpoint中会先调用api.input.from_http_request(request)解析输入再调用api.output.to_http_response(output)生成响应见 http_app.py。最后一行app svc.asgi_app是文档特意强调的注释“make sure to expose the asgi_app from the service instance”。只有把 ASGI 应用挂载为模块级变量appuvicorn才能直接加载它。1.2 启动服务器开发文档提供了两种等价的启动方式。方式一使用bentoml serve命令并开启热重载bentoml serve hello:svc --reload方式二直接让uvicorn加载上一步导出的 ASGI 应用uvicorn hello:app --reload两者的差异可以从 serve.py 的实现中看出bentoml serve别名serve-http在加载出 Service 后会根据 SDK 版本走不同分支serve.py若加载到的是 1.2 之前的bentoml.legacy.Service实例本文示例正是这种写法bentoml.legacy.Service则走 serving.py 中的serve_http_production它基于 Circus 进程管理器拉起 API server 与 Runner 等多个 watcher并处理 runner map、Prometheus 目录、reload 插件等生产级细节开发模式下强制api_workers1、host 默认绑定127.0.0.1。若加载到的是 1.2 新 SDK 的Service则调用 serve_http它同样用 Circus 编排进程但改为通过BENTOML_RUNNER_MAP环境变量把依赖服务地址注入各 worker并支持多服务依赖all_services的级联启动。bentoml serve常用参数定义于 serve.py参数说明-p, --portREST API 监听端口可被环境变量BENTOML_PORT覆盖--host绑定地址环境变量BENTOML_HOST可覆盖--development开发模式生产模式为默认--production已标记废弃--reload检测到代码变更自动重启--working-dir从源码加载 Service 时的查找目录--timeoutAPI server 与 runner 的超时时间秒--backlog最大待处理连接数--ssl-certfile/--ssl-keyfile等一组 SSL 相关参数其中--reload的行为在 docstring 中定义得很明确默认监听--working-dir缺省为当前目录下的全部文件变更若bentofile.yaml配置了include/exclude或存在.bentoignore则遵循其过滤规则模型存储中的新增/删除同样会触发重启serve.py。实现上make_reload_pluginserving.py会把 Circus 的ServiceReloaderPlugin挂到 arbiter 上由文件监视驱动进程重启。1.3 发送测试请求服务器默认监听 8000 端口对predict端点发起 POST 请求curl -X POST localhost:8000/predict -H Content-Type: application/json -d {abc: 123}预期返回{input_received: {abc: 123}, foo: bar}。如果同步函数predict被定义成普通def服务端并不会阻塞事件循环——_create_api_endpoint检测到函数不是 async 可调用对象时会把它放进线程池执行output await run_in_threadpool(api.func, **input_data)http_app.py。这解释了示例中同步、异步两个 API 可以并存且行为一致的原因。二、BaseAppFactory所有 API 服务器的公共骨架base_app.py 定义了抽象基类BaseAppFactoryHTTP 与 Runner 两类服务器都继承它。它规定了三个统一的系统能力2.1 系统路由get_system_routesbase_app.py为每个服务器自动注入一组运维端点/livez与/healthz存活探针始终返回 200注释明确说明“Make sure it works with Kubernetes liveness probe”/readyz就绪探针服务器完成on_startup钩子mark_as_ready前会返回 500/metricsPrometheus 指标端点仅当api_server_config.metrics.enabled配置开启时才注册内容由metrics_client.generate_latest()生成。HTTP 服务器还会在 http_app.py 覆写readyz当runner_probe.enabled开启时它会并发检查所有 runner 的就绪状态任一 runner 未就绪即返回 503 “Runners are not ready.”——这就是为什么 Kubernetes 的 readiness 探针要等模型加载完成才放行流量。2.2 流量保护中间件middlewares属性base_app.py在构造时根据timeout与max_concurrency装配两组中间件实现位于 traffic.pyTimeoutMiddleware用loop.call_later给每个 HTTP/WebSocket 请求上定时器超时后返回 504 和Not able to process the request in {timeout} seconds。HTTPAppFactory 的 timeout 取自配置项api_server_config.traffic.timeouthttp_app.py而 RunnerAppFactory 会先从全局runners_config.traffic取、再按runners_config[runner.name].traffic做按 runner 的覆盖runner_app.py这解释了为什么bentoml serve --timeout能同时约束 API server 和 runner。MaxConcurrencyMiddleware用asyncio.Semaphore限制并发信号量锁定时直接返回 429 “Too many requests”。注意它的BYPASS_PATHS硬编码豁免了/metrics、/healthz、/livez、/readyz保证限流不会误伤探针与监控采集。2.3 生命周期钩子lifespan上下文管理器base_app.py按序执行on_startup/on_shutdown钩子并兼容同步与异步钩子。HTTP 服务器在启动阶段依次为每个 runner 挂上初始化钩子开发模式下全部runner.init_local生产模式下嵌入式 runner 本地初始化、非嵌入式 runner 走init_client连接远端http_app.py关闭阶段则逐个runner.destroy并清理临时目录池。三、HTTPAppFactoryREST API 的完整装配3.1 路由结构结合 http_app.py 的routes属性一个完整服务器的路由表为路径来源说明/index_view_func内嵌的 Swagger UI 首页DEFAULT_INDEX_HTML引用/static_content/下的 swagger-ui 静态资源/docs.jsondocs_view_func返回bento_service.openapi_spec的 JSON即 OpenAPI 定义/static_contentStaticFiles挂载服务仓库内 static_content 目录 的静态文件/livez/healthz/readyz/metricsBaseAppFactory系统端点/{api.name}用户svc.api每个 InferenceAPI 一条路由此外__call__中还会把用户通过mount_apps注册的第三方 ASGI 应用挂载到指定路径http_app.py这是 Service 组合model composition在 HTTP 层的落点。3.2 中间件与可观测性HTTPAppFactory.middlewareshttp_app.py在流量中间件之上依次追加用户自定义的bento_service.middlewaresCORSenable_access_control由配置http.cors.enabled注入开启时要求access_control_allow_origin必须设置否则直接assert失败HTTPTrafficMetricsMiddleware开启 metrics 时统计请求流量指标OpenTelemetryMiddleware通过client_request_hook把 span id 写入trace_context.request_id与下面的响应头形成闭环AccessLogMiddleware由api_server_config.logging.access控制可分别配置是否记录请求/响应的 Content-Length 与 Content-Type。响应侧_create_api_endpoint在返回前会写入两个追踪头X-BentoML-Request-ID恒有与X-BentoML-Trace-ID当http.response.trace_id配置开启且存在 trace id 时。异常处理策略也很清晰BentoMLException按其error_code映射为对应状态码4xx除 401/403会带错误信息文本其他未捕获异常统一返回 500 并提示“find the error details in server logs”http_app.py。3.3 uvicorn 如何接管 ASGI 应用回到 1.2 节“uvicorn 直接跑”的路径bentoml serve拉起的生产 worker 其实也是让 uvicorn 运行同一个svc.asgi_app。入口在 http_api_server.py它接收 Circus 传递的 socket 文件描述符--fd、runner map JSON--runner-map亦可用环境变量BENTOML_RUNNER_MAP提供、--backlog默认 2048等参数组装uvicorn_options后以uvicorn.Server(config).run(sockets[sock])复用 Circus 已绑定的 socket从而跳过 uvicorn 内置的多进程 supervisorhttp_api_server.py。SSL 参数在此统一转成 uvicorn 选项缺省ssl_version取ssl.PROTOCOL_TLS_SERVER、ssl_cert_reqs取ssl.CERT_NONE。四、RunnerAppFactory模型进程内部的服务器多 runner 架构下模型推理发生在独立进程的 Runner 中每个 runner 进程内部同样运行一个 Starlette 应用由 runner_app.py 的RunnerAppFactory构建路由每个runner_method对应一条 POST 路由__call__方法映射到根路径/runner_app.py再加上系统端点批处理对batchable且max_batch_size 1的方法构造CorkDispatcher带max_latency_in_ms、batch_dim的自适应批处理器批处理结果按indices拆回单条批处理尺寸会记入bentoml_runner_adaptive_batch_size直方图批处理满员超时会以ServiceUnavailable(process is overloaded)作为 fallback协议请求体以 pickled payload 传输单参数时通过Payload-Container、Payload-Meta、Batch-Size头描述容器类型runner_app.py响应Content-Type形如application/vnd.bentoml.{container}多输出走application/vnd.bentoml.multiple_outputs流式方法返回StreamingResponse且标注application/vnd.bentoml.stream_outputs。API server 与 runner 之间通过 socket 通信POSIX 下使用 Unix domain socket路径长度上限 103见 serving.py 中MAX_AF_UNIX_PATH_LENGTH断言Windows/WSL 下退化为tcp://127.0.0.1:{port}。五、gRPC 服务器与进程编排5.1 gRPC Server 的实现grpc_app.py 中的Server直接继承grpc.aio._server.Server关键装配包括健康检查内置非阻塞health.aio.HealthServicer启动完成后把所有 service 标记为SERVING拦截器顺序AsyncOpenTelemetryServerInterceptor恒在前随后按需追加PrometheusServerInterceptormetrics 开启时与AccessLogServerInterceptoraccess log 开启且日志级别 ≤ INFO 时最后扩展用户自定义拦截器注释强调“order of interceptors is important”grpc_app.py传输选项非 Windows 平台显式设置grpc.so_reuseport 1以便在生产模式下用 SO_REUSEPORT 并发多个 gRPC servergrpc_app.py可选项--enable-reflection需grpcio-reflection、--enable-channelz需grpcio-channelz在 serve.py 的serve-grpc子命令中暴露。由于 SO_REUSEPORT 依赖serve_grpc_production在 Windows 非开发模式下会直接抛出异常在 macOS/FreeBSD 上则建议以容器方式部署serving.py——这是使用 gRPC 模式时需要记住的平台限制。5.2 Circus 编排的进程模型生产模式下bentoml serve并不把一切都塞进一个进程。以 legacy 路径为例serving.pyensure_prometheus_dir准备 Prometheus multiproc 目录并注入PROMETHEUS_MULTIPROC_DIR环境变量为每个非嵌入式Runner创建runner_{name}watchernumprocessesrunner.scheduled_worker_count命令为python -m bentoml_cli.worker.runner ...通过 CircusSocket 传递 UDS/TCP 地址为 API server 创建api_serverwatcher命令为python -m bentoml_cli.worker.http_api_server ...numprocessesapi_workers生产模式默认取 CPU 核数--reload时挂载ServiceReloaderPluginarbiter.start启动后打印 “Starting production HTTP BentoServer ... listening on http://host:port”。新 SDK 路径serve_http结构相同但把所有依赖服务包括主服务统一以service_{name}watcher 拉起并通过BENTOML_RUNNER_MAP注入互相发现的地址同时会先执行server_on_deployment解析全部模型、触发用户的 deployment hook、按needs_task_db()初始化任务结果的 SQLite 存储。六、小结与延伸阅读围绕 src/bentoml/_internal/server/README.md 这份开发文档本文给出的完整路径是用hello.py示例 Service 加bentoml serve hello:svc --reload或uvicorn hello:app --reload跑通最小服务用curl验证/predict端点再沿 base_app.py 的BaseAppFactory系统路由、超时/限流中间件、生命周期理解服务器骨架沿 http_app.py 理解 REST 路由、CORS、OpenTelemetry 与异常映射沿 runner_app.py 理解 runner 内部的批处理与 payload 协议沿 grpc_app.py 理解 gRPC 模式的健康检查、拦截器与 SO_REUSEPORT 细节最后沿 serve.py 与 serving.py 理解 Circus 多进程编排。需要说明的适用前提示例代码基于bentoml.legacy.Service写法属于文档明确给出的开发路径生产部署建议以容器化 Bento 为最终形态且 gRPC 模式在 Windows 上受 SO_REUSEPORT 限制需配合--development或改用 Linux 容器。赞分享模型推理服务人工智能后端大模型MLOpsLLMOps【免费下载链接】BentoMLThe easiest way to serve AI apps and models - Build Model Inference APIs, Job queues, LLM apps, Multi-model pipelines, and more!项目地址https://gitcode.com/gh_mirrors/be/BentoML点击查看免费下载相关推荐mistral.rs 服务器 Anthropic Messages API 实战指南从 anthropic_chat.py 示例到源码级原理mistral.rs 服务器 Anthropic Messages API 实战指南从 anthropic_chat.py 示例到源码级原理 本篇技术指南以推理引擎模型推理服务AI Agent多模态Uvicorn ASGI服务器全面解析从入门到实战Uvicorn ASGI服务器全面解析从入门到实战 什么是Uvicorn Uvicorn是一个基于ASGI规范的轻量级Web服务器实现专为Python异步后端Web框架Conv-TasNet完全指南从安装到部署的简单步骤Conv TasNet完全指南从安装到部署的简单步骤 Conv TasNet是一个基于PyTorch实现的语音分离模型能够有效分离混合语音信号超越传统的时人工智能深度学习语音音频上一篇grepWin 项目常见问题解决方案下一篇ESP32-Paxcounter 项目常见问题解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考