Node.js HTTP 服务:内置 http 模块与请求响应心智

很多人学 Node 是从 ExpressFastify 开始的,反而对底下的 http 模块不熟。
这一篇先回到 http / https 本身,想讲清楚几件事:一个请求在 Node 里长什么样、Server 与 IncomingMessage / ServerResponse 怎么分工、以及这和前面讲过的 Stream、事件循环是什么关系——框架只是在这层之上叠了路由和中间件。

最简形态是:

  • 创建一个 http.Server
  • 对每个进来的连接,触发你传的 (req, res) => {}

这里的 reqIncomingMessage)本质上是 可读流:请求体要按 chunk 读,不能默认假设已经完整在内存里。

resServerResponse)是 可写流write / end 往客户端推数据,背压规则同样适用。

所以 HTTP 在 Node 里不是「一次性对象」,而是 流 + 事件 的组合——这和 Stream 那一篇是同一套心智。

解析顺序大致是:

  • 先有一行 方法与路径
  • 再是 (键值对);
  • 最后可能是 (长度由 Content-Length、或 chunked 编码等决定)。

对服务端代码的含义:

  • 头可以先读,体可能分多次 data 事件到达;
  • 大上传必须 流式处理 或落盘,而不是 readFile 式一次性读完。

框架里的 body-parser 一类中间件,本质上是在帮你 把流聚合成对象,并承担 大小限制、类型判断 等责任——底层仍然是流。

server.listen(port) 背后:

  • 向操作系统注册一个 监听 socket
  • 新连接进来后,Node 通过 libuv 把可读可写交给你的回调。

并发并不是「每个请求一个线程」,而是:

  • 大量连接复用事件循环
  • 单连接上的处理若阻塞太久,会拖慢整池。

因此 慢处理函数同步 CPU 重活,在 HTTP 层会直接表现为 吞吐下降与延迟尖刺

https 模块在接口形态上与 http 很像,多了一层 TLS。心智上只要记住:

  • 证书与私钥createServer 选项里配置;
  • TLS 握手与加解密本身有 CPU 成本,高流量站点常在 反向代理(Nginx、LB) 做 TLS 终止,Node 前面只跑 HTTP——架构选型问题,不是 Node 独有。

Express / Fastify / Koa 等做的事,可以粗分为:

  • 路由:把 URL + 方法映射到处理函数;
  • 中间件链:在 req / res 上挂上下文、做鉴权、解析 body、记日志;
  • 错误处理:统一捕获、映射 HTTP 状态码。

它们 没有替换 http 模块,而是 包装 http.Server 的回调与生命周期。

好处是工程化;代价是:

  • 多一层抽象,出问题时要能 沉回到底层 看 socket、头、流有没有被消费干净。

几个高频问题:

  • 调了 write 忘记 end,客户端一直等;
  • 读了 req 一部分就丢连接,没有 abort / destroy 清理
  • 异常路径上 既没有 res 错误响应,也没有关闭连接

习惯上:

  • 每个请求路径都应有 明确的结束态(成功 end、失败返回错误码);
  • 对 pipeline 使用 stream.promises.pipeline 或等价物,统一处理 error
  • IncomingMessage / ServerResponse 的流本质,决定了上传下载、代理转发的写法;
  • 事件循环 + 非阻塞 I/O 决定了「为什么不能在这里做重 CPU」;
  • 框架解决路由与横切能力,调试时仍要会看 http 行为

把这一层搞清楚,再去看具体框架的插件、OpenAPI、GraphQL 网关,会知道它们最终在操纵什么。