上一篇:19-3《视频帧与媒体模块——默认不启用的 H.264》| 下一篇:20-2《路由与自研最小 JSON》
真机实测通过:本文实验已在 RK3588 板端实测完成(2026-09;方法学与原始记录见仓库 docs 与《实验脚本》目录)
一句话导读:极简 HTTP 服务:socket、bind、select 轮询与有界 worker 队列怎么撑起零外部库的自研服务器,板端实测 /health 往返,讲清自研与上框架在单机板端场景的成本取舍。
关键词:HTTP、socket、select 轮询、零依赖、推理引擎多模态与数据面收官,Day 20 进入服务化:引擎的 HTTP 服务在 src/serve/vllm_http.c,一个文件里装下了 socket 监听、自研 JSON 解析器、请求/响应解析、线程池、SSE 流式——零外部库,只依赖系统 socket API。这篇先回答"极简服务器怎么撑起来的",并用板端实测的 /health 往返时间把"自研 vs 上框架"的成本讲清楚。
1. 知识点:一个最小 HTTP 服务要几块砖
HTTP 服务最内核只有四件事:
socket() → bind() → listen() → accept() 循环
│
每来一个连接 → 读请求头/体 → 路由 → 写响应
- listen:告诉内核"我在这端口等人连接",参数是积压队列长度;
- accept:从内核拿一个已完成握手的连接;
- select:一次等待多个 fd 就绪(本引擎用它在 accept 上等连接,附带超时做优雅停机轮询,vllm_http.c 854–868 行);
- SO_RCVTIMEO:给每个已接受连接设接收超时(vnet_set_recv_timeout,511 行)——慢连接/半开连接会被超时踢掉,这是"不引库也能防呆"的典型写法。
引擎的取舍(文件头注释 4–9 行):accept 循环单线程串行 + 连接丢给有界队列 + 8 个 worker。HTTP 层并行度不归推理管——推理本身是单 SSE 流串行(Day 14 讲过"单机守则"),HTTP worker 只是把"慢客户端"与"推理线程"隔开。
为什么不上 libevent/nginx? 板端场景的并发模型是"单模型 + 少量并发请求",select + 有界队列 + SO_RCVTIMEO 已经覆盖;少一个第三方依赖,就少一分供应链审计成本(这是 Day 23 起国密/签名体系能自洽的前提)。
2. 对应代码:vllm_http.c 的砖块顺序
监听与主循环(vhttp_serve_ex,804–868 行):
listener.fd = socket(AF_INET, SOCK_STREAM, 0); // 812
setsockopt(listener.fd, SOL_SOCKET, SO_REUSEADDR, …);// 816
bind(listener.fd, (struct sockaddr *)&addr, sizeof(addr)); // 823
listen(listener.fd, 32); // 824
for (;;) {
fd_set rfds; FD_SET(listener.fd, &rfds);
select(listener.fd + 1, &rfds, NULL, NULL, &tv); // 860:等连接或超时
int cf = accept(listener.fd, (struct sockaddr *)&ca, &calen); // 868
queue_push(&ctx->q, conn); // 连接进有界队列,worker 取走
}
配套的三块:
"极简"的代价要在文档里说清(技术文档 §2.2):默认 –threads 8 的 worker 数只影响连接并发,推理仍串行;想要吞吐只能靠批处理而不是把 HTTP worker 堆到推理上。
3. 改动后果:板端实测 /health 往返
实测口径:RK3588 板 serve(model.vqf 已加载)+ x86 主机客户端,局域网 192.168.1.92:8801,2026-09-07。客户端记录的是 urlopen 完整往返(含 TCP 握手),不是服务端处理时间。
GET /health ×6(模型已加载、无排队)
42.6 ms / 29.3 ms / 16.8 ms / 27.8 ms / 7.2 ms / 22.5 ms
响应体: {"status":"ok","model":"qwen3-vl-8b","busy":false,"queued":0,…}
三个观察:
4. 学员调试任务
A 档(板端/本地动手):
B 档(纯读源码):回答:① 为什么 select 的超时对"优雅停机"是必要的(提示:g_vhttp_stop 标志 28–34 行 + vhttp_stop 在 vhttp_serve_ex 主循环哪里被检查)?② SO_RCVTIMEO 设多少秒合理、设成 0 会发生什么?③ 若把 queue_push 换成无界队列,worker 忙时连接会怎样堆积,内存上会出什么问题?
预期输出:自己板子的 /health 往返直方图 + 监听 socket 的 netstat 截图,并能说出 select 在这里"等的是什么"。
收尾
本篇源码点名:vllm_http.c(socket/bind/listen 812–824、select 主循环 854–868、read_request 554、worker 队列 729–799)
开源仓库:Kestrel-LLM (Gitee)(AGPL-3.0-or-later 或商业许可,二选一)
下篇预告:连接进来了,URL 和 JSON 体怎么变成一次"对话"?20-2 讲路由表与自研最小 JSON 解析器,并用"改错字段名"实验演示兼容性断裂。



