【vllm 】WorkerProc WorkerWrapperBase GPU worker之间的关系

中文回答 WorkerProc 与 WorkerWrapperBase 及worker 相关的区别与联系,从设计模式角度考虑,为啥要加一层workerwrapper. 相关类干嘛用的

这段代码的作用是处理通过 RPC 传递的可调用对象,允许主进程动态地让子进程执行任意函数(而不仅仅是预定义的方法名)。

代码含义

python 复制代码
elif isinstance(method, bytes):
    func = partial(cloudpickle.loads(method), self.worker)
  • cloudpickle.loads(method):将字节串反序列化回一个 Python 可调用对象(比如函数或方法)。
  • partial(..., self.worker):将该可调用对象的第一个参数 固定为 self.worker(即当前 Worker 实例)。
  • 最终 func 是一个新的可调用对象,调用 func(*args, **kwargs) 等价于调用原始函数 original(self.worker, *args, **kwargs)

为什么要这样做?

  1. 灵活性 :主进程可以动态定义 Worker 需要执行的代码,而不必提前在 WorkerWrapperBase 类中声明所有方法。
  2. 序列化传递 :函数通过 cloudpickle 序列化后经消息队列传输,子进程反序列化后执行。
  3. 绑定 Worker 实例 :因为反序列化得到的函数通常需要访问 Worker 对象(如调用其内部方法或访问属性),通过 partialself.worker 作为第一个参数固定,使得调用时只需传入后续参数。

具体举例

假设主进程需要让 Worker 执行一个自定义操作:记录当前 GPU 显存使用情况并返回剩余显存 。这个操作不是 WorkerWrapperBase 的现有方法,但主进程可以动态定义并发送。

步骤 1:主进程(Executor)中定义并序列化函数

python 复制代码
import cloudpickle

def get_free_memory(worker, threshold_mb: int):
    """返回当前 Worker 的空闲显存(MB),如果低于阈值则打印警告"""
    # 假设 worker 有一个 .device 属性和 .get_memory_info() 方法
    free = worker.get_memory_info()[0] / (1024**2)  # 转换为 MB
    if free < threshold_mb:
        print(f"Warning: only {free:.2f} MB free on {worker.device}")
    return free

# 序列化函数
serialized_func = cloudpickle.dumps(get_free_memory)

# 通过 RPC 发送到 Worker,参数 (threshold_mb=100) 作为 args/kwargs
# output_rank 指定由哪个 Worker 返回结果
self.rpc_broadcast_mq.enqueue((serialized_func, (100,), {}, output_rank=0))

步骤 2:子进程(WorkerProc)中处理

python 复制代码
# method 是 bytes 类型,值为 serialized_func
elif isinstance(method, bytes):
    # 反序列化得到原始函数 get_free_memory
    original_func = cloudpickle.loads(method)  # 即 get_free_memory
    # 用 partial 固定第一个参数为 self.worker
    func = partial(original_func, self.worker)

# 之后调用 func(*args, **kwargs)
output = func(100)  # 等价于 get_free_memory(self.worker, 100)

步骤 3:执行效果

  • 子进程实际执行 get_free_memory(self.worker, 100),返回当前 Worker 的空闲显存(如果低于 100MB 还会打印警告)。
  • 结果通过响应队列返回给主进程。

与字符串方法名的对比

method 类型 用途 示例
str 调用 WorkerWrapperBase 的预定义方法 "execute_model"getattr(self.worker, "execute_model")(*args, **kwargs)
bytes 执行任意自定义函数(动态行为) 序列化的 get_free_memorypartial(..., self.worker)(*args, **kwargs)

典型应用场景

  • 调试或性能分析:临时注入监控代码,获取 Worker 内部状态。
  • 动态更新策略:主进程根据当前负载发送不同的处理逻辑(如动态调整采样参数)。
  • 扩展性 :无需修改 WorkerWrapperBase 类即可新增功能,适合插件化架构。

这种设计让 vLLM 的 RPC 机制非常灵活,但也要注意序列化开销(函数较大时可能影响性能),因此主要用于非频繁调用的操作。

WorkerWrapperBaseWorkerProc 在 vLLM 的多进程架构中扮演着职责分明的两个角色。

简单来说,WorkerWrapperBase 定义了如何执行 模型(What to do),而 WorkerProc 则负责管理和通信(How to manage & communicate)。

🎭 核心区别:两类Worker的分工

对比维度 WorkerWrapperBase (业务执行者) WorkerProc (进程管理器与信使)
本质身份 一个Python基类(Wrapper)。 一个独立的OS进程(Process)。
核心职责 1. 执行核心模型操作:execute_model, sample_tokens等。 2. 管理GPU:加载模型、管理KV Cache、执行推理。 1. 生命周期管理:作为守护进程被创建、启动、监控和销毁。 2. IPC通信枢纽:持有MessageQueue,接收Executor的RPC请求,并发送执行结果。 3. 封装WorkerWrapperBase:接收请求后,调用其内部的WorkerWrapperBase实例执行具体任务。
包含关系 WorkerProc 内部包含 一个 WorkerWrapperBase 的实例作为其核心功能组件。 不包含,WorkerWrapperBase 是其委托执行具体工作的内部对象。
部署位置 作为一个对象,被包含并运行在 WorkerProc 进程中。 作为一个独立的OS进程运行。

🔗 设计剖析:为何选择 WorkerWrapperBase 作为 RPC 目标

vLLM 的设计如此选择,核心是为了实现关注点分离,这是一种经典的模块化设计原则。

  1. 单一职责原则(SRP):将 RPC 协议实现、进程生命周期、IPC 队列管理这些"通信与调度"的复杂逻辑,与"模型加载、执行"这些纯粹的"业务逻辑"彻底分开。

    • WorkerWrapperBase 专注于具体的模型计算任务,而不必关心自己是被单个进程还是多进程调用。
    • WorkerProc 则专注于进程间通信和健壮性,无需理会 execute_model 内部复杂的计算细节。
  2. 实现通信与执行的解耦 :当 MultiprocExecutor 广播一个RPC请求(如 "execute_model")时,它发送的目标是 WorkerWrapperBase 的方法。

    • WorkerProc 接收请求后,通过 getattr 等方法,动态调用其内部 WorkerWrapperBase 实例对应的 execute_model 方法,并将结果通过 worker_response_mq 发送回去。
    • 这种设计使得 vLLM 能够无缝替换底层的执行逻辑,例如从 GPU Worker 切换到 CPU Worker,只需更换 WorkerWrapperBase 的不同子类即可,而无需修改 WorkerProc 的进程管理代码。

中文回答 WorkerProc 与 WorkerWrapperBase 及worker 相关的区别与联系,从设计模式角度考虑,为啥要加一层workerwrapper. 相关类关系,画出流程图,时许等相关的图

WorkerProc、WorkerWrapperBase 与 Worker 的区别与联系

核心区别

组件 职责 使用场景
WorkerBase 抽象基类,定义 worker 接口 所有 worker 实现的基础
WorkerWrapperBase 通用包装器,延迟初始化和方法转发 跨执行器复用
WorkerProc 多进程执行器的进程包装器 仅用于 MultiprocExecutor
具体Worker 实际的模型执行逻辑 GPUWorker、CPUWorker 等

设计模式分析

1. 延迟初始化模式 (Lazy Initialization)

WorkerWrapperBase 实现延迟初始化,将 worker 创建推迟到真正需要时 1

python 复制代码
class WorkerWrapperBase:
    def __init__(self, rpc_rank: int = 0, global_rank: int | None = None):
        self.rpc_rank: int = rpc_rank
        self.global_rank: int = self.rpc_rank if global_rank is None else global_rank
        # 此时 worker 还未创建,只是保存配置信息
        self.worker: WorkerBase  # 类型注解,但实际对象未创建

真正的初始化在 init_worker 中进行 2

2. 代理模式 (Proxy Pattern)

通过 __getattr__ 实现方法转发,将所有调用代理给实际的 worker 实例 3

python 复制代码
def __getattr__(self, attr: str):
    return getattr(self.worker, attr)

3. 工厂模式 (Factory Pattern)

WorkerWrapperBase 根据配置动态创建不同类型的 worker 4

python 复制代码
if isinstance(parallel_config.worker_cls, str):
    worker_class: type[WorkerBase] = resolve_obj_by_qualname(
        parallel_config.worker_cls
    )

类关系图

包含
包含
管理
<<abstract>>
WorkerBase
+init_device()
+load_model()
+execute_model()
+shutdown()
WorkerWrapperBase
-worker: WorkerBase
-vllm_config: VllmConfig
+init_worker()
+getattr(attr)
+execute_model()
WorkerProc
-worker: WorkerWrapperBase
-rank: int
-async_output_queue: queue.Queue
+worker_busy_loop()
+handle_output()
+make_worker_process()
MultiprocExecutor
-workers: list[WorkerProc]
+_init_executor()
+execute_model()
GPUWorker
+init_device()
+load_model()
CPUWorker
+init_device()
+load_model()
RayWorkerWrapper
+adjust_rank()
+execute_method()

时序流程图

初始化流程

GPUWorker WorkerWrapperBase WorkerProc MultiprocExecutor GPUWorker WorkerWrapperBase WorkerProc MultiprocExecutor 延迟初始化 - 只保存配置 make_worker_process() worker_main() WorkerWrapperBase() init_worker() resolve_obj_by_qualname() GPUWorker(**kwargs) init_device() load_model() worker_busy_loop()

方法调用流程

GPUWorker WorkerWrapperBase WorkerProc MultiprocExecutor GPUWorker WorkerWrapperBase WorkerProc MultiprocExecutor getattr 代理转发 execute_model() execute_model() execute_model() ModelRunnerOutput ModelRunnerOutput handle_output()

为什么需要 WorkerWrapperBase 层

1. 跨执行器复用

  • UniProcExecutor 直接使用 WorkerWrapperBase 5
  • RayExecutor 使用 RayWorkerWrapper 继承自 WorkerWrapperBase 6
  • MultiprocExecutor 通过 WorkerProc 间接使用

2. 环境变量依赖处理

在 Ray 环境中,GPU 分配是在 actor 调度后确定的,需要延迟初始化 6

3. 生命周期管理

统一的初始化和清理接口,支持插件系统和扩展 7

4. 错误处理和资源管理

集中的错误处理逻辑,避免部分初始化状态。

WorkerProc 的特殊职责

作为多进程执行器的特有组件,WorkerProc 负责:

  1. 进程创建和管理 :通过 multiprocessing.Process 创建子进程 8
  2. 进程间通信 :管理消息队列和管道通信 9
  3. 异步输出处理 :管理异步输出队列和线程 10
  4. 工作循环 :运行 worker_busy_loop 处理 RPC 调用 11

Notes

这种分层设计体现了单一职责原则和开闭原则:

  • WorkerBase 定义接口
  • WorkerWrapperBase 提供通用的生命周期管理
  • WorkerProc 处理多进程特有的逻辑
  • 具体 Worker 实现专注于模型执行

Wiki pages you might want to explore:

Wiki pages you might want to explore:

Citations

File: vllm/v1/worker/worker_base.py (L174-181)

python 复制代码
class WorkerWrapperBase:
    """
    This class represents one process in an executor/engine. It is responsible
    for lazily initializing the worker and handling the worker's lifecycle.
    We first instantiate the WorkerWrapper, which remembers the worker module
    and class name. Then, when we call `update_environment_variables`, and the
    real initialization happens in `init_worker`.
    """

File: vllm/v1/worker/worker_base.py (L216-301)

python 复制代码
    @instrument(span_name="Worker init")
    def init_worker(self, all_kwargs: list[dict[str, Any]]) -> None:
        """
        Here we inject some common logic before initializing the worker.
        Arguments are passed to the worker class constructor.
        """
        kwargs = all_kwargs[self.rpc_rank]

        vllm_config: VllmConfig | None = kwargs.get("vllm_config")
        assert vllm_config is not None, (
            "vllm_config is required to initialize the worker"
        )
        self.vllm_config = vllm_config

        vllm_config.enable_trace_function_call_for_thread()

        from vllm.plugins import load_general_plugins

        load_general_plugins()

        parallel_config = vllm_config.parallel_config
        if isinstance(parallel_config.worker_cls, str):
            worker_class: type[WorkerBase] = resolve_obj_by_qualname(
                parallel_config.worker_cls
            )
        else:
            raise ValueError(
                "passing worker_cls is no longer supported. "
                "Please pass keep the class in a separate module "
                "and pass the qualified name of the class as a string."
            )

        if parallel_config.worker_extension_cls:
            worker_extension_cls = resolve_obj_by_qualname(
                parallel_config.worker_extension_cls
            )
            extended_calls = []
            if worker_extension_cls not in worker_class.__bases__:
                # check any conflicts between worker and worker_extension_cls
                for attr in dir(worker_extension_cls):
                    if attr.startswith("__"):
                        continue
                    assert not hasattr(worker_class, attr), (
                        f"Worker class {worker_class} already has an attribute"
                        f" {attr}, which conflicts with the worker"
                        f" extension class {worker_extension_cls}."
                    )
                    if callable(getattr(worker_extension_cls, attr)):
                        extended_calls.append(attr)
                # dynamically inherit the worker extension class
                worker_class.__bases__ = worker_class.__bases__ + (
                    worker_extension_cls,
                )
                logger.info(
                    "Injected %s into %s for extended collective_rpc calls %s",
                    worker_extension_cls,
                    worker_class,
                    extended_calls,
                )

        shared_worker_lock = kwargs.pop("shared_worker_lock", None)
        if shared_worker_lock is None:
            msg = (
                "Missing `shared_worker_lock` argument from executor. "
                "This argument is needed for mm_processor_cache_type='shm'."
            )

            mm_config = vllm_config.model_config.multimodal_config
            if mm_config and mm_config.mm_processor_cache_type == "shm":
                raise ValueError(msg)
            else:
                logger.warning_once(msg)

            self.mm_receiver_cache = None
        else:
            self.mm_receiver_cache = (
                MULTIMODAL_REGISTRY.worker_receiver_cache_from_config(
                    vllm_config,
                    shared_worker_lock,
                )
            )

        with set_current_vllm_config(self.vllm_config):
            # To make vLLM config available during worker initialization
            self.worker = worker_class(**kwargs)

File: vllm/v1/worker/worker_base.py (L314-315)

python 复制代码
    def __getattr__(self, attr: str):
        return getattr(self.worker, attr)

File: vllm/v1/executor/uniproc_executor.py (L29-29)

python 复制代码
        self.driver_worker = WorkerWrapperBase(rpc_rank=0)

File: vllm/v1/executor/ray_utils.py (L53-63)

python 复制代码
    class RayWorkerWrapper(WorkerWrapperBase):
        """Ray wrapper for vllm.worker.Worker, allowing Worker to be
        lazily initialized after Ray sets CUDA_VISIBLE_DEVICES."""

        def __init__(self, *args, **kwargs) -> None:
            super().__init__(*args, **kwargs)
            # Since the compiled DAG runs a main execution
            # in a different thread that calls cuda.set_device.
            # The flag indicates is set_device is called on
            # that thread.
            self.compiled_dag_cuda_device_set = False

File: vllm/v1/executor/multiproc_executor.py (L555-585)

python 复制代码
    def _init_message_queues(
        self, input_shm_handle: Handle, vllm_config: VllmConfig
    ) -> None:
        if vllm_config.parallel_config.nnodes_within_dp == 1:
            # Initialize MessageQueue for receiving SchedulerOutput
            self.rpc_broadcast_mq = MessageQueue.create_from_handle(
                input_shm_handle, self.worker.rank
            )

            # Initializes a message queue for sending the model output
            self.worker_response_mq = MessageQueue(1, 1)
            self.peer_response_handles = []
        else:
            # Initialize remote MessageQueue for receiving SchedulerOutput across nodes
            self.rpc_broadcast_mq = get_inner_dp_world_group().create_mq_broadcaster(
                external_writer_handle=input_shm_handle,
                # Since there is external_writer_handle from executor proc,
                # where the ready signal from actual writer is sent out of the
                # create_mq_broadcaster method and after this setup, we make it
                # non blocking. The handshake will be triggered when
                # worker.rpc_broadcast_mq.wait_until_ready() is called
                blocking=False,
            )
            # Initializes remote message queue for sending the model output to the
            # driver worker, exposing peer_response_handles for driver worker
            # that include handles for all ranks
            self.worker_response_mq, self.peer_response_handles = (
                get_inner_dp_world_group().create_single_reader_mq_broadcasters(
                    reader_rank_in_group=0
                )
            )

File: vllm/v1/executor/multiproc_executor.py (L632-639)

python 复制代码
        if self.use_async_scheduling:
            self.async_output_queue: queue.Queue = queue.Queue()
            self.async_output_copy_thread = Thread(
                target=self.async_output_busy_loop,
                daemon=True,
                name="WorkerAsyncOutputCopy",
            )
            self.async_output_copy_thread.start()

File: vllm/v1/executor/multiproc_executor.py (L685-690)

python 复制代码
        proc = context.Process(
            target=WorkerProc.worker_main,
            kwargs=process_kwargs,
            name=f"VllmWorker-{rank}",
            daemon=True,
        )

File: vllm/v1/executor/multiproc_executor.py (L953-980)

python 复制代码
    def worker_busy_loop(self):
        """Main busy loop for Multiprocessing Workers"""
        assert self.rpc_broadcast_mq is not None
        while True:
            method, args, kwargs, output_rank = self.rpc_broadcast_mq.dequeue(
                indefinite=True
            )
            try:
                if isinstance(method, str):
                    func = getattr(self.worker, method)
                elif isinstance(method, bytes):
                    func = partial(cloudpickle.loads(method), self.worker)

                output = func(*args, **kwargs)
            except Exception as e:
                # Notes have been introduced in python 3.11
                if hasattr(e, "add_note"):
                    e.add_note(traceback.format_exc())
                logger.exception("WorkerProc hit an exception.")
                # exception might not be serializable, so we convert it to
                # string, only for logging purpose.
                if output_rank is None or self.rank == output_rank:
                    self.handle_output(e)
                continue

            if output_rank is None or self.rank == output_rank:
                self.handle_output(output)

三十、WorkerWrapperBase vs WorkerProc 详解

30.1 问题的本质

worker_busy_loop 中,RPC 调用最终落到 self.worker 上:

python 复制代码
# multiproc_executor.py L870-871
func = getattr(self.worker, method)  # self.worker 是 WorkerWrapperBase!
output = func(*args, **kwargs)

这里的 self.workerWorkerWrapperBase ,而不是 WorkerBase(真正的 GPU Worker)。为什么要多这一层?

30.2 三层架构总览

复制代码
┌──────────────────────────────────────────────────────┐
│                    WorkerProc                        │ ← 进程管理层
│  职责: 进程生命周期, MQ通信, 信号处理, 异步输出线程     │
│                                                      │
│  ┌──────────────────────────────────────────────┐    │
│  │            WorkerWrapperBase                  │    │ ← 代理/装饰层
│  │  职责: 懒初始化, 环境配置, MM缓存, 方法拦截     │    │
│  │                                              │    │
│  │  ┌──────────────────────────────────────┐    │    │
│  │  │     Worker (GPU Worker)              │    │    │ ← 业务实现层
│  │  │  职责: GPU操作, 模型加载/执行, KV缓存  │    │    │
│  │  │     继承自 WorkerBase                │    │    │
│  │  └──────────────────────────────────────┘    │    │
│  └──────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────┘

30.3 类关系图

继承
包含 self.worker
包含 self.worker
getattr 代理
<<abstract>>
WorkerBase
+vllm_config
+local_rank, rank
+device, model_runner
+init_device()
+load_model()
+execute_model(scheduler_output)
+sample_tokens(grammar_output)
+initialize_cache(num_gpu, num_cpu)
+get_kv_cache_spec()
+compile_or_warm_up_model()
+get_cache_block_size_bytes()
+add_lora() / remove_lora() / pin_lora()
+check_health()
+shutdown()
+reset_mm_cache()
+apply_model(fn)
Worker
+model_runner: GPUModelRunner
+init_device()
+load_model()
+execute_model()
+sample_tokens()
+initialize_cache()
+get_kv_cache_spec()
+execute_dummy_batch()
WorkerWrapperBase
+rpc_rank: int
+global_rank: int
+worker: WorkerBase
+mm_receiver_cache
+init_worker(all_kwargs)
+update_environment_variables()
+execute_method(method)
+getattr(attr) : → self.worker
+execute_model() : 拦截
+init_device() : 拦截
+reset_mm_cache() : 拦截
+_apply_mm_cache()
+shutdown()
WorkerProc
+rank: int
+worker: WorkerWrapperBase
+rpc_broadcast_mq: MessageQueue
+worker_response_mq: MessageQueue
+use_async_scheduling: bool
+async_output_queue
+async_output_copy_thread
+worker_busy_loop()
+handle_output()
+enqueue_output()
+make_worker_process()
+worker_main()
+shutdown()

30.4 WorkerWrapperBase 全部方法一览

方法 行号 类型 作用
__init__(rpc_rank, global_rank) L188 初始化 记录 rank,不创建真实 Worker
shutdown() L210 生命周期 调用 self.worker.shutdown()
adjust_rank(rank_mapping) L214 初始化 调整 rpc_rank(SPMD 场景)
update_environment_variables(envs_list) L223 初始化 设置该 rank 的环境变量
init_worker(all_kwargs) L231 核心 懒初始化:解析 worker 类名 → 实例化 Worker
initialize_from_config(kv_cache_configs) L316 拦截 按 global_rank 选配置,注入 vllm_config 上下文
init_device() L322 拦截 注入 set_current_vllm_config 上下文后委托
execute_method(method, *args, **kwargs) L328 RPC 入口 通过 run_method 分发调用
__getattr__(attr) L347 代理 未定义的属性/方法 → 转发给 self.worker
_apply_mm_cache(scheduler_output) L350 多模态 将共享内存中的 MM features 注入请求
execute_model(scheduler_output) L360 拦截 _apply_mm_cache,再委托 Worker
reset_mm_cache() L367 拦截 清理 MM receiver cache + Worker cache

30.5 为什么不是 WorkerProc 直接持有 Worker?

关键原因:WorkerWrapperBase 是跨 Executor 通用的,WorkerProc 只在多进程 Executor 中使用。

复制代码
MultiprocExecutor  ──→  WorkerProc  ──→  WorkerWrapperBase  ──→  Worker
RayExecutor        ──→  RayWorker   ──→  WorkerWrapperBase  ──→  Worker
UniProcExecutor    ──→  (直接)      ──→  WorkerWrapperBase  ──→  Worker

不同的 Executor 后端有不同的进程/通信管理方式,但都需要:

  • 懒初始化 Worker
  • 环境变量设置
  • 多模态缓存拦截
  • vllm_config 上下文注入

如果把这些逻辑放在 WorkerProc 里,RayExecutorUniProcExecutor 就得重复实现。

30.6 职责对比

职责 WorkerProc WorkerWrapperBase Worker (GPU)
进程创建/销毁 make_worker_process, worker_main
信号处理 (SIGTERM) signal_handler
父进程死亡检测 death_pipe + monitor
MessageQueue 通信 rpc_broadcast_mq, worker_response_mq
RPC 消息循环 worker_busy_loop
异步输出线程 async_output_copy_thread
Worker 类解析/实例化 init_worker
环境变量注入 update_environment_variables
vllm_config 上下文 init_device, initialize_from_config
多模态缓存拦截 _apply_mm_cache, execute_model
__getattr__ 代理 ✅ 透明转发给 Worker
Worker 扩展类注入 ✅ 动态 mixin __bases__
GPU 初始化 init_device
模型加载/推理 load_model, execute_model
KV Cache 管理 initialize_cache
LoRA 管理 add_lora, remove_lora

30.7 __getattr__ 代理模式的工作原理

python 复制代码
# WorkerWrapperBase L347-348
def __getattr__(self, attr: str):
    return getattr(self.worker, attr)

当 Executor 调用 collective_rpc("some_method") 时,worker_busy_loop 执行:

python 复制代码
func = getattr(self.worker, "some_method")
# self.worker 是 WorkerWrapperBase
#   1. Python 先在 WorkerWrapperBase 类及其 MRO 中查找 "some_method"
#   2. 如果找到(如 execute_model, init_device)→ 调用 WorkerWrapperBase 的版本(拦截)
#   3. 如果没找到 → 触发 __getattr__ → getattr(self.worker, "some_method")
#      → 转发给 Worker(GPU Worker)

示例------哪些被拦截,哪些被透传:

方法调用 路径 原因
execute_model(so) WorkerWrapperBase.execute_model → _apply_mm_cache → Worker.execute_model 需要先注入 MM 缓存
init_device() WorkerWrapperBase.init_device → with set_current_vllm_config → Worker.init_device 需要设置全局配置上下文
reset_mm_cache() WorkerWrapperBase.reset_mm_cache → clear receiver cache → Worker.reset_mm_cache 需要额外清理 receiver cache
load_model() 透传__getattr__ → Worker.load_model 不需要额外处理
sample_tokens(g) 透传__getattr__ → Worker.sample_tokens 不需要额外处理
check_health() 透传__getattr__ → Worker.check_health 不需要额外处理
add_lora(req) 透传__getattr__ → Worker.add_lora 不需要额外处理
get_kv_cache_spec() 透传__getattr__ → Worker.get_kv_cache_spec 不需要额外处理

30.8 设计模式分析

设计模式 体现 好处
Proxy(代理) WorkerWrapperBase.__getattr__self.worker 透明转发,调用者不感知中间层
Decorator(装饰器) execute_model 先注入 MM cache 再委托 不修改 Worker 代码就增加新行为
Lazy Initialization init_worker() 延迟创建真正的 Worker 允许先配置环境再初始化
Template Method init_worker 中的固定流程(加载插件→解析类→扩展→实例化) 标准化 Worker 创建流程
Strategy parallel_config.worker_cls 字符串 → 动态解析 Worker 类 运行时切换 GPU/CPU/TPU Worker
Mixin worker_class.__bases__ += (worker_extension_cls,) 动态扩展 Worker 能力

30.9 初始化流程时序图

Worker (GPU) WorkerWrapperBase WorkerProc MultiprocExecutor Worker (GPU) WorkerWrapperBase WorkerProc MultiprocExecutor _init_executor() 仅记录 rank,不创建 Worker getattr 透传 make_worker_process() 创建子进程 worker_main() 入口 WorkerWrapperBase(rpc_rank, global_rank) init_worker(all_kwargs) 解析 worker_cls 字符串 处理 worker_extension_cls (Mixin) 创建 mm_receiver_cache Worker(**kwargs) self.worker = Worker实例 self.worker = wrapper (WorkerWrapperBase) _init_message_queues() init_device() with set_current_vllm_config worker.init_device() 初始化 GPU 设备、NCCL load_model() worker.load_model() 加载模型权重 READY 信号 worker_busy_loop() 开始接收 RPC

30.10 RPC 调用方法解析流程

是: execute_model, init_device...
否: load_model, sample_tokens...
Executor: collective_rpc('method_name')
rpc_broadcast_mq.enqueue()
WorkerProc.worker_busy_loop()
getattr(self.worker, method_name)
method_name 在

WorkerWrapperBase

中定义?
调用 WorkerWrapperBase 的方法

(拦截 + 增强)
触发 getattr

→ getattr(self.worker, method_name)

→ Worker 的方法
执行方法,返回结果

30.11 为什么这样设计?------ 总结

  1. 关注点分离(Separation of Concerns)

    • WorkerProc = 怎么运行(进程、MQ、信号)
    • WorkerWrapperBase = 怎么管理(初始化、配置、拦截)
    • Worker = 做什么(GPU 计算、模型推理)
  2. 跨 Executor 复用

    • WorkerWrapperBaseMultiprocExecutorRayExecutorUniProcExecutor 共同使用
    • 只有 WorkerProcMultiprocExecutor 独有的
  3. 开闭原则(Open-Closed Principle)

    • 新增 Worker 类型(如 TPU Worker)?只需实现 WorkerBaseWorkerWrapperBase 自动代理
    • 新增拦截逻辑(如 MM 缓存)?只需修改 WorkerWrapperBase,不动 Worker
    • 新增执行后端(如新的分布式框架)?只需写新的 "Proc" 类,复用 WorkerWrapperBase
  4. 动态扩展

    • worker_extension_cls 通过 Mixin 动态注入方法到 Worker 类
    • worker_cls 通过字符串在运行时解析为具体类
    • 这些都在 WorkerWrapperBase.init_worker() 中完成,与进程管理无关

30.12 代码文件引用

文件 行号 关键内容
vllm/v1/worker/worker_base.py L34-177 WorkerBase 抽象基类全部方法
vllm/v1/worker/worker_base.py L179-373 WorkerWrapperBase 完整实现
vllm/v1/worker/worker_base.py L347 __getattr__ 代理方法
vllm/v1/worker/worker_base.py L231-314 init_worker 懒初始化 + Mixin
vllm/v1/worker/worker_base.py L360-365 execute_model 拦截(MM cache 注入)
vllm/v1/executor/multiproc_executor.py L506-925 WorkerProc 完整实现
vllm/v1/executor/multiproc_executor.py L557 wrapper = WorkerWrapperBase(...) 创建
vllm/v1/executor/multiproc_executor.py L862-888 worker_busy_loop RPC 分发
vllm/v1/worker/gpu_worker.py L102-120 Worker (GPU) 继承 WorkerBase
vllm/v1/serial_utils.py L435-459 run_method 方法解析逻辑

三十一、WorkerProc / WorkerWrapperBase / Worker 完整架构分析

本节从设计模式和跨 Executor 复用角度,全面分析三者的区别、联系、以及"为什么要加一层 Wrapper"。

31.1 一句话定位

一句话定位 文件
WorkerBase 硬件抽象接口------"一个 Worker 能做什么" worker_base.py L34
Worker (GPU) 具体硬件实现------"GPU 上怎么做" gpu_worker.py L102
WorkerWrapperBase 生命周期代理------"怎么创建和装饰 Worker" worker_base.py L179
RayWorkerWrapper Ray 专用装饰------"Ray Actor 中怎么包装 Worker" ray_utils.py L41
WorkerProc 进程容器------"怎么在独立进程中运行 Worker" multiproc_executor.py L506

31.2 完整类层次关系图

继承
持有 self.worker
继承
持有 self.worker
直接持有
管理进程
管理Actor
<<abstract 硬件接口>>
WorkerBase
+init_device()
+load_model()
+execute_model(scheduler_output)
+sample_tokens(grammar_output)
+initialize_cache(gpu, cpu)
+get_kv_cache_spec()
+compile_or_warm_up_model()
+check_health()
+shutdown()
+add_lora() / remove_lora()
+apply_model(fn)
+reset_mm_cache()
<<GPU 实现>>
Worker
+model_runner: GPUModelRunner
+init_device() : 初始化CUDA+NCCL
+load_model() : 加载权重
+execute_model() : PP流水线
+sample_tokens() : 转发ModelRunner
<<生命周期代理>>
WorkerWrapperBase
+rpc_rank: int
+global_rank: int
+worker: WorkerBase
+mm_receiver_cache

--拦截方法--
+init_worker(all_kwargs)
+update_environment_variables()
+adjust_rank()
+execute_method()
+getattr() : → self.worker
+execute_model() : 注入MM缓存
+init_device() : 注入Config上下文
+reset_mm_cache() : 清理Receiver缓存
+initialize_from_config() : 按rank选配置
<<Ray Actor 装饰>>
RayWorkerWrapper
+compiled_dag_cuda_device_set
+get_node_ip()
+get_node_and_gpu_ids()
+setup_device_if_necessary()
+execute_model_ray()
<<进程容器>>
WorkerProc
+worker: WorkerWrapperBase
+rpc_broadcast_mq
+worker_response_mq
+async_output_queue
+async_output_copy_thread

+worker_busy_loop()
+handle_output()
+make_worker_process()
+worker_main()
UniProcExecutor
+driver_worker: WorkerWrapperBase
+collective_rpc() : → run_method
MultiprocExecutor
+workers: list<WorkerProcHandle>
+collective_rpc() : → MQ RPC
RayDistributedExecutor
+workers: list<RayWorkerWrapper>
+collective_rpc() : → ray.remote

31.3 三个 Executor 如何使用 WorkerWrapperBase

这是"为什么要加 Wrapper 层"的核心答案

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    UniProcExecutor                           │
│  self.driver_worker = WorkerWrapperBase(rpc_rank=0)         │
│  collective_rpc → run_method(driver_worker, method)         │
│  (单进程, 直接调用)                                          │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                    MultiprocExecutor                         │
│  WorkerProc 子进程 {                                         │
│    self.worker = WorkerWrapperBase(rpc_rank=local_rank)     │
│    worker_busy_loop: getattr(self.worker, method)           │
│  }                                                          │
│  collective_rpc → MessageQueue → WorkerProc.worker_busy_loop│
│  (多进程, MQ通信)                                            │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                    RayDistributedExecutor                    │
│  Ray Actor = RayWorkerWrapper(rpc_rank=rank)  ← 继承自 WWB  │
│  collective_rpc → worker.execute_method.remote(method)      │
│  (Ray Actor, gRPC通信)                                       │
└─────────────────────────────────────────────────────────────┘

关键洞察: 三种 Executor 使用完全不同的通信机制(直接调用 / MQ / Ray gRPC),但都通过 WorkerWrapperBase 统一了 Worker 的创建和管理逻辑。如果没有这一层,以下逻辑需要在每个 Executor 中重复实现:

逻辑 复用位置
worker_cls 字符串动态解析 Worker 类 init_worker() L251
动态 Mixin worker_extension_cls init_worker() L262
多模态 Receiver Cache 创建 init_worker() L290
execute_model 前注入 MM 缓存 execute_model() L360
set_current_vllm_config 上下文注入 init_device() L322, initialize_from_config() L316
global_rank 选择配置 initialize_from_config() L317
__getattr__ 透明代理 L347

31.4 设计模式分析

  1. Strategy 策略模式
    Executor 选择通信策略
    UniProc: 直接调用
    Multiproc: MessageQueue
    Ray: gRPC Remote
  2. Mixin 动态混入
    worker_class.bases +=

(worker_extension_cls,)
4. Template Method 模板方法
init_worker() 固定流程

  1. 加载插件
  2. 解析 worker_cls
  3. 处理 extension_cls
  4. 创建 MM cache
  5. 实例化 Worker
  6. Factory Method 工厂方法
    init_worker()
    resolve_obj_by_qualname(worker_cls)
    GPU Worker
    CPU Worker
    TPU Worker
  7. Decorator 装饰器模式
    WorkerWrapperBase.execute_model()
    _apply_mm_cache()
    Worker.execute_model()
  8. Proxy 代理模式
    getattr(attr)

透明转发
WorkerWrapperBase
Worker (GPU)

模式详解:

1. Proxy(代理模式) --- __getattr__ 让 WorkerWrapperBase 对外表现得像一个 Worker:

python 复制代码
def __getattr__(self, attr):
    return getattr(self.worker, attr)  # 调用者不知道中间有代理

2. Decorator(装饰器模式) --- 拦截方法,在原始行为前后注入新逻辑:

python 复制代码
def execute_model(self, scheduler_output):
    self._apply_mm_cache(scheduler_output)  # ← 新增行为
    return self.worker.execute_model(scheduler_output)  # ← 原始行为

3. Factory Method(工厂方法) --- 根据配置字符串创建不同硬件的 Worker:

python 复制代码
worker_class = resolve_obj_by_qualname(parallel_config.worker_cls)
# "vllm.v1.worker.gpu_worker.Worker" → GPU Worker
# "vllm.worker.cpu_worker.CPUWorker" → CPU Worker
# "vllm.worker.tpu_worker.TPUWorker" → TPU Worker
self.worker = worker_class(**kwargs)

4. Template Method(模板方法) --- init_worker() 定义固定初始化流程:

复制代码
加载插件 → 解析worker_cls → 处理extension_cls → 创建MM cache → 实例化Worker

5. Mixin(动态混入) --- 运行时扩展 Worker 类:

python 复制代码
worker_class.__bases__ = worker_class.__bases__ + (worker_extension_cls,)

6. Strategy(策略模式) --- 不同 Executor 用不同通信策略,但统一使用 WorkerWrapperBase:

复制代码
UniProcExecutor → run_method(self.driver_worker, method)
MultiprocExecutor → rpc_broadcast_mq → getattr(self.worker, method)
RayExecutor → worker.execute_method.remote(method)

31.5 complete RPC 调用链路对比时序图

Worker (GPU) WorkerWrapperBase WorkerProc (仅Multiproc) Executor EngineCore Worker (GPU) WorkerWrapperBase WorkerProc (仅Multiproc) Executor EngineCore UniProcExecutor 路径(单进程直接调用) getattr(self, "execute_model") 命中 WWB 自身定义 MultiprocExecutor 路径(跨进程 MQ) 子进程 worker_busy_loop 命中 WWB.execute_model (拦截) async_output_copy_thread RayDistributedExecutor 路径(Ray gRPC) RayWorkerWrapper.execute_method() getattr(self, "execute_model") 命中 WWB.execute_model (拦截) collective_rpc("execute_model", args) run_method(driver_worker, "execute_model", args) _apply_mm_cache(scheduler_output) self.worker.execute_model(scheduler_output) ModelRunnerOutput return output [output] collective_rpc("execute_model", args) rpc_broadcast_mq.enqueue(method, args) FutureWrapper (non_block) rpc_broadcast_mq.dequeue() getattr(self.worker, "execute_model") _apply_mm_cache() self.worker.execute_model() AsyncModelRunnerOutput return output handle_output → async_output_queue worker_response_mq.enqueue(output) collective_rpc("execute_model", args) worker.execute_method.remote("execute_model", args) run_method(self, "execute_model", args) _apply_mm_cache() self.worker.execute_model() ModelRunnerOutput ray.get() 获取结果 [output]

31.6 方法拦截 vs 透传 详细流程图

Worker (GPU)
WorkerWrapperBase
透传方法 (触发 getattr)
拦截方法 (定义在 WWB 上)
execute_model
init_device
reset_mm_cache
initialize_from_config
load_model
sample_tokens
check_health
get_kv_cache_spec
add_lora
execute_dummy_batch
委托
委托
委托
委托
RPC: method_name + args
getattr(self, method_name)
method_name 在

WWB 中定义?
execute_model()

→ _apply_mm_cache + 委托
init_device()

→ set_current_vllm_config + 委托
reset_mm_cache()

→ 清理receiver + 委托
initialize_from_config()

→ 按rank选配置 + 委托
getattr(method_name)

→ getattr(self.worker, method_name)
load_model()
sample_tokens()
check_health()
get_kv_cache_spec()
add_lora()
execute_dummy_batch()
Worker.execute_model()
Worker.init_device()
Worker.reset_mm_cache()
Worker.initialize_from_config()

31.7 初始化完整时序

Worker (GPU) WorkerWrapperBase WorkerProc (子进程) Executor 主进程 (EngineCore) Worker (GPU) WorkerWrapperBase WorkerProc (子进程) Executor 主进程 (EngineCore) 只记录 rank,self.worker 未赋值 getattr 透传 RayWorkerWrapper 继承 WorkerWrapperBase alt [UniProcExecutor] [MultiprocExecutor] [RayDistributedExecutor] Executor(vllm_config) _init_executor() WorkerWrapperBase(rpc_rank=0) init_worker([kwargs]) resolve_obj_by_qualname("...gpu_worker.Worker") 处理 worker_extension_cls (Mixin) 创建 mm_receiver_cache Worker(**kwargs) self.worker = Worker实例 init_device() with set_current_vllm_config: worker.init_device() load_model() worker.load_model() make_worker_process() spawn worker_main() 入口 WorkerWrapperBase(rpc_rank, global_rank) init_worker([kwargs]) Worker(**kwargs) self.worker = wrapper init_device() worker.init_device() load_model() worker.load_model() READY 信号 (via Pipe) worker_busy_loop() 等待 RPC ray.remote(RayWorkerWrapper).remote(rpc_rank) collective_rpc("init_worker", args=all_kwargs) Worker(**kwargs) collective_rpc("init_device") worker.init_device() collective_rpc("load_model") worker.load_model()

31.8 为什么要加 WorkerWrapperBase ------ 总结

复制代码
如果没有 WorkerWrapperBase,会怎样?

UniProcExecutor._init_executor():
    worker_cls = resolve_obj_by_qualname(config.worker_cls)  # 重复!
    if config.worker_extension_cls: ...                       # 重复!
    mm_cache = create_mm_cache(...)                           # 重复!
    self.worker = worker_cls(**kwargs)                        # 重复!

MultiprocExecutor (in WorkerProc):
    worker_cls = resolve_obj_by_qualname(config.worker_cls)  # 重复!
    if config.worker_extension_cls: ...                       # 重复!
    mm_cache = create_mm_cache(...)                           # 重复!
    self.worker = worker_cls(**kwargs)                        # 重复!

RayDistributedExecutor (in RayWorker):
    worker_cls = resolve_obj_by_qualname(config.worker_cls)  # 重复!
    if config.worker_extension_cls: ...                       # 重复!
    mm_cache = create_mm_cache(...)                           # 重复!
    self.worker = worker_cls(**kwargs)                        # 重复!

加了 WorkerWrapperBase 后:

复制代码
WorkerWrapperBase.init_worker():  ← 统一一份代码
    worker_cls = resolve_obj_by_qualname(config.worker_cls)
    if config.worker_extension_cls: 动态 Mixin
    mm_cache = create_mm_cache(...)
    self.worker = worker_cls(**kwargs)

UniProcExecutor  → WorkerWrapperBase → Worker
MultiprocExecutor → WorkerProc → WorkerWrapperBase → Worker
RayExecutor → RayWorkerWrapper(继承WWB) → Worker

核心价值:

原则 体现
DRY (Don't Repeat Yourself) Worker 初始化逻辑只写一次
SRP (单一职责) WorkerProc=进程管理, WWB=Worker生命周期, Worker=GPU计算
OCP (开闭原则) 新增硬件→只加 Worker 子类; 新增拦截逻辑→只改 WWB; 新增执行后端→只加 Proc/Executor
DIP (依赖倒置) Executor 依赖 WorkerWrapperBase 接口,不直接依赖 GPU Worker
透明代理 __getattr__ 让上层完全不感知 Wrapper 的存在

31.9 代码文件引用

文件 行号 关键内容
vllm/v1/worker/worker_base.py L34-177 WorkerBase --- 硬件抽象接口
vllm/v1/worker/worker_base.py L179-373 WorkerWrapperBase --- 生命周期代理
vllm/v1/worker/gpu_worker.py L102 Worker --- GPU 具体实现
vllm/v1/executor/multiproc_executor.py L506-925 WorkerProc --- 多进程容器
vllm/v1/executor/uniproc_executor.py L25-129 UniProcExecutor --- 单进程直接使用 WWB
vllm/v1/executor/ray_executor.py L62-511 RayDistributedExecutor --- Ray 使用 RayWorkerWrapper
vllm/v1/executor/ray_utils.py L41-120 RayWorkerWrapper --- 继承 WWB 的 Ray 子类
vllm/v1/executor/abstract.py L36-191 Executor --- 抽象基类,定义 collective_rpc
vllm/config/parallel.py L215-225 worker_clsworker_extension_cls 配置
vllm/v1/serial_utils.py L435-459 run_method --- 方法分发工具
相关推荐
hongyuyahei7 小时前
绝对/相对位置编码
vllm
d1z8881 天前
(十七)32天GPU测试从入门到精通-vLLM 部署与性能测试day15
服务器·显卡·nvidia·vllm
谢白羽3 天前
vllm抢占机制详解
算法·vllm
从零开始学习人工智能3 天前
vLLM 多卡部署技巧:如何单独降低某张 GPU 的显存占用
vllm
执笔论英雄4 天前
【vllm】PD分离
vllm
谢白羽4 天前
多集群/分布式 LLM 推理方案全景:2026 年选型指南
分布式·vllm·sglang·llm-d
x²+(y-√³x²)²=15 天前
Linux 或者 Ubuntu 离线使用 vllm启动大模型
linux·ubuntu·vllm
HyperAI超神经6 天前
【TVM教程】理解 Relax 抽象层
人工智能·深度学习·学习·机器学习·gpu·tvm·vllm
晨欣6 天前
单卡 48GB 实测:Gemma 4 26B A4B、Gemma 4 31B、gpt-oss-20b 三模型部署与并发对比
google·openai·nvidia·vllm·llama.cpp·gpt-oss-20b·gemma4