1 | # agentkeepalive
|
2 |
|
3 | [![NPM version][npm-image]][npm-url]
|
4 | [![build status][travis-image]][travis-url]
|
5 | [![Appveyor status][appveyor-image]][appveyor-url]
|
6 | [![Test coverage][codecov-image]][codecov-url]
|
7 | [![David deps][david-image]][david-url]
|
8 | [![Known Vulnerabilities][snyk-image]][snyk-url]
|
9 | [![npm download][download-image]][download-url]
|
10 |
|
11 | [npm-image]: https://img.shields.io/npm/v/agentkeepalive.svg?style=flat
|
12 | [npm-url]: https://npmjs.org/package/agentkeepalive
|
13 | [travis-image]: https://img.shields.io/travis/node-modules/agentkeepalive.svg?style=flat
|
14 | [travis-url]: https://travis-ci.org/node-modules/agentkeepalive
|
15 | [appveyor-image]: https://ci.appveyor.com/api/projects/status/k7ct4s47di6m5uy2?svg=true
|
16 | [appveyor-url]: https://ci.appveyor.com/project/fengmk2/agentkeepalive
|
17 | [codecov-image]: https://codecov.io/gh/node-modules/agentkeepalive/branch/master/graph/badge.svg
|
18 | [codecov-url]: https://codecov.io/gh/node-modules/agentkeepalive
|
19 | [david-image]: https://img.shields.io/david/node-modules/agentkeepalive.svg?style=flat
|
20 | [david-url]: https://david-dm.org/node-modules/agentkeepalive
|
21 | [snyk-image]: https://snyk.io/test/npm/agentkeepalive/badge.svg?style=flat-square
|
22 | [snyk-url]: https://snyk.io/test/npm/agentkeepalive
|
23 | [download-image]: https://img.shields.io/npm/dm/agentkeepalive.svg?style=flat-square
|
24 | [download-url]: https://npmjs.org/package/agentkeepalive
|
25 |
|
26 | The Node.js's missing `keep alive` `http.Agent`. Support `http` and `https`.
|
27 |
|
28 | ## What's different from original `http.Agent`?
|
29 |
|
30 | - `keepAlive=true` by default
|
31 | - Disable Nagle's algorithm: `socket.setNoDelay(true)`
|
32 | - Add free socket timeout: avoid long time inactivity socket leak in the free-sockets queue.
|
33 | - Add active socket timeout: avoid long time inactivity socket leak in the active-sockets queue.
|
34 |
|
35 | ## Install
|
36 |
|
37 | ```bash
|
38 | $ npm install agentkeepalive --save
|
39 | ```
|
40 |
|
41 | ## new Agent([options])
|
42 |
|
43 | * `options` {Object} Set of configurable options to set on the agent.
|
44 | Can have the following fields:
|
45 | * `keepAlive` {Boolean} Keep sockets around in a pool to be used by
|
46 | other requests in the future. Default = `true`.
|
47 | * `keepAliveMsecs` {Number} When using HTTP KeepAlive, how often
|
48 | to send TCP KeepAlive packets over sockets being kept alive.
|
49 | Default = `1000`. Only relevant if `keepAlive` is set to `true`.
|
50 | * `freeSocketKeepAliveTimeout`: {Number} Sets the free socket to timeout
|
51 | after `freeSocketKeepAliveTimeout` milliseconds of inactivity on the free socket.
|
52 | Default is `15000`.
|
53 | Only relevant if `keepAlive` is set to `true`.
|
54 | * `timeout`: {Number} Sets the working socket to timeout
|
55 | after `timeout` milliseconds of inactivity on the working socket.
|
56 | Default is `freeSocketKeepAliveTimeout * 2`.
|
57 | * `maxSockets` {Number} Maximum number of sockets to allow per
|
58 | host. Default = `Infinity`.
|
59 | * `maxFreeSockets` {Number} Maximum number of sockets to leave open
|
60 | in a free state. Only relevant if `keepAlive` is set to `true`.
|
61 | Default = `256`.
|
62 | * `socketActiveTTL` {Number} Sets the socket active time to live, even if it's in use.
|
63 | If not setted the behaviour continues the same (the socket will be released only when free)
|
64 | Default = `null`.
|
65 |
|
66 | ## Usage
|
67 |
|
68 | ```js
|
69 | const http = require('http');
|
70 | const Agent = require('agentkeepalive');
|
71 |
|
72 | const keepaliveAgent = new Agent({
|
73 | maxSockets: 100,
|
74 | maxFreeSockets: 10,
|
75 | timeout: 60000,
|
76 | freeSocketKeepAliveTimeout: 30000, // free socket keepalive for 30 seconds
|
77 | });
|
78 |
|
79 | const options = {
|
80 | host: 'cnodejs.org',
|
81 | port: 80,
|
82 | path: '/',
|
83 | method: 'GET',
|
84 | agent: keepaliveAgent,
|
85 | };
|
86 |
|
87 | const req = http.request(options, res => {
|
88 | console.log('STATUS: ' + res.statusCode);
|
89 | console.log('HEADERS: ' + JSON.stringify(res.headers));
|
90 | res.setEncoding('utf8');
|
91 | res.on('data', function (chunk) {
|
92 | console.log('BODY: ' + chunk);
|
93 | });
|
94 | });
|
95 | req.on('error', e => {
|
96 | console.log('problem with request: ' + e.message);
|
97 | });
|
98 | req.end();
|
99 |
|
100 | setTimeout(() => {
|
101 | if (keepaliveAgent.statusChanged) {
|
102 | console.log('[%s] agent status changed: %j', Date(), keepaliveAgent.getCurrentStatus());
|
103 | }
|
104 | }, 2000);
|
105 |
|
106 | ```
|
107 |
|
108 | ### `getter agent.statusChanged`
|
109 |
|
110 | counters have change or not after last checkpoint.
|
111 |
|
112 | ### `agent.getCurrentStatus()`
|
113 |
|
114 | `agent.getCurrentStatus()` will return a object to show the status of this agent:
|
115 |
|
116 | ```js
|
117 | {
|
118 | createSocketCount: 10,
|
119 | closeSocketCount: 5,
|
120 | timeoutSocketCount: 0,
|
121 | requestCount: 5,
|
122 | freeSockets: { 'localhost:57479:': 3 },
|
123 | sockets: { 'localhost:57479:': 5 },
|
124 | requests: {}
|
125 | }
|
126 | ```
|
127 |
|
128 | ### Support `https`
|
129 |
|
130 | ```js
|
131 | const https = require('https');
|
132 | const HttpsAgent = require('agentkeepalive').HttpsAgent;
|
133 |
|
134 | const keepaliveAgent = new HttpsAgent();
|
135 | // https://www.google.com/search?q=nodejs&sugexp=chrome,mod=12&sourceid=chrome&ie=UTF-8
|
136 | const options = {
|
137 | host: 'www.google.com',
|
138 | port: 443,
|
139 | path: '/search?q=nodejs&sugexp=chrome,mod=12&sourceid=chrome&ie=UTF-8',
|
140 | method: 'GET',
|
141 | agent: keepaliveAgent,
|
142 | };
|
143 |
|
144 | const req = https.request(options, res => {
|
145 | console.log('STATUS: ' + res.statusCode);
|
146 | console.log('HEADERS: ' + JSON.stringify(res.headers));
|
147 | res.setEncoding('utf8');
|
148 | res.on('data', chunk => {
|
149 | console.log('BODY: ' + chunk);
|
150 | });
|
151 | });
|
152 |
|
153 | req.on('error', e => {
|
154 | console.log('problem with request: ' + e.message);
|
155 | });
|
156 | req.end();
|
157 |
|
158 | setTimeout(() => {
|
159 | console.log('agent status: %j', keepaliveAgent.getCurrentStatus());
|
160 | }, 2000);
|
161 | ```
|
162 |
|
163 | ## [Benchmark](https://github.com/node-modules/agentkeepalive/tree/master/benchmark)
|
164 |
|
165 | run the benchmark:
|
166 |
|
167 | ```bash
|
168 | cd benchmark
|
169 | sh start.sh
|
170 | ```
|
171 |
|
172 | Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz
|
173 |
|
174 | node@v0.8.9
|
175 |
|
176 | 50 maxSockets, 60 concurrent, 1000 requests per concurrent, 5ms delay
|
177 |
|
178 | Keep alive agent (30 seconds):
|
179 |
|
180 | ```js
|
181 | Transactions: 60000 hits
|
182 | Availability: 100.00 %
|
183 | Elapsed time: 29.70 secs
|
184 | Data transferred: 14.88 MB
|
185 | Response time: 0.03 secs
|
186 | Transaction rate: 2020.20 trans/sec
|
187 | Throughput: 0.50 MB/sec
|
188 | Concurrency: 59.84
|
189 | Successful transactions: 60000
|
190 | Failed transactions: 0
|
191 | Longest transaction: 0.15
|
192 | Shortest transaction: 0.01
|
193 | ```
|
194 |
|
195 | Normal agent:
|
196 |
|
197 | ```js
|
198 | Transactions: 60000 hits
|
199 | Availability: 100.00 %
|
200 | Elapsed time: 46.53 secs
|
201 | Data transferred: 14.88 MB
|
202 | Response time: 0.05 secs
|
203 | Transaction rate: 1289.49 trans/sec
|
204 | Throughput: 0.32 MB/sec
|
205 | Concurrency: 59.81
|
206 | Successful transactions: 60000
|
207 | Failed transactions: 0
|
208 | Longest transaction: 0.45
|
209 | Shortest transaction: 0.00
|
210 | ```
|
211 |
|
212 | Socket created:
|
213 |
|
214 | ```
|
215 | [proxy.js:120000] keepalive, 50 created, 60000 requestFinished, 1200 req/socket, 0 requests, 0 sockets, 0 unusedSockets, 50 timeout
|
216 | {" <10ms":662," <15ms":17825," <20ms":20552," <30ms":17646," <40ms":2315," <50ms":567," <100ms":377," <150ms":56," <200ms":0," >=200ms+":0}
|
217 | ----------------------------------------------------------------
|
218 | [proxy.js:120000] normal , 53866 created, 84260 requestFinished, 1.56 req/socket, 0 requests, 0 sockets
|
219 | {" <10ms":75," <15ms":1112," <20ms":10947," <30ms":32130," <40ms":8228," <50ms":3002," <100ms":4274," <150ms":181," <200ms":18," >=200ms+":33}
|
220 | ```
|
221 |
|
222 | ## License
|
223 |
|
224 | ```
|
225 | (The MIT License)
|
226 |
|
227 | Copyright(c) node-modules and other contributors.
|
228 | Copyright(c) 2012 - 2015 fengmk2 <fengmk2@gmail.com>
|
229 |
|
230 | Permission is hereby granted, free of charge, to any person obtaining
|
231 | a copy of this software and associated documentation files (the
|
232 | 'Software'), to deal in the Software without restriction, including
|
233 | without limitation the rights to use, copy, modify, merge, publish,
|
234 | distribute, sublicense, and/or sell copies of the Software, and to
|
235 | permit persons to whom the Software is furnished to do so, subject to
|
236 | the following conditions:
|
237 |
|
238 | The above copyright notice and this permission notice shall be
|
239 | included in all copies or substantial portions of the Software.
|
240 |
|
241 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
242 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
243 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
244 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
245 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
246 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
247 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
248 | ```
|