http.Agent
Agent
类用于管理HTTP客户端
连接的持久性与复用性。它维护一个给定主机和端口的待处理请求队列,为每个请求重用单个套接字(socket)连接。
在node
中发送请求一般都是使用http.get
或者http.request
,每次请求都是‘建立连接-数据传输-销毁连接’的过程。而内部是通过创建sokcet
对象(tcp套接字)来实现通信的,频繁创建销毁肯定是会带来一定的性能损耗的,所以就有了Agent类,它可以对相同的主机和端口的请求复用同一个socket对象达到keepAlive的效果。基本使用方式如下:
const http = require('http');
const keepAliveAgent = new http.Agent({
// 保留套接字,这样后面的请求就可以复用无需重新创建,默认false
keepAlive: true,
// 每个主机允许的最大套接字数量,默认Infinity
maxSockets: 6,
// 套接字超时,在该时间范围内复用同一个套接字,默认256
timeout: 256
});
http.get({
hostname: 'localhost',
port: 80,
path: '/',
agent: keepAliveAgent // 仅为这个请求创建新代理
}, (res) => {
// 使用响应做些事情
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
agent
选项默认指向的是http.globalAgent
,也就是配置全部都为默认值的agent实例
。如果设为false
则不使用agent
。
值得注意的是这里的keepAlive
与http
头部的connection: keep-alive
不是一个概念。开启keepAlive
同时也会将connection
设为keep-alive
,将agent设为false
则connection
也会默认设为close
。但这些默认行为并不会与手动设置connection
头部冲突,通过下面的例子可以证实:
// server.js
const http = require('http');
const server = http.createServer((req, res) => {
console.log('connection:' + req.headers['connection']);
req.on('end', () => {
console.log('request end');
});
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
data: 'Hello World!'
}));
});
server.listen(3000);
server.on('error', e => {
console.log(e);
});
server.on('connection', socket => {
console.log(`create new socket:${socket.remotePort}`);
});
// client.js
const keepAliveAgent = new http.Agent({
keepAlive: true,
maxSockets: 3
});
const options = {
host: '127.0.0.1',
port: 3000,
path: '/pathname',
agent: keepAliveAgent
};
const request = () => {
return new Promise((resolve, reject) => {
http.get(options, (res) => {
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
resolve(parsedData);
} catch (e) {
console.error(e.message);
reject(e.message);
}
});
}).on('error', (e) => {
reject(e.message);
});
});
};
(async () => {
for (let i = 0; i < 3; i++) {
await request();
}
})();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
server.js创建了一个简单的http服务器,而客户端设置了keepAlive
的agent
,并串行发送三个相同请求,结果如下:
create new socket:50328
connection:keep-alive
request end
connection:keep-alive
request end
connection:keep-alive
request end
2
3
4
5
6
7
可以看到三个请求只创建了以socket
,同时头部的connection
也是keep-alive
。那么我们将keepAlive
设为false
再看:
create new socket:51634
connection:keep-alive
request end
create new socket:51635
connection:keep-alive
request end
create new socket:51636
connection:keep-alive
request end
2
3
4
5
6
7
8
9
此时socket
没有复用,每次都创建了新的实例,但connection
依然是keep-alive
。那么我们再将agent
设为false
:
create new socket:52724
connection:close
request end
create new socket:52725
connection:close
request end
create new socket:52726
connection:close
request end
2
3
4
5
6
7
8
9
这次connection
也变为了close
。
通过这么几个例子也证实了Agent
与http
头的keep-alive
并无太多关联。正常的网页的http
请求必须建立tcp
连接,而浏览器会帮我们实现keep-alive
复用tcp
连接。但是在服务端,我们就得自己实现,而node
中的socket
就是tcp套接字
,通过socket
来建立连接。
大家可能注意到上面的多个请求采用的是串行方式,为什么要用串行呢?如果直接用并行请求会怎样?话不多说,直接动手去除await
,查看打印结果:
create new socket:56120
create new socket:56121
create new socket:56122
connection:close
request end
connection:close
request end
connection:close
request end
2
3
4
5
6
7
8
9
直接就创建了三个socket
实例,这是因为http
模块使用的是http1.1
版本,而1.1版本虽然实现了多路复用,但是一个tcp
连接同时只能处理一个请求,无法处理并行请求。而针对并行请求只能创建多个tcp
连接,而在node
中就是同时创建多个socket
实例。
所以简单概括来说,Agent
就是node
对connection:keep-alive
的具体实现。