解析 vLLM 架构及源码系列 - API Server

API 层
vLLM 是一个推理框架,支持在线推理和离线推理,更多的时候,在线推理的需求更多。这篇文章分析一下 vLLM 在进行 API 推理的时候,是如何进行数据流转的。

首先入口是 vllm/entrypoints/openai/api_server.py,从这个文件的 main 函数开始跟踪,可以开展 api 部分的代码分析。
run_server_worker 是主要的工作函数,它主要包括两个逻辑:
构建 Web APP: vLLM 使用 FastApi 框架作为 web 程序的后端服务框架,这是一个高性能的 API 组件,这里不做过多分析,app 就是它的主要对象。
构建 EngineClient: EngineClient 是主要面向引擎层进行通信的客户端
EngineClient 根据 V0 和 V1 的架构不同,使用 AsyncLLMEngine 和 AsyncLLM 这两个不同的类进行构建。看到这里我有一点思考,为什么只是 V0 和 V1 的版本变更,这两个类名就不一致了呢?比如 V1 的类名为什么不继续叫 AsyncLLMEngine 呢?因此我仔细对比了两个类的初始化过程,发现这里其实印证了 V1 Blog 里提到的一个目标: Provide a simple, modular, and easy-to-hack codebase.

V0 的架构比较臃肿,一个 AsyncLLMEngine 的逻辑,其实分散到了三个类中,例如_AsyncLLMEngine、LLMEngine。并且 AsyncLLMEngine 中还尝试融合 V1 的调度逻辑,见下面这段代码:
当启用 VLLM_USE_V1 时,LLMEngine 会被替换成 V1LLMEngine,但是后面这段逻辑其实被废弃了,V1 的逻辑直接被转到了 AsyncLLM(vllm/v1/engine/async_llm.py).
async_llm.py 的逻辑就简单明了,首先没有这么多层级的复杂逻辑,并且也不再依赖 LLMEngine 类,在 V1 的架构设计中,在线推理和离线推理完全隔离,在线推理的 AsyncLLM 不再依赖离线推理的 LLMEngine 类。
因此推荐大家从 V1 的代码开始阅读,如果有兴趣可以再去追溯 V0 的代码结构。
接下来让我们展开 V1 中 AsyncLLM 的初始化过程和数据流程:

AsyncLLM 是最核心的启动类,通过调用 AsyncMPclient 的 launch_core_engines 方法,初始化 EngineCoreProc,最终整条链路追踪下来,得到三个关键的函数:
process_input_sockets: 用于从 socket 中获取来自接口层传递的 request
process_input_queue and process_engine_step: 这两个函数都是用于将请求传输到 Scheduler 参与调度,区别在于 process_engine_step 是用于处理 scheduler 中还存在的 unfinished 的请求
从数据接口调用开始,以 /v1/chat/completions 为例,请求进入到 FastApi 的路由层之后,会被 handler 处理,这个 handler 是 api_server.py 的create_chat_completion
方法,从这个方法可以很容易的跟踪到,它会经过:
AsyncLLM 的 engine_client.generate—→ add_request
engine_core 的 add_request_async
最终会被发送到一个 socket 中,这个 socket 叫做 input_socket. 而 EngineCoreProc 的 process_input_sockets 方法同样也会监听这个 socket,他们的 socket 地址是一致的。在这张图中,只有接口层向引擎层的这一次通信会通过 socket 进行,因为它属于跨 Thread 的通信。
API Server 的源码剖析部分就总结到这里,关于路由管理、健全、日志等等细节部分这里不再赘述,也不是我们分析 vLLM 源码、架构的核心,搞清楚当客户端在调用 openAPI 的接口时,后端是如何接收请求,如何处理请求,就足够了。
在这一章节的基础上,后续我们应该关注推理引擎层是如何实现调度,如何与 GPU 通信这些核心问题上。
版权声明: 本文为 InfoQ 作者【Jason黄】的原创文章。
原文链接:【http://xie.infoq.cn/article/6c273b118ad1c324a336c1b56】。文章转载请联系作者。
评论