Created
December 11, 2012 15:14
-
-
Save dblock/4259205 to your computer and use it in GitHub Desktop.
Reverse-proxy setup to beat the Heroku 60 second boot timeout.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$stdout.sync = true | |
require ::File.expand_path('../config/environment', __FILE__) | |
run MyApp::Application |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Setup a reverse proxy for faster Heroku boot and future load-balancing. | |
# http://noverloop.be/beating-herokus-60s-boot-times-with-the-cedar-stack-and-a-reverse-proxy | |
# http://jgwmaxwell.com/fighting-the-unicorns-becoming-a-thin-wizard-on-heroku | |
# https://github.com/jgwmaxwell/heroku-thin-cluster | |
# | |
require 'rubygems' | |
require 'bundler' | |
$stdout.sync = true | |
Bundler.require(:rack) | |
PORT = (ARGV.first || 3000).to_i | |
RACK_ENV = ENV['RACK_ENV'] || 'development' | |
ENV['PORT'] = PORT.to_s | |
ENV['RACK_ENV'] = RACK_ENV | |
require 'tmpdir' | |
socket = File.join(Dir.tmpdir, "thin.sock") | |
puts "Launching Thin on #{socket}" | |
pid = spawn("thin start -R app.ru --socket #{socket} -e #{RACK_ENV}") | |
sleep 5 # wait till thin binds to port, otherwise the proxy will die on first request | |
require File.expand_path('../lib/forward_proxy', __FILE__) | |
ForwardProxy::Server.run '0.0.0.0', PORT, pid, socket # start proxy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# inspired by https://github.com/jgwmaxwell/heroku-thin-cluster | |
# also see https://github.com/igrigorik/em-proxy/issues/30 | |
require 'em-proxy' | |
require 'uri' | |
require 'socket' | |
module ForwardProxy | |
extend self | |
# Represents a "backend", ie. the endpoint for the proxy. | |
class Backend | |
attr_reader :socket | |
def initialize(options = {}) | |
@socket = options[:socket] | |
end | |
end | |
# Callbacks for em-proxy events | |
module Callbacks | |
extend self | |
def on_connect | |
end | |
def on_finish | |
end | |
def on_data | |
lambda do |data| | |
data | |
end | |
end | |
def on_response | |
lambda do |backend, resp| | |
resp | |
end | |
end | |
end | |
# Wrapping the proxy server | |
module Server | |
RETRIES = 10 | |
def run(host = '0.0.0.0', port = 3000, pid, socket) | |
@pid = pid | |
@host = host | |
@port = port | |
@pid = pid | |
@start = Time.now | |
@retries = 0 | |
puts "Launching Proxy Server at #{host}:#{port} ..." | |
@backend = Backend.new({ socket: socket }) | |
Proxy.start(host: host, port: port, debug: false) do |conn| | |
if @start | |
EM.next_tick do | |
Server.connect(conn) | |
end | |
else | |
Server.connect(conn) | |
end | |
conn.on_connect &Callbacks.on_connect | |
conn.on_data &Callbacks.on_data | |
conn.on_response &Callbacks.on_response | |
conn.on_finish &Callbacks.on_finish | |
end | |
end | |
def stop | |
puts "Terminating Proxy Server" | |
EventMachine.stop | |
puts "Terminating Web Server #{pid}" | |
Process.kill('QUIT', @pid) | |
end | |
module_function :run | |
module_function :stop | |
private | |
def connect(conn) | |
begin | |
if @start | |
$stdout.puts "Attempting to connect to #{@backend.socket}." | |
end | |
conn.server @backend, socket: @backend.socket | |
if @start | |
$stdout.puts "Proxy Server ready at #{@host}:#{@port} (#{(Time.now - @start).to_i}s)." | |
@start = nil | |
end | |
rescue RuntimeError => e | |
$stderr.puts "ERROR: #{e.message} after #{@retries} #{@retries == 1 ? 'retry' : 'retries'}" | |
@retries += 1 | |
end | |
end | |
module_function :connect | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You will also need em-proxy from https://github.com/dblock/em-proxy or with igrigorik/em-proxy#31 merged.