SGLang Server 与 Router#
通过一份 Hydra 配置启动一组 sglang HTTP server 与一个 sglang router——rollout (或任意外部客户端)只需访问一个 URL,由 router 将请求分发到各个引擎。
你将完成: 在 cluster.component_placement 中声明放置 → 填写一个参数块
(包含 sglang ServerArgs 与 RouterArgs 的字段)→ 调用
launch_sglang_router_and_server() → 用 InferenceHTTPClient
与 router URL 通信。
配置#
配置分两部分:cluster.component_placement 负责 在哪里 运行引擎;
参数块负责 每个引擎与 router 如何 配置。
cluster:
num_nodes: 1
component_placement:
rollout: all # <-- "rollout" 这个 key 是任意的,含义见下文
router_server_args: # <-- 顶层 key 名字也是任意的,含义见下文
model_path: /path/to/hf_model
tensor_parallel_size: 2
pipeline_parallel_size: 1
group_name: SGLangServerGroup
launch_server: True
server: # 原样作为 ServerArgs(**) 的参数
model_path: ${..model_path}
tp_size: ${..tensor_parallel_size}
pp_size: ${..pipeline_parallel_size}
mem_fraction_static: 0.85
max_running_requests: 64
attention_backend: triton
log_level: warning
router_group_name: SGLangRouterGroup
launch_router: True
router: # 原样作为 launch_router 的 CLI flag
policy: cache_aware
log_level: warn
worker_startup_timeout_secs: 1800
request_timeout_secs: 1800
组件名(rollout 这个 key)#
cluster.component_placement 下的 key(上面的 rollout)**只是一个标签**,
按 runner 的习惯起名即可。Launcher 不会按固定名字去查找这个 key;驱动脚本
负责挑选名字,并在三处使用 同一字符串:
作为
cluster.component_placement下的 key。向 placement 询问引擎硬件 rank:
placement.get_hardware_ranks(component_name)。读取该组件的 node-group 标签(若有):
cfg.cluster.component_placement.get(component_name)。
把名字定义为常量并在三处复用——这样改名时只需修改 YAML key 和这一个 Python 常量:
from omegaconf import DictConfig
from rlinf.scheduler.placement import ComponentPlacement
from rlinf.workers.rollout.sglang_server import launch_sglang_router_and_server
placement = ComponentPlacement(cfg, cluster)
component_name = "rollout"
llm_cfg = cfg.cluster.component_placement.get(component_name)
rollout_node_group = (
llm_cfg.get("node_group", None)
if isinstance(llm_cfg, DictConfig)
else None
)
server_group, router_group = launch_sglang_router_and_server(
cfg,
cluster,
rollout_hardware_ranks=placement.get_hardware_ranks(component_name),
router_server_args=cfg.router_server_args,
rollout_node_group=rollout_node_group,
)
备注
Launcher 自身看不到这个名字,它只接收解析后的硬件 rank 和 node-group 字符串。
只要 YAML key 与 component_name 一致,叫 rollout、server、
my_engine 还是别的都没区别。
参数块(router_server_args 这个 key)#
router_server_args 是你传给 launcher 的 顶层 YAML key,名字同样可以任意,
重要的是 块内部的结构。完全可以传入其他子配置(cfg.rollout、
cfg.my_engine 等),只要它带有 launcher 需要消费的字段:
字段 |
类型 |
作用 |
|---|---|---|
|
int |
单引擎 TP 大小。 |
|
int |
单引擎 PP 大小。 |
|
str |
sglang server worker group 的名字。 |
|
bool |
设为 |
|
dict |
原样 作为 |
|
str |
router worker 的 worker group 名字。 |
|
bool |
设为 |
|
dict |
作为 |
警告
Launcher 不会校验 server 与 router 块内的 key。server 里有非法字段会让
ServerArgs(**kwargs) 报错;router 里有非法字段则会由 launcher 抛出
ValueError 并列出所有合法字段。把这两个块当作向上游 dataclass 的直接透传即可。
server 的 host/port/dist_init_addr,以及 router 的 port,都会在
运行时填入——在 YAML 里留空即可。Router 默认绑定 0.0.0.0;server 同样绑定
0.0.0.0 并把 Ray 节点 IP 报告给 router。
Launcher 如何使用这份配置#
rlinf.workers.rollout.sglang_server.launch_sglang_router_and_server()
顺序做了以下事情:
将扁平的
rollout_hardware_ranks重新打包成PackedPlacementStrategy,每个 process 占tp_size × pp_size张卡—— 一个 process 即一个 sglang 引擎。启动
SGLangServerWorkergroup(配置取自group_name与server)。在 node 0 启动单个
SGLangRouterWorker,初始不挂载任何 worker (配置取自router_group_name与router)。收集每个 server 的
http://host:port并通过POST /workers注册到 router,阻塞直到每个 worker 报告is_healthy=true。
扁平 rank 重打包有一个 硬性要求:硬件 rank 必须是连续的。若需要非连续布局
(例如由 placement: 0-1:0-3,3-5 解析出的 FlexiblePlacementStrategy),
请自行构造 strategy,通过 placement_strategy=... 传入——
该参数会短路重打包逻辑,原样使用你给的 strategy。
启动#
把上面的配置串起来的最小 Hydra 入口:
import hydra
from omegaconf import DictConfig
from rlinf.scheduler import Cluster
from rlinf.scheduler.placement import ComponentPlacement
from rlinf.workers.rollout.sglang_server import launch_sglang_router_and_server
@hydra.main(version_base="1.1", config_path="config", config_name="my_config")
def main(cfg: DictConfig) -> None:
cluster = Cluster(cluster_cfg=cfg.cluster)
placement = ComponentPlacement(cfg, cluster)
component_name = "rollout"
llm_cfg = cfg.cluster.component_placement.get(component_name)
rollout_node_group = (
llm_cfg.get("node_group", None)
if isinstance(llm_cfg, DictConfig)
else None
)
server_group, router_group = launch_sglang_router_and_server(
cfg,
cluster,
rollout_hardware_ranks=placement.get_hardware_ranks(component_name),
router_server_args=cfg.router_server_args,
rollout_node_group=rollout_node_group,
)
router_url = router_group.get_router_url().wait()[0]
# ... 使用 router_url(见下文 “后续:调用 router”) ...
router_group.shutdown().wait()
server_group.shutdown().wait()
if __name__ == "__main__":
main()
这段代码做了什么:通过 Cluster 启动 Ray,从 cluster.component_placement
构造 ComponentPlacement,再让 launcher 拉起引擎与 router,等所有 server 都
注册成功且健康为止。launch_sglang_router_and_server 返回之后,router 已可
通过 router_url 访问,且所有后端均已通过 GET /workers/<id>。
异构集群(node_group)#
如需把引擎绑定到一组带标签的节点——例如把推理节点专门留给 rollout,训练放别处
运行——使用 cluster 下的 node_group/node_groups 形式:
cluster:
num_nodes: 2
component_placement:
rollout:
node_group: rollout_gpu # 落在哪个 group
placement: all # 使用该 group 内所有硬件 rank
node_groups:
- label: rollout_gpu
node_ranks: 1 # node rank 1 承载引擎
- label: test
node_ranks: 0 # node rank 0 留给其他工作
router_server_args:
# ... 与上面的单机配置一致 ...
与扁平配置相比的变化:
component_placement.rollout由一个字符串变成一个 mapping,包含node_group与placement。驱动脚本从 launcher 所期望接收硬件 rank 的同一个 key 上读出标签,即
cfg.cluster.component_placement.get(component_name).node_group。该标签作为
rollout_node_group传给 launcher;launcher 再把它注入到重打包 的PackedPlacementStrategy中,从而把 server 调度到正确的节点。
如果把组件 key 从 rollout 改成别的(例如 my_engine),只需 同步修改
YAML key 和驱动里的 component_name 变量。node_group 标签
(这里是 rollout_gpu)和 node_groups 下的条目是独立的——
那是集群本身的命名空间。
也可以传入 标签列表,让引擎横跨多个 group;launcher 会原样把列表交给 placement strategy。
完整的 node_groups / env_configs / hardware schema——
包含按 node group 指定 Python 解释器、环境变量、以及非加速器硬件(机器人)的写法——
参见 异构集群。
编程接口#
如果你的 runner 已经自己构造好 placement strategy,可以跳过扁平 rank 路径,
直接传入一个 strategy。该 strategy 必须已编码 tp_size × pp_size 张卡/process:
from rlinf.scheduler.placement import ComponentPlacement
from rlinf.workers.rollout.sglang_server import launch_sglang_router_and_server
placement = ComponentPlacement(cfg, cluster)
component_name = "rollout"
server_group, router_group = launch_sglang_router_and_server(
cfg,
cluster,
rollout_hardware_ranks=None, # placement_strategy 已传入时会被忽略
router_server_args=cfg.router_server_args,
placement_strategy=placement.get_strategy(component_name),
router_node_rank=0, # router 落在哪个 node
)
返回的句柄上还有其他常用入口:
server_group.get_server_url().wait()→ server URL 列表。router_group.register_server(url).wait()→ 事后把一个 server URL 挂到 router 上(阻塞直到该 worker 就绪)。router_group.unregister_server(url).wait()→ 摘下。router_group.get_router_url().wait()[0]→ router URL。
后续:调用 router#
launch_sglang_router_and_server 返回后,router_url 即可通过普通 HTTP
访问——任何 HTTP 客户端都行。/generate 与 /v1/chat/completions
(同步与异步)的具体调用方式,请参见配套指南:
使用 InferenceHTTPClient 调用 SGLang。