Node.js HTTP 服务:内置 http 模块与请求响应心智
很多人学 Node 是从
Express或Fastify开始的,反而对底下的http模块不熟。
这一篇先回到http/https本身,想讲清楚几件事:一个请求在 Node 里长什么样、Server 与 IncomingMessage / ServerResponse 怎么分工、以及这和前面讲过的 Stream、事件循环是什么关系——框架只是在这层之上叠了路由和中间件。
http.createServer:回调里有什么
最简形态是:
- 创建一个
http.Server; - 对每个进来的连接,触发你传的
(req, res) => {}。
这里的 req(IncomingMessage)本质上是 可读流:请求体要按 chunk 读,不能默认假设已经完整在内存里。
res(ServerResponse)是 可写流:write / end 往客户端推数据,背压规则同样适用。
所以 HTTP 在 Node 里不是「一次性对象」,而是 流 + 事件 的组合——这和 Stream 那一篇是同一套心智。
请求行、头、体:顺序与边界
解析顺序大致是:
- 先有一行 方法与路径;
- 再是 头(键值对);
- 最后可能是 体(长度由
Content-Length、或 chunked 编码等决定)。
对服务端代码的含义:
- 头可以先读,体可能分多次
data事件到达; - 大上传必须 流式处理 或落盘,而不是
readFile式一次性读完。
框架里的 body-parser 一类中间件,本质上是在帮你 把流聚合成对象,并承担 大小限制、类型判断 等责任——底层仍然是流。
Server 与 TCP:监听、并发与 backlog
server.listen(port) 背后:
- 向操作系统注册一个 监听 socket;
- 新连接进来后,Node 通过 libuv 把可读可写交给你的回调。
并发并不是「每个请求一个线程」,而是:
- 大量连接复用事件循环;
- 单连接上的处理若阻塞太久,会拖慢整池。
因此 慢处理函数、同步 CPU 重活,在 HTTP 层会直接表现为 吞吐下降与延迟尖刺。
HTTPS:TLS 终止放在哪
https 模块在接口形态上与 http 很像,多了一层 TLS。心智上只要记住:
- 证书与私钥在
createServer选项里配置; - TLS 握手与加解密本身有 CPU 成本,高流量站点常在 反向代理(Nginx、LB) 做 TLS 终止,Node 前面只跑 HTTP——架构选型问题,不是 Node 独有。
和「框架」的关系:中间件在干什么
Express / Fastify / Koa 等做的事,可以粗分为:
- 路由:把 URL + 方法映射到处理函数;
- 中间件链:在
req/res上挂上下文、做鉴权、解析 body、记日志; - 错误处理:统一捕获、映射 HTTP 状态码。
它们 没有替换 http 模块,而是 包装 http.Server 的回调与生命周期。
好处是工程化;代价是:
- 多一层抽象,出问题时要能 沉回到底层 看 socket、头、流有没有被消费干净。
常见坑:响应没 end、体没读完、错误没处理
几个高频问题:
- 调了
write忘记end,客户端一直等; - 读了
req一部分就丢连接,没有abort/destroy清理; - 异常路径上 既没有
res错误响应,也没有关闭连接。
习惯上:
- 每个请求路径都应有 明确的结束态(成功
end、失败返回错误码); - 对 pipeline 使用
stream.promises.pipeline或等价物,统一处理error。
小结:http 是骨架,框架是肌腱
IncomingMessage/ServerResponse的流本质,决定了上传下载、代理转发的写法;- 事件循环 + 非阻塞 I/O 决定了「为什么不能在这里做重 CPU」;
- 框架解决路由与横切能力,调试时仍要会看 裸
http行为。
把这一层搞清楚,再去看具体框架的插件、OpenAPI、GraphQL 网关,会知道它们最终在操纵什么。