Skip to content
This repository has been archived by the owner on Mar 16, 2022. It is now read-only.

Commit

Permalink
feat(#27): add benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
h2non committed Jul 6, 2015
1 parent 205193e commit 46e4c2c
Show file tree
Hide file tree
Showing 13 changed files with 5,023 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
test/.tmp
test/fixtures
*.nar
31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ Requires node.js +0.12 or io.js +1.6
- [Features](#features)
- [When rocky could be useful?](#when-rocky-could-be-useful)
- [Motivation](#motivation)
- [Design](#design)
- [Design](#design)
- [Stability](#stability)
- [Versions](#versions)
- [How does it work?](#how-does-it-work)
- [Middleware layer](#middleware-layer)
- [Types of middleware]
- [Installation](#installation)
- [Standalone binaries](#standalone-binaries)
- [Usage](#usage)
- [Third-party middleware](#third-party-middleware)
- [Command-line](#command-line)
- [Examples](#examples)
Expand Down Expand Up @@ -138,7 +139,7 @@ npm install -g rocky
- [linux-x64](https://github.com/h2non/rocky/releases/download/0.2.0/rocky-0.2.0-linux-x64.nar)
- [darwin-x64](https://github.com/h2non/rocky/releases/download/0.2.0/rocky-0.2.0-darwin-x64.nar)

Packaged using [nar](https://github.com/h2non/nar)
Packaged using [nar](https://github.com/h2non/nar). Shipped with node.js `0.12.6`

##### Usage

Expand Down Expand Up @@ -411,7 +412,7 @@ Alias: `param()`
Maps the specified path parameter name to a specialized param-capturing middleware.
The middleware stack is the same as `.use()`

#### rocky#useOn(name, ...middleware)
#### rocky#useFor(name, ...middleware)

Use a custom middleware for a specific phase. Supported phase names are: `forward`, 'replay'.

Expand Down Expand Up @@ -539,16 +540,16 @@ Overwrite the `Host` header value when forward the request

#### route#transformRequestBody(middleware, [ filter ])

**Caution**: using this middleware could generate negative performance side-effects, since the whole payload data will be buffered in the heap until it's finished. Don't use it if you need to handle large payloads
This method implements a non-instrusive native `http.IncommingMessage` stream wrapper that allow you to intercept and transform the request body received from the client before sending it to the target server.

This method allow you to intercept and transform the response body recieved from the client before sending it to the target server.

The `middleware` argument must a function accepting the following arguments: `function(req, res, next)`
The `middleware` argument must a function which accepts the following arguments: `function(req, res, next)`
The `filter` arguments is optional and it can be a `string`, `regexp` or `function(req)` which should return `boolean` if the `request` passes the filter. The default check value by `string` or `regexp` test is the `Content-Type` header.

In the middleware function **must call the `next` function**, which accepts the following arguments: `err, newBody, encoding`
You can see an usage example [here](/examples/interceptor.js).

**Caution**: using this middleware could generate in some scenarios negative performance side-effects, since the whole payload data will be buffered in the heap until it's finished. Don't use it if you need to handle large payloads.

The body will be exposed as raw `Buffer` or `String` on both properties `body` and `originalBody` in `http.ClientRequest`:
```js
rocky
Expand All @@ -570,16 +571,16 @@ rocky

#### route#transformResponseBody(middleware, [ filter ])

**Caution**: using this middleware could generate negative performance side-effects since the whole payload data will be buffered in the heap until it's finished. Don't use it if you need to handle large payloads.
This method implements a non-instrusive native `http.RequestResponse` stream wrapper that allow you to intercept and transform the response body received from the target server before sending it to the client.

This method allow you to intercept and transform the response body received from the target server before sending it to the client.

The `middleware` argument must a function accepting the following arguments: `function(req, res, next)`
The `middleware` argument must a function which accepts the following arguments: `function(req, res, next)`
The `filter` arguments is optional and it can be a `string`, `regexp` or `function(res)` which should return `boolean` if the `request` passes the filter. The default check value by `string` or `regexp` test is the `Content-Type` header.

In the middleware function **must call the `next` function**, which accepts the following arguments: `err, newBody, encoding`
You can see an usage example [here](/examples/interceptor.js).

**Caution**: using this middleware could generate in some scenarios negative performance side-effects since the whole payload data will be buffered in the heap until it's finished. Don't use it if you need to handle large payloads.

The body will be exposed as raw `Buffer` or `String` on both properties `body` and `originalBody` in `http.ClientResponse`:
```js
rocky
Expand Down Expand Up @@ -608,6 +609,12 @@ You can pass any supported option by [http-proxy](https://github.com/nodejitsu/n

Add custom middleware to the specific route.

#### rocky#useFor(name, ...middleware)

#### rocky#useReplay(...middleware)

#### rocky#useForward(...middleware)

#### route#on(event, ...handler)

Subscribes to a specific event for the given route.
Expand Down
66 changes: 66 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Benchmark

Simple benchmark suite for `rocky`

## Requirements

- Go +1.3
- vegeta `go get github.com/tsenart/vegeta`

## Run it!

```
bash benchmark/run.sh
```

Supported optional arguments:
```
bash benchmark/run.sh [rocky url] [rate] [duration]
```

Example:
```
bash benchmark/run.sh http://rocky.server:8080 200 60s
```

## Suite results

Using a Macbook Pro i7 2.7 Ghz 16 GB OSX Yosemite and `[email protected]`

##### Simple forward (200 req/sec)
```
# Running benchmark suite: forward
Requests [total] 1000
Duration [total, attack, wait] 9.994724089s, 9.991463892s, 3.260197ms
Latencies [mean, 50, 95, 99, max] 3.696245ms, 3.39041ms, 7.345854ms, 41.871707ms, 41.871707ms
Bytes In [total, mean] 12000, 12.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:1000
```

##### Forward + replay to 2 backends (200 req/sec)
```
# Running benchmark suite: replay
Requests [total] 1000
Duration [total, attack, wait] 9.995610711s, 9.992172904s, 3.437807ms
Latencies [mean, 50, 95, 99, max] 4.825458ms, 4.321398ms, 7.390831ms, 44.316375ms, 44.316375ms
Bytes In [total, mean] 12000, 12.00
Bytes Out [total, mean] 0, 0.00
Success [ratio] 100.00%
Status Codes [code:count] 200:1000
```

##### Forward with POST payload (~250KB) (50 req/sec)
```
# Running benchmark suite: forward-payload
Requests [total] 500
Duration [total, attack, wait] 1m0.401897863s, 9.984355502s, 50.417542361s
Latencies [mean, 50, 95, 99, max] 125.460034ms, 4.99145ms, 10.086713ms, 1m0.001088277s, 1m0.001088277s
Bytes In [total, mean] 6131, 12.26
Bytes Out [total, mean] 116857317, 233714.63
Success [ratio] 97.60%
Status Codes [code:count] 200:486 502:6
Error Set:
502 Bad Gateway
```
95 changes: 95 additions & 0 deletions benchmark/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/bin/bash
#
# Simple benchmark test suite for rocky
#
# You must have vegeta installed:
# go get github.com/tsenart/vegeta
#

#
# Benchmark config
#
url="http://localhost:9000" # default rocky proxy URL
rate=100 # concurrent requests per second
duration=10s # benchmark duration in human friendly format

#
# Read from arguments
#
[ ! -z $1 ] && url=$1
[ ! -z $2 ] && rate=$2
[ ! -z $3 ] && duration=$3

#
# Test variables
#
current=0
proxyPid=0
serverPid=0

if [ -z `which vegeta` ]; then
echo "Error: vegeta binary not found. Run: go get github.com/tsenart/vegeta"
exit 1
fi

cd `dirname $0`

targetServer() {
node servers & > /dev/null
serverPid=$!
}

proxyServer() {
node suites/$1 & > /dev/null
proxyPid=$!
}

before() {
proxyServer $1
targetServer
sleep 1
}

after() {
disown $serverPid
disown $proxyPid
kill -9 $serverPid
kill -9 $proxyPid
}

getBenchmark() {
echo "GET $url" \
| vegeta attack \
-duration=$duration \
-rate=$rate \
| vegeta report
}

postBenchmark() {
echo "POST $url" \
| vegeta attack \
-duration=$duration \
-rate=50 \
-timeout=60s \
-body="../test/fixtures/data.json" \
| vegeta report
}

test() {
before $1

echo "# Running benchmark suite: $1"
$2 # run test function!
echo

after
}

#
# Run suites
#
test "forward" getBenchmark
test "replay" getBenchmark
test "forward-payload" postBenchmark

exit $?
13 changes: 13 additions & 0 deletions benchmark/servers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const http = require('http')

http.createServer(function (req, res) {
res.end('Hello world!')
}).listen(9001)

http.createServer(function (req, res) {
res.end('Hello world!')
}).listen(9002)

http.createServer(function (req, res) {
res.end('Hello world!')
}).listen(9003)
8 changes: 8 additions & 0 deletions benchmark/suites/forward-payload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const rocky = require('../..')
const timeout = 60 * 1000

rocky({ timeout: timeout, proxyTimeout: timeout })
.forward('http://localhost:9001')
.replay('http://localhost:9002')
.listen(9000)
.post('/*')
6 changes: 6 additions & 0 deletions benchmark/suites/forward.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const rocky = require('../..')

rocky()
.forward('http://localhost:9001')
.listen(9000)
.all('/*')
8 changes: 8 additions & 0 deletions benchmark/suites/replay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const rocky = require('../..')

rocky()
.forward('http://localhost:9001')
.replay('http://localhost:9002')
.replay('http://localhost:9003')
.listen(9000)
.all('/*')
18 changes: 9 additions & 9 deletions lib/handler.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
const Dispatcher = require('./dispatcher')

const events = [
'route:error', 'route:missing',
'proxy:error', 'replay:start',
'replay:error', 'error',
'proxyReq', 'proxyRes'
]

module.exports = function handler(rocky, route) {
var dispatcher = new Dispatcher(rocky, route)
const dispatcher = new Dispatcher(rocky, route)

// Propagate route events to global event bus
propagateEvents(rocky, route)
Expand All @@ -12,17 +19,10 @@ module.exports = function handler(rocky, route) {
}
}

const events = [
'route:error', 'route:missing',
'proxy:error', 'replay:start',
'replay:error', 'error',
'proxyReq', 'proxyRes'
]

function propagateEvents(rocky, route) {
events.forEach(function (event) {
route.on(event, function () {
var args = [].slice.call(arguments)
const args = [].slice.call(arguments)
rocky.emit.apply(rocky.emit, [ event ].concat(args))
})
})
Expand Down
Loading

0 comments on commit 46e4c2c

Please sign in to comment.