Kage
Kage (kah-geh) is an HTTP shadow proxy server that sits between clients and your server(s) to enable "shadow requests".
Kage can be used to duplex requests to the master (production) server and shadow servers that have newer code changes that are going to be deployed. By shadowing requests to the new code you can make sure there are no big/surprising changes in the response in terms of data, performance and database loads etc.
Kage is built with EventMachine and em-proxy and all shadow requests are done asynchronously, while responses from master are sent back to the client without blocking the network, so clients will never notice any delays even when shadow traffic is made.
You can customize the behavior of Kage with simple callbacks, when it chooses which backends to send shadow requests to (or not at all), appends or deletes HTTP headers per backend, and examines the complete HTTP response (including headers and body).
Features
- Support HTTP/1.0 and HTTP/1.1 with partial keep-alive support (See below)
- Callback to decide backends per request URLs
- Callback to manipulate request headers per request and backend
- Callback to examine responses from multiple backends (e.g. calcurate diffs)
Kage does not yet support:
- SSL
- HTTP/1.1 requests pipelining
Usage
require 'kage'
def compare(a, b)
p [a, b]
end
Kage::ProxyServer.start do |server|
server.port = 8090
server.host = '0.0.0.0'
server.debug = false
# backends can share the same host/port
server.add_master_backend(:production, 'localhost', 80)
server.add_backend(:sandbox, 'localhost', 80)
server.client_timeout = 15
server.backend_timeout = 10
# Dispatch all GET requests to multiple backends, otherwise only :production
server.on_select_backends do |request, headers|
if request[:method] == 'GET'
[:production, :sandbox]
else
[:production]
end
end
# Add optional headers
server.on_munge_headers do |backend, headers|
headers['X-Kage-Session'] = self.session_id
headers['X-Kage-Sandbox'] = 1 if backend == :sandbox
end
# This callback is only fired when there are multiple backends to respond
server.on_backends_finished do |backends, requests, responses|
compare(responses[:production][:data], responses[:sandbox][:data])
end
end
Read more sample code under the examples/
directory.
Keep-alives
Kage supports keep-alives for single backend requests, i.e. for requests where on_select_backends
returns only the master backend.
To make on_backend_finished
callback simpler, if the current request matches with multiple backends, Kage sends Connection: close
to the backends so that the callback will only get one response per backend in responses
hash, which would look like:
responses = {
:original => {:data => "(RAW HTTP response)", :elapsed => 0.1234},
:sandbox => {:data => "(RAW HTTP response)", :elapsed => 0.2333},
}
Authors
Tatsuhiko Miyagawa, Yusuke Mito
Acknowledgements
Ilya Grigorik, Jos Boumans