vLLM v1 KV Offload 模块 — 超深度架构分析(一):模块定位与整体结构

分析基于源码目录:github.com/vllm/vllm/v1/kv_offload
总计 16 个 Python 文件,约 1,398 行代码


一、模块定位

图 1:系统上下文图 — kv_offload 在 vLLM 中的位置

vLLM v1 System

Hardware

kv_offload 模块

Worker (数据面)

Scheduler (控制面)

API Layer

LoadStoreSpec

装饰器

swap_blocks_batch

mmap

GPU VRAM
(有限, ~48GB)

Scheduler Core
请求调度/资源分配

OffloadingManager
块状态跟踪/驱逐决策

Worker Core
执行推理计算

OffloadingWorker
Handler 路由器

OffloadingHandler
DMA 传输执行

API Server
接收推理请求

CPUOffloadingManager

CachePolicy
LRU/ARC

FilterReused
OffloadingManager

OffloadingSpec
规格工厂

SingleDirection
OffloadingHandler

SharedOffloadRegion
mmap 共享内存

DMA Engine
(GPU Direct)

CPU DRAM
(充裕, ~256GB+)


1.1 业务职责

kv_offload 模块是 vLLM v1 架构中 KV Cache 卸载(Offloading) 子系统的完整实现。其核心业务职责是:

职责维度 描述
KV Cache 换出(GPU → CPU) 将 GPU 显存中暂时不用的 KV Cache 块搬移到 CPU 内存,释放 GPU 显存供活跃请求使用
KV Cache 换入(CPU → GPU) 当需要重用已卸载的 KV Cache 时,将其从 CPU 内存加载回 GPU 显存
块级缓存管理 以 block 为粒度管理卸载数据的生命周期,包括分配、驱逐、引用计数、状态跟踪
替换策略 支持可插拔的缓存淘汰策略(LRU / ARC),决定哪些块应被驱逐
复用频率过滤 通过装饰器模式过滤低频复用的块,避免将只用一次的块卸载到 CPU
共享内存管理 通过 mmap 机制实现多 Worker 间的 CPU 内存共享,避免数据冗余
异步传输管线 基于 CUDA Stream + Event 的异步传输管线,保证传输顺序性同时不阻塞计算

1.2 功能定位

本模块解决的核心问题是 GPU 显存容量有限而 KV Cache 随请求增长 的矛盾:

┌──────────────────────────────────────────────────────────┐
│                    问题空间                                │
│                                                          │
│  GPU VRAM (有限)                    CPU DRAM (充裕)       │
│  ┌─────────┐                        ┌──────────────┐     │
│  │ Active  │  ←── 需要换出 ────→    │ Offloaded    │     │
│  │ KV Cache│  ←── 需要换入 ────→    │ KV Cache     │     │
│  └─────────┘                        └──────────────┘     │
│                                                          │
│  当 GPU KV Cache 满时:                                   │
│  ① 简单策略:直接丢弃 → 浪费计算(需重新计算)            │
│  ② 卸载策略:搬到 CPU → 需要时可恢复 → kv_offload 的价值  │
└──────────────────────────────────────────────────────────┘

1.3 在系统中的位置

kv_offload 模块横跨 vLLM 的 调度器(Scheduler)工作器(Worker) 两大组件,形成经典的 “控制面 + 数据面” 分离架构:

┌─────────────────────────────────────────────────────────────────┐
│                       vLLM v1 整体架构                           │
│                                                                 │
│  ┌─────────────────┐                      ┌──────────────────┐ │
│  │   Scheduler     │                      │   Worker(s)      │ │
│  │   (控制面)       │                      │   (数据面)        │ │
│  │                 │                      │                  │ │
│  │ ┌─────────────┐ │   OffloadingSpec     │ ┌──────────────┐ │ │
│  │ │Offloading   │ │ ──────────────────→  │ │Offloading    │ │ │
│  │ │Manager      │ │   (调度器侧管理器)    │ │Handler       │ │ │
│  │ │             │ │                      │ │              │ │ │
│  │ │ • lookup    │ │   LoadStoreSpec      │ │ • transfer_  │ │ │
│  │ │ • prepare_  │ │ ──────────────────→  │ │   async      │ │ │
│  │ │   load/store│ │   (传输规格)          │ │ • get_       │ │ │
│  │ │ • complete_ │ │                      │ │   finished   │ │ │
│  │ │   load/store│ │                      │ │ • wait       │ │ │
│  │ └─────────────┘ │                      │ └──────────────┘ │ │
│  │                 │                      │                  │ │
│  │  跟踪块状态      │     控制指令          │   执行实际传输    │ │
│  │  管理驱逐策略    │ ════════════════════→ │   GPU↔CPU DMA   │ │
│  │  引用计数保护    │                      │                  │ │
│  └─────────────────┘                      └──────────────────┘ │
│                                                                 │
│                    ↑ kv_offload 模块覆盖范围 ↑                   │
└─────────────────────────────────────────────────────────────────┘

关键交互路径:

  • Scheduler 通过 OffloadingManager 决策"哪些块需要卸载/加载"
  • Worker 通过 OffloadingHandler 执行"实际的 DMA 数据搬移"
  • OffloadingSpec 是连接两者的规格对象,负责创建 Manager 和 Handler
  • OffloadingSpecFactory 是工厂类,根据配置动态创建具体的 Spec

二、模块整体结构

2.1 目录结构

kv_offload/
├── __init__.py                    # 空包初始化文件
├── base.py                        # 核心抽象基类与数据结构定义(398行)
├── factory.py                     # OffloadingSpec 工厂(58行)
├── reuse_manager.py               # 复用频率过滤装饰器(120行)
├── cpu/                           # CPU 卸载实现子包
│   ├── __init__.py                # 空
│   ├── common.py                  # CPULoadStoreSpec 定义(13行)
│   ├── spec.py                    # CPUOffloadingSpec 实现(102行)
│   ├── manager.py                 # CPUOffloadingManager 实现(204行)
│   ├── gpu_worker.py              # GPU↔CPU 传输处理器(433行)
│   ├── shared_offload_region.py   # mmap 共享内存区域(192行)
│   └── policies/                  # 缓存替换策略子包
│       ├── __init__.py            # 空
│       ├── base.py                # CachePolicy 抽象 + BlockStatus(76行)
│       ├── lru.py                 # LRU 策略实现(46行)
│       └── arc.py                 # ARC 策略实现(156行)
└── worker/                        # Worker 侧抽象子包
    ├── __init__.py                # 空
    └── worker.py                  # OffloadingHandler/Worker 抽象(176行)

2.2 类结构总览

creates

_backing

_policy

«abstract»

LoadStoreSpec

+medium() : str

BlockIDsLoadStoreSpec

+block_ids: np.ndarray[int64]

+repr() : str

GPULoadStoreSpec

+group_sizes: Sequence<int>

+block_indices: Sequence<int>

+medium() : str "GPU"

CPULoadStoreSpec

+medium() : str "CPU"

«NewType bytes»

OffloadKey

ReqContext

+kv_transfer_params: dict | None

PrepareStoreOutput

+keys_to_store: list<OffloadKey>

+store_spec: LoadStoreSpec

+evicted_keys: list<OffloadKey>

OffloadingEvent

+keys: list<OffloadKey>

+medium: str

+removed: bool

«abstract»

OffloadingManager

+lookup(key, req_context) : bool|None

+prepare_load(keys, req_context) : LoadStoreSpec

+touch(keys) : void

+complete_load(keys) : void

+prepare_store(keys, req_context) : PrepareStoreOutput|None

+complete_store(keys, success) : void

+take_events() : Iterable<OffloadingEvent>

+shutdown() : void

«abstract»

OffloadingSpec

+vllm_config: VllmConfig

+kv_cache_config: KVCacheConfig

+hash_block_size: int

+gpu_block_size: tuple<int>

+block_size_factor: int

+get_manager() : OffloadingManager

+get_handlers(kv_caches) : Iterator

CanonicalKVCacheTensor

+tensor: torch.Tensor

+page_size_bytes: int

CanonicalKVCacheRef

+tensor_idx: int

+page_size_bytes: int

CanonicalKVCaches

+tensors: list<CanonicalKVCacheTensor>

+group_data_refs: list<list<CanonicalKVCacheRef>>

OffloadingSpecFactory

-_registry: dict<str, Callable>

+register_spec(name, module_path, class_name) : void

+create_spec(config, kv_cache_config) : OffloadingSpec

CPUOffloadingSpec

+num_blocks: int

+eviction_policy: str

-_manager: OffloadingManager|None

-_handlers: CpuGpuOffloadingHandlers|None

+get_manager() : OffloadingManager

+get_handlers(kv_caches) : Iterator

CPUOffloadingManager

+medium: str

-_num_blocks: int

-_num_allocated_blocks: int

-_free_list: list<int>

-_policy: CachePolicy

+events: list<OffloadingEvent>|None

+lookup(key, req_context) : bool|None

+prepare_load(keys, req_context) : LoadStoreSpec

+touch(keys) : void

+complete_load(keys) : void

+prepare_store(keys, req_context) : PrepareStoreOutput|None

+complete_store(keys, success) : void

+take_events() : Iterable<OffloadingEvent>

FilterReusedOffloadingManager

-_backing: OffloadingManager

+store_threshold: int

+max_tracker_size: int

+counts: OrderedDict<OffloadKey, int>

+lookup(key, req_context) : bool|None

+prepare_store(keys, req_context) : PrepareStoreOutput|None

BlockStatus

+ref_cnt: c_int32

+block_id: c_int64

+is_ready: bool

«abstract»

CachePolicy

+get(key) : BlockStatus|None

+insert(key, block) : void

+remove(key) : void

+touch(keys) : void

+evict(n, protected) : list|None

LRUCachePolicy

+blocks: OrderedDict<OffloadKey, BlockStatus>

ARCCachePolicy

+cache_capacity: int

+target_t1_size: float

+t1: OrderedDict<OffloadKey, BlockStatus>

+t2: OrderedDict<OffloadKey, BlockStatus>

+b1: OrderedDict<OffloadKey, None>

+b2: OrderedDict<OffloadKey, None>

«abstract»

OffloadingHandler

+transfer_async(job_id, spec) : bool

+get_finished() : list<TransferResult>

+wait(job_ids) : void

+shutdown() : void

OffloadingWorker

+handlers: set<OffloadingHandler>

+transfer_type_to_handler: dict

+register_handler(src_cls, dst_cls, handler) : void

+transfer_async(job_id, spec) : bool

+get_finished() : list<TransferResult>

+wait(job_ids) : void

+shutdown() : void

SingleDirectionOffloadingHandler

+src_tensors: list<torch.Tensor>

+dst_tensors: list<torch.Tensor>

+gpu_to_cpu: bool

-_transfers: deque<Transfer>

-_stream_pool: list<Stream>

-_event_pool: list<Event>

+transfer_async(job_id, spec) : bool

+get_finished() : list<TransferResult>

+wait(job_ids) : void

+shutdown() : void

CpuGpuOffloadingHandlers

+gpu_to_cpu_handler: SingleDirectionOffloadingHandler

+cpu_to_gpu_handler: SingleDirectionOffloadingHandler

SharedOffloadRegion

+page_size: int

+total_size_bytes: int

+mmap_path: str

+num_blocks: int

+rank: int|None

-_row_stride: int

-_worker_offset: int

-_worker_area_end: int

-_base: torch.Tensor

-_views: list<torch.Tensor>

+is_pinned: bool

+create_next_view(tensor_page_size) : torch.Tensor

+cleanup() : void

2.3 接口定义与依赖注入关系

2.3.1 核心接口矩阵
接口 位置 实现者 消费者 设计模式
OffloadingSpec base.py CPUOffloadingSpec Scheduler/Worker 初始化 工厂+策略
OffloadingManager base.py CPUOffloadingManager, FilterReusedOffloadingManager Scheduler 模板方法+装饰器
OffloadingHandler worker/worker.py SingleDirectionOffloadingHandler OffloadingWorker 策略
CachePolicy policies/base.py LRUCachePolicy, ARCCachePolicy CPUOffloadingManager 策略
LoadStoreSpec base.py GPULoadStoreSpec, CPULoadStoreSpec Manager→Handler 数据传递 值对象
2.3.2 依赖注入关系
OffloadingSpecFactory
  │ (根据配置创建)
  ▼
CPUOffloadingSpec ──────────────────────────────────────┐
  │                                                      │
  ├─ get_manager()                                       │ get_handlers()
  │   │                                                  │
  │   ▼                                                  ▼
  │  CPUOffloadingManager  ◄── FilterReusedOffloadingManager (可选装饰)
  │   │                         │                        CpuGpuOffloadingHandlers
  │   └── CachePolicy ◄────────┘ (策略注入)              │
  │       ├── LRUCachePolicy                              ├── gpu_to_cpu_handler
  │       └── ARCCachePolicy                              └── cpu_to_gpu_handler
  │                                                           │
  │                                                     SingleDirectionOffloadingHandler
  │                                                     (×2, 分别处理两个方向)
  │                                                           │
  │                                                     SharedOffloadRegion (可选)
  │                                                     (mmap 共享内存)

2.4 核心方法清单与作用

2.4.1 OffloadingManager(调度器侧 — 控制面)
方法 作用 调用时机
lookup(key, req_context) 检查块是否已卸载且可读 Scheduler 处理请求时
prepare_load(keys, req_context) 准备加载,增加引用计数保护 Scheduler 决定加载块时
touch(keys) 标记块最近被使用 前缀缓存命中时
complete_load(keys) 加载完成,释放引用计数 Worker 完成加载后
prepare_store(keys, req_context) 准备存储,可能触发驱逐 Scheduler 决定卸载块时
complete_store(keys, success) 存储完成/失败处理 Worker 完成存储后
take_events() 获取卸载事件流 事件监控
2.4.2 OffloadingHandler(工作器侧 — 数据面)
方法 作用 调用时机
transfer_async(job_id, spec) 提交异步 DMA 传输 Worker 收到传输指令时
get_finished() 查询已完成传输 Worker 轮询时
wait(job_ids) 阻塞等待指定传输完成 需要同步时
shutdown() 释放资源 Worker 关闭时
2.4.3 CachePolicy(策略层)
方法 作用
get(key) 查找块状态
insert(key, block) 插入新块
remove(key) 删除块
touch(keys) 标记最近使用
evict(n, protected) 驱逐 n 个块

2.5 内部调用关系

2.5.1 Store 流程(GPU → CPU 卸载)

图 4:Store 完整流程图(GPU → CPU 卸载)

Yes

No

Yes

No

Yes

No

未完成

完成

Scheduler 决定卸载块

OffloadingManager.prepare_store(keys, req_context)

过滤已存储的块:
keys_to_store = [k for k in keys if policy.get(k) is None]

keys_to_store
为空?

返回 PrepareStoreOutput(keys=[], spec, evicted=[])

计算空闲块数:
free = len(_free_list) + _num_blocks - _num_allocated

需要驱逐?
len(keys_to_store) > free

policy.evict(n, protected=keys)

驱逐成功?

返回 None (无法存储)

回收被驱逐块到 _free_list
记录 OffloadingEvent(removed=True)

分配 CPU 块:
_allocate_blocks(keys_to_store)

注册到策略:
policy.insert(key, block)
block.ref_cnt = -1 (写入中)

构建 CPULoadStoreSpec:
block_ids = [block.block_id for block in blocks]

返回 PrepareStoreOutput(keys_to_store, store_spec, evicted_keys)

Scheduler 发送传输指令给 Worker

Worker.transfer_async(job_id, (GPULoadStoreSpec, CPULoadStoreSpec))

OffloadingWorker 路由:
transfer_type = ('GPU', 'CPU')
→ gpu_to_cpu_handler

SingleDirectionOffloadingHandler.transfer_async()

提取 group_sizes, block_indices

计算子块指针:
compute_sub_block_ptrs()
for each group × each data_ref

从对象池获取 Stream + Events

设置依赖:
wait_stream(current_stream)
wait_event(last_transfer.end_event)

在专用 Stream 上执行:
ops.swap_blocks_batch(src_ptrs, dst_ptrs, sizes)

记录 start/end Event
Transfer 加入队列

返回 True

Worker 轮询 get_finished()

Transfer.end_event
.query()?

计算传输耗时
elapsed_time * 1e-3

回收 Stream/Event 到对象池

返回 TransferResult(job_id, success=True)

OffloadingManager.complete_store(keys, success=True)

block.ref_cnt = 0
(从 -1 变为就绪)

记录 OffloadingEvent(removed=False)

卸载完成


OffloadingHandler Worker CachePolicy OffloadingManager Scheduler OffloadingHandler Worker CachePolicy OffloadingManager Scheduler alt [需要驱逐] prepare_store(keys, req_context) get(k) [过滤已存储的块] _get_num_free_blocks() [检查空闲块] evict(n, protected) [选择驱逐块] evicted list _free_block() [回收块] 记录 OffloadingEvent(removed=True) _allocate_blocks() [分配新块] insert(key, block) [注册到策略] PrepareStoreOutput(keys_to_store, store_spec, evicted_keys) 传输指令 (job_id, GPU→CPU spec) transfer_async(job_id, spec) compute_sub_block_ptrs() [计算DMA地址] ops.swap_blocks_batch() [执行DMA] Transfer 加入队列 get_finished() → TransferResult 通知完成 complete_store(keys, success=True) get(key).is_ready → True [标记为可读] 记录 OffloadingEvent(removed=False)
2.5.2 Load 流程(CPU → GPU 加载)

图 5:Load 完整流程图(CPU → GPU 加载)

No

Yes

No

Yes

No

Yes

Scheduler 检查请求需要的块

lookup(key, req_context)

policy.get(key)

块在缓存中?

返回 False
(块未卸载,需重新计算)

block.is_ready?
(ref_cnt >= 0)

返回 None
(块正在写入,延迟请求)

返回 True
(块已就绪,可加载)

prepare_load(keys, req_context)

获取每个块的 BlockStatus:
block = policy.get(key)

block.ref_cnt += 1
(防止加载期间被驱逐)

构建 CPULoadStoreSpec:
block_ids = [block.block_id]

返回 LoadStoreSpec

Scheduler 发送传输指令

Worker.transfer_async(job_id, (CPULoadStoreSpec, GPULoadStoreSpec))

OffloadingWorker 路由:
transfer_type = ('CPU', 'GPU')
→ cpu_to_gpu_handler

SingleDirectionOffloadingHandler.transfer_async()

提取 group_sizes, block_indices
(从 GPU spec 获取)

计算子块指针
(src=CPU, dst=GPU)

如果前面有未完成的 CPU→GPU 传输:
wait_event(last_transfer.end_event)

ops.swap_blocks_batch()

记录 Transfer

Worker 轮询 get_finished()

传输完成?

返回 TransferResult

complete_load(keys)

block.ref_cnt -= 1
(解除驱逐保护)

加载完成


OffloadingHandler Worker CachePolicy OffloadingManager Scheduler OffloadingHandler Worker CachePolicy OffloadingManager Scheduler alt [块不存在] [块未就绪(正在写入)] [块已就绪] lookup(key, req_context) get(key) BlockStatus False None [延迟请求] True prepare_load(keys, req_context) get(key) block.ref_cnt += 1 [保护不被驱逐] LoadStoreSpec (CPU块ID列表) 传输指令 (job_id, CPU→GPU spec) transfer_async(job_id, spec) compute_sub_block_ptrs() ops.swap_blocks_batch() Transfer 加入队列 get_finished() → TransferResult 通知完成 complete_load(keys) block.ref_cnt -= 1 [解除保护]

2.6 数据流入流出方式

2.6.1 数据流全景
                    ┌─────────────────────┐
                    │   VllmConfig        │
                    │   KVCacheConfig     │
                    └────────┬────────────┘
                             │
                    ┌────────▼────────────┐
                    │ OffloadingSpecFactory│  ← 输入:配置对象
                    │  .create_spec()      │  → 输出:OffloadingSpec 实例
                    └────────┬────────────┘
                             │
              ┌──────────────┼──────────────┐
              ▼              │              ▼
    ┌─────────────────┐     │     ┌──────────────────┐
    │ OffloadingManager│     │     │OffloadingHandler  │
    │ (Scheduler 侧)   │     │     │ (Worker 侧)       │
    │                   │     │     │                    │
    │ 输入: OffloadKey  │     │     │ 输入: TransferSpec │
    │       ReqContext  │     │     │       job_id      │
    │ 输出: LoadStoreSpec│     │     │ 输出: TransferResult│
    │       PrepareStore│     │     │                    │
    │       Output      │     │     │                    │
    └─────────────────┘     │     └──────────────────┘
                            │
                            ▼  LoadStoreSpec 作为桥梁
                   连接控制面(哪些块)与数据面(如何搬移)
2.6.2 关键数据结构流转
数据结构 创建者 消费者 传递方向
OffloadKey (bytes) Scheduler Manager → Policy 控制面内部
LoadStoreSpec Manager → Worker Handler 跨控制/数据面
GPULoadStoreSpec Manager Handler 包含 block_ids + group_sizes + block_indices
CPULoadStoreSpec Manager Handler 包含 block_ids
TransferSpec (src_spec, dst_spec) Scheduler/Worker Handler 数据面内部
TransferResult Handler Worker → Scheduler 数据面→控制面
PrepareStoreOutput Manager Scheduler 控制面内部
OffloadingEvent Manager 外部监控系统 控制面→外部

三、模块层级关系与职责边界

3.1 四层架构

┌────────────────────────────────────────────────────────┐
│  Layer 4: 配置与工厂层 (factory.py)                      │
│  职责:根据运行时配置创建具体 Spec                         │
│  关键类:OffloadingSpecFactory                           │
├────────────────────────────────────────────────────────┤
│  Layer 3: 规格层 (base.py + cpu/spec.py)                 │
│  职责:定义跨 Scheduler/Worker 的抽象契约                  │
│  关键类:OffloadingSpec, OffloadingManager,              │
│         LoadStoreSpec, CanonicalKVCaches                │
├────────────────────────────────────────────────────────┤
│  Layer 2: 策略与装饰器层 (policies/ + reuse_manager.py)  │
│  职责:可插拔的缓存淘汰策略 + 复用频率过滤                   │
│  关键类:CachePolicy, LRUCachePolicy, ARCCachePolicy,   │
│         FilterReusedOffloadingManager                   │
├────────────────────────────────────────────────────────┤
│  Layer 1: 传输与存储层 (gpu_worker.py +                  │
│           shared_offload_region.py + worker/worker.py)  │
│  职责:实际 DMA 传输 + CPU 内存分配 + 共享内存             │
│  关键类:SingleDirectionOffloadingHandler,               │
│         CpuGpuOffloadingHandlers, SharedOffloadRegion   │
└────────────────────────────────────────────────────────┘

3.2 职责边界

组件 知道什么 不知道什么
OffloadingManager 块的存在性、位置、引用计数、驱逐策略 具体传输方式、CUDA 操作、内存布局
CachePolicy 块的访问顺序、驱逐候选 引用计数、DMA 传输、事件系统
OffloadingHandler GPU/CPU 张量地址、CUDA Stream 块的业务语义、驱逐决策
SharedOffloadRegion mmap 文件、内存布局、Worker 间偏移 块的生命周期、缓存策略
FilterReusedOffloadingManager 块的访问频率 块的物理存储位置、驱逐算法细节

图:组件依赖关系图

数据结构

存储层

Worker 侧

调度器侧

规格层

工厂层

配置层

装饰器

桥梁

桥梁

VllmConfig
KVCacheConfig

OffloadingSpecFactory

CPUOffloadingSpec

CPUOffloadingManager

FilterReusedOffloadingManager

LRUCachePolicy

ARCCachePolicy

OffloadingWorker

SingleDirectionHandler
(GPU→CPU)

SingleDirectionHandler
(CPU→GPU)

CpuGpuOffloadingHandlers

SharedOffloadRegion

OffloadKey

GPULoadStoreSpec /
CPULoadStoreSpec

BlockStatus

CanonicalKVCaches


四、设计模式总结

模式 应用位置 说明
工厂模式 OffloadingSpecFactory 延迟加载 + 注册表,根据配置名创建 Spec
策略模式 CachePolicy → LRU/ARC 可插拔的缓存淘汰算法
装饰器模式 FilterReusedOffloadingManager 包装底层 Manager,拦截 prepare_store
模板方法 OffloadingManager 的 prepare_store/complete_store 定义骨架流程,子类/策略填充细节
迭代器模式 CPUOffloadingSpec.get_handlers() yield 逐步返回 handler,支持多方向
对象池 SingleDirectionOffloadingHandler 的 stream/event pool 复用 CUDA Stream 和 Event,减少分配开销
生产者-消费者 Manager(生产 LoadStoreSpec) → Handler(消费并执行) 解耦决策与执行

文档一完成。接下来请参阅文档二(核心业务逻辑逐行解析)和文档三(架构图/流程图集合)。

Logo

免费领 100 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐