Skip to content

Loading…

Add a new option for different keep-alive behaviour #7

Merged
merged 5 commits into from

1 participant

@dannycoates
Owner

This adds the agentkeepalive module for the new option.keepAlive

When keepAlive is true the Endpoint will use the alternate Agent. When false or unset it will use the default node Agent.

added some commits
@dannycoates use agentkeepalive module for endpoint keep-alive support 20fa515
@dannycoates Merge branch 'master' into keepalive 58a28ba
@dannycoates made the behaviour match the name of the maxRetries options
maxRetries should mean the number of request to try _after_ the
initial attempt fails. So attemptsLeft should be maxRetries + 1
to account for the first attempt. Before this change the total
number of attempts made was maxRetries, one less than expected.
8fd5baa
@dannycoates updated readme for maxRetries. Also documented events 855f910
@dannycoates add keepAlive option to readme 855f0eb
@dannycoates dannycoates merged commit c2e5e76 into master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 15, 2012
Commits on Nov 16, 2012
  1. made the behaviour match the name of the maxRetries options

    committed
    maxRetries should mean the number of request to try _after_ the
    initial attempt fails. So attemptsLeft should be maxRetries + 1
    to account for the first attempt. Before this change the total
    number of attempts made was maxRetries, one less than expected.
Showing with 43 additions and 331 deletions.
  1. +0 −82 benchmark/mini_cluster.js
  2. +0 −29 benchmark/mini_server.js
  3. +0 −34 examples/client.js
  4. +0 −20 examples/worker.js
  5. +16 −4 lib/endpoint.js
  6. +1 −0 lib/pool.js
  7. +1 −1 lib/request_set.js
  8. +3 −1 package.json
  9. +22 −160 readme.md
View
82 benchmark/mini_cluster.js
@@ -1,82 +0,0 @@
-var spawn = require('child_process').spawn
-var http = require('http')
-var argv = require('optimist').argv
-var Pool = require('../index')
-
-var instances = argv.n
-var start = argv.start
-
-console.log("starting " + instances + " servers. starting at port " + start)
-
-var exited = 0
-function done(code) {
- exited++
- if (exited === instances) {
- process.exit(0)
- }
-}
-
-var children = []
-var nodes = []
-
-function create(i, port) {
- console.log("spawning server on port " + port)
- var p = spawn
- ( 'node'
- , [ 'mini_server.js'
- , '--port=' + port
- ]
- )
- p.stdout.on('data', function (data) {
- console.log(data.toString())
- })
- p.stderr.on('data', function (data) {
- console.log('\t\033[31m' + data.toString() + '\033[39m')
- })
- p.on('exit', done)
- children.push(p)
- nodes.push('127.0.0.1:' + port)
-}
-
-for (var i = 0; i < instances; i++) {
- create(i, start + i)
-}
-
-var pool = new Pool(http, nodes, {maxPending: 100 })
-pool.on('timing', function (time, op) {
- if (!op.success) {
- console.error('\033[31m' + time + '\033[39m')
- }
- // else {
- // console.error(time)
- // }
-})
-var x = 0
-var start = Date.now()
-var r = 10000
-var a = r
-var delay = 0 + Math.floor(Math.random() * 10)
-
-function result(error, response, body) {
- if (error) {
- console.error('\033[31m' + error.reason + '\033[39m')
- r--
- }
- else {
- x++
- //console.error(pool.nodes.map(function (n) { return n.requestRate }))
- }
- if (x % 1000 === 0) {//if (x === r) {
- console.error(pool.nodes.map(function (n) { return n.requestRate }))
- console.error((a - r) + " failed")
- console.error(Date.now() - start)
- //children.forEach(function (c) { c.kill() })
- }
- }
-
-setInterval(function () {
- for (var i = 0; i < 5; i++) {
- pool.get({ path: "/?delay=" + delay, attempts: 10, retryDelay: 50 }, result)
- }
-}, 1)
-
View
29 benchmark/mini_server.js
@@ -1,29 +0,0 @@
-var argv = require('optimist').argv
-var http = require('http')
-var url = require('url')
-
-var i = 0
-var port = argv.port
-
-function respond(res) {
- res.writeHead(200, {'Content-Type': 'text/plain'})
- res.end(port + ":" + (i++))
-}
-
-http.createServer(
- function (req, res) {
- var q = url.parse(req.url, true)
- var delay = q.query ? +q.query.delay : 0
- if (delay) {
- setTimeout(
- function () {
- respond(res)
- }
- , delay
- )
- }
- else {
- respond(res)
- }
- }
-).listen(port, '127.0.0.1')
View
34 examples/client.js
@@ -1,34 +0,0 @@
-var Poolee = require('../')
- , http = require('http')
- , ms = require('ms') // converts a time to milliseconds
- , servers = [ '127.0.0.1:8080', '127.0.0.1:8081', '127.0.0.1:8082' ]
- , pool = null
-
- // healthiest node, populated later on
- , active_node = null
-
-pool = new Poolee(http, servers)
-
-pool.on('retrying', function(error) {
- console.log(error.message);
-})
-
-console.log('fib(40) = ...calculating on worker.js...');
-pool.request(
- { method: 'GET'
- , path: '/'
- }
-, function (error, response, body) {
- if (error) {
- console.error(error.message)
- return
- }
- if (response.statusCode === 200) {
- console.log(body)
-
- } else {
- console.log(response.statusCode)
- console.log(body)
- }
- }
-)
View
20 examples/worker.js
@@ -1,20 +0,0 @@
-// An http server that does things for you!
-// Do not write your fib server this way, instead use
-// https://gist.github.com/2018811 which this code is based on.
-var http = require('http')
-var PORT = process.argv[2]
-
-function fib(n) {
- if (n < 2) {
- return 1
- } else {
- return fib(n - 2) + fib(n - 1)
- }
-}
-
-var server = http.createServer(function(req, res) {
- res.writeHead(200)
- res.end(fib(40) + "\n")
-})
-server.listen(PORT)
-console.log("worker.js online at http://localhost:" + PORT)
View
20 lib/endpoint.js
@@ -1,4 +1,6 @@
var Stream = require('stream')
+var http = require('http')
+var KeepAlive = require('agentkeepalive')
module.exports = function (inherits, EventEmitter) {
var MAX_COUNT = Math.pow(2, 31) // largest smi value
@@ -16,11 +18,12 @@ module.exports = function (inherits, EventEmitter) {
// maxSockets: max concurrent open sockets (20)
// timeout: default request timeout in ms (60000)
// resolution: how often timeouts are checked in ms (1000)
+ // keepAlive: use an alternate Agent that does keep-alive properly (boolean) default false
// }
- function Endpoint(http, ip, port, options) {
+ function Endpoint(protocol, ip, port, options) {
options = options || {}
- this.http = http
+ this.http = protocol
this.ip = ip
this.port = port
this.healthy = true
@@ -28,8 +31,17 @@ module.exports = function (inherits, EventEmitter) {
this.address = this.ip
this.pingPath = options.ping
this.pingTimeout = options.pingTimeout || 2000
-
- this.agent = new http.Agent()
+ if (options.keepAlive) {
+ if (protocol === http) {
+ this.agent = new KeepAlive()
+ }
+ else {
+ this.agent = new KeepAlive.HttpsAgent()
+ }
+ }
+ else {
+ this.agent = new protocol.Agent()
+ }
this.agent.maxSockets = options.maxSockets || 20
this.requests = {}
View
1 lib/pool.js
@@ -13,6 +13,7 @@ module.exports = function (inherits, EventEmitter, Endpoint, RequestSet) {
// pingTimeout: number (milliseconds) default 2000
// retryFilter: function (response) { return true to reject response and retry }
// retryDelay: number (milliseconds) default 20
+ // keepAlive: use an alternate Agent that does keep-alive properly (boolean) default false
// name: string (optional)
// maxRetries: number (default = 5)
// }
View
2 lib/request_set.js
@@ -34,7 +34,7 @@ function attemptsFu(options, pool) {
if (options.data instanceof Stream) {
return 1
}
- return options.attempts || Math.min(pool.options.maxRetries, Math.max(pool.length, 2))
+ return options.attempts || Math.min(pool.options.maxRetries + 1, Math.max(pool.length, 2))
}
function exponentialBackoff(attempt, delay) {
View
4 package.json
@@ -10,7 +10,9 @@
"url": "http://github.com/dannycoates/poolee.git"
},
"engines" : { "node": ">=0.6.0" },
- "dependencies": {},
+ "dependencies": {
+ "agentkeepalive": "*"
+ },
"devDependencies": {
"mocha": "*",
"optimist": "*",
View
182 readme.md
@@ -63,10 +63,12 @@ var pool = new Pool(
, maxSockets: 200 // max sockets per endpoint Agent
, timeout: 60000 // request timeout in ms
, resolution: 1000 // timeout check interval (see below)
+ , keepAlive: false // use an alternate Agent that does http keep-alive properly
, ping: undefined // health check url
, pingTimeout: 2000 // ping timeout in ms
, retryFilter: undefined // see below
, retryDelay: 20 // see below
+ , maxRetries: 5 // see below
, name: undefined // optional string
}
)
@@ -82,6 +84,11 @@ signal to slow down the rate of requests.
Pending requests have their timeouts checked at this rate. If your timeout is 60000
and resolution is 1000, the request will timeout no later than 60999
+###### keepAlive
+
+The default http Agent does keep-alive in a stupid way. If you want it to work
+how you'd expect it to set this to true.
+
###### retryFilter
All valid http responses aren't necessarily a "success". This function lets you
@@ -111,6 +118,11 @@ Math.random() * Math.pow(2, attemptNumber) * retryDelay
```
If `retryDelay` is 20, attemptNumber 1 (the first retry) will delay at most 40ms
+###### maxRetries
+
+The maximum number of attempts to make after the first request fails. This only
+takes effect if maxRetries < pool size.
+
###### ping
When an endpoint is unresponsive the pool will not use it for requests. The ping
@@ -148,7 +160,7 @@ pool.request(
, data: undefined // request body, may be a string, buffer, or stream
, headers: {} // extra http headers to send
, retryFilter: undefined // see below
- , attempts: pool.length // or at least 2, at most 5
+ , attempts: pool.length // or at least 2, at most options.maxRetries + 1
, retryDelay: 20 // retries wait with exponential backoff times this number of ms
, timeout: 60000 // ms to wait before timing out the request
, encoding: 'utf8' // response body encoding
@@ -200,171 +212,21 @@ Same arguments as `request` that sets `options.method = 'POST'`
Same arguments as `request` that sets `options.method = 'DELETE'`
----
-
-# Advanced
-
-## Make a pool
-```js
-var pool = new Poolee(http, servers, options)
-```
-
-`servers`: array of strings formatted like 'ip:port'
-
-`options`: defaults and explanations below
-
-```js
-// options
-{
- // number of pending requests allowed
- maxPending: 1000
-
- // ping path. (default = no ping checks)
-, ping: null
-
-, retryFilter: function (options, response) {
- // return true to reject response and retry
- }
-
- // number in milliseconds
-, retryDelay: 20
-
- // optional string name
-, name: null
-}
-```
-
-### Events emitted by `pool`:
-```js
-pool.on('health', function(messageString) {
- // message string of of the form:
- // "127.0.0.1:8888 health: true"
- // or
- // "127.0.0.1:8888 health: false"
-})
-
-pool.on('timeout', function(url) {
- // where url is a ip+port+path combination for the timed-out request
-})
-
-pool.on('retrying', function(error) { })
-
-pool.on('timing', function(time, options) {
- // `time`: the time the latest request took to complete
- // `options`: options used to send the request
-})
-```
-
-
-### Get a healthy node
- var node = pool.get_node()
-
-Attached to `node`:
-
- // Counts of interest
- // node.pending;
- // node.successes
- // node.failures
- // node.requestRate
-
- // node.ip;
- // node.port;
- // node.name = node.ip + ':' + node.port;
-### Events emitted by `node`
-```js
-node.on('health', function(self) {
- // `self` has all the same properties as `node`
-})
-
-node.on('timeout', function (request) {
- // the request that timed out
-})
-```
+### Events
-### Example
+##### timing
-Note that this example should fail, because there won't be any nodes running.
-You can also see this code in
-[`examples/`](https://github.com/dannycoates/poolee/tree/master/examples).
+Emits the request `duration` and `options` after each request
-#### `client.js`
-```js
-var Poolee = require('../')
- , http = require('http')
- , ms = require('ms') // converts a time to milliseconds
- , servers = [ '127.0.0.1:8080', '127.0.0.1:8081', '127.0.0.1:8082' ]
- , pool = null
+##### retrying
- // healthiest node, populated later on
- , active_node = null
+Emits the `error` of why a request is being retried
-pool = new Poolee(http, servers)
+##### timeout
-pool.on('retrying', function(error) {
- console.log(error.message);
-})
+Emits the `request` when a request times out
-console.log('fib(40) = ...calculating on worker.js...');
-pool.request(
- { method: 'GET'
- , path: '/'
- }
-, function (error, response, body) {
- if (error) {
- console.error(error.message)
- return
- }
- if (response.statusCode === 200) {
- console.log(body)
+##### health
- } else {
- console.log(response.statusCode)
- console.log(body)
- }
- }
-)
-```
-
-Run this before the above script, then see what happens.
-
-#### `worker.js`
-```js
-// An http server that does things for you!
-// Do not write your fib server this way, instead use
-// https://gist.github.com/2018811 which this code is based on.
-var http = require('http')
-var PORT = process.argv[2]
-
-function fib(n) {
- if (n < 2) {
- return 1
- } else {
- return fib(n - 2) + fib(n - 1)
- }
-}
-
-var server = http.createServer(function(req, res) {
- res.writeHead(200)
- res.end(fib(40) + "\n")
-})
-server.listen(PORT)
-console.log("worker.js online at http://localhost:" + PORT)
-```
-
-To see a pool that is 100% healthy:
-
-```sh
-node ./worker.js 8080 &
-node ./worker.js 8081 &
-node ./worker.js 8082 &
-
-echo "running client.js ..."
-node ./client.js
-```
-
-## Running tests
-```sh
-npm -g install mocha
-mocha
-```
+Emits the `endpoint` when a node changes state between healthy/unhealthy
Something went wrong with that request. Please try again.