HTTP模块是Node.js标准库中最基础也最关键的模块之一,它提供了完整的HTTP协议实现。不同于PHP或Java等传统后端语言需要依赖Apache/Nginx等Web服务器,Node.js通过这个内置模块就能直接创建完整的Web服务。我在实际项目中发现,很多开发者虽然会用Express/Koa等框架,但对底层HTTP模块的理解却不够深入。
这个模块主要包含两类核心功能:
理解这些底层机制,不仅能帮助开发者更好地使用上层框架,还能处理一些需要直接操作原始HTTP流的特殊场景。比如最近我们团队就遇到需要直接处理multipart/form-data原始数据的需求,这时候就必须深入HTTP模块层面进行操作。
创建一个最小化的HTTP服务器只需要几行代码:
javascript复制const http = require('http');
const server = http.createServer((req, res) => {
res.end('Hello World');
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});
这里有几个关键点需要注意:
请求对象(req)包含客户端发来的所有信息,常用的属性包括:
| 属性 | 说明 | 示例值 |
|---|---|---|
| req.method | HTTP请求方法 | 'GET', 'POST' |
| req.url | 请求的URL路径 | '/api/users?id=123' |
| req.headers | 请求头对象 |
一个实际开发中的技巧:可以通过req.url.split('?')[0]来快速获取纯净的路径部分,这在实现简单路由时非常有用。
响应对象(res)控制着服务器返回给客户端的内容,必须掌握的核心方法:
javascript复制res.writeHead(200, {
'Content-Type': 'text/plain',
'X-Custom-Header': 'value'
});
res.write('Hello');
res.end(' World');
注意事项:
Node.js不仅可以创建服务,也能作为客户端发起请求:
javascript复制const http = require('http');
const options = {
hostname: 'example.com',
port: 80,
path: '/api/data',
method: 'GET'
};
const req = http.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(JSON.parse(data));
});
});
req.on('error', (error) => {
console.error(error);
});
req.end();
关键点说明:
发送POST请求需要额外处理请求体:
javascript复制const postData = JSON.stringify({
username: 'john',
password: 'doe'
});
const options = {
hostname: 'example.com',
port: 80,
path: '/api/login',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = http.request(options, (res) => {
// 处理响应...
});
req.write(postData);
req.end();
特别要注意:
HTTP/1.1默认启用持久连接,但Node.js中需要正确管理:
javascript复制// 服务器端设置Keep-Alive
server.keepAliveTimeout = 60000; // 60秒
server.headersTimeout = 65000; // 比keepAliveTimeout稍长
// 客户端启用连接池
const agent = new http.Agent({
keepAlive: true,
maxSockets: 10,
timeout: 60000
});
const options = {
agent: agent
// 其他配置...
};
对于大文件传输,一定要使用流式处理:
javascript复制const fs = require('fs');
// 服务器流式响应
const server = http.createServer((req, res) => {
const stream = fs.createReadStream('./large-file.zip');
stream.pipe(res);
});
// 客户端流式接收
const req = http.request(options, (res) => {
const file = fs.createWriteStream('./download.zip');
res.pipe(file);
});
这样做可以避免内存爆满的问题,实测处理GB级文件时内存占用可以控制在几十MB。
这是网络编程中最常见的错误之一,通常表示连接被对端重置。处理方案:
javascript复制req.on('error', (err) => {
if (err.code === 'ECONNRESET') {
console.log('连接被对方重置');
// 重试逻辑...
}
});
javascript复制req.setTimeout(5000, () => {
req.abort();
});
可能原因和解决方案:
解决方案:
javascript复制res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8'
});
同时确保:
在电商API网关的开发中,我们遇到过高并发下TCP端口耗尽的问题。解决方案是合理配置Agent:
javascript复制const agent = new http.Agent({
keepAlive: true,
maxSockets: 50, // 根据服务器配置调整
maxFreeSockets: 10,
timeout: 30000
});
另一个重要经验是关于错误处理的。一定要为所有请求添加error事件监听:
javascript复制const req = http.request(options, callback);
req.on('error', (err) => {
// 记录日志
// 重试或降级处理
});
对于性能敏感的应用,建议使用原生的http模块而不是框架,因为框架通常会有额外的性能开销。在我们的压测中,原生模块的QPS能比Express高出20-30%。