zebra 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/bin/zebra +10 -0
- data/contrib/config.yml +1 -0
- data/lib/core_ext/hash.rb +21 -0
- data/lib/zebra.rb +29 -0
- data/lib/zebra/command_line.rb +137 -0
- data/lib/zebra/config.rb +65 -0
- data/lib/zebra/proxy_server.rb +106 -0
- data/lib/zebra/proxy_worker.rb +125 -0
- data/lib/zebra/queue.rb +41 -0
- data/lib/zebra/version.rb +3 -0
- data/zebra.gemspec +32 -0
- metadata +254 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/zebra
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift(File.expand_path('.')) # Ruby 1.9 doesn't have . in the load path...
|
3
|
+
$:.push(File.expand_path('lib/'))
|
4
|
+
$0 = __FILE__ + " " + ARGV.join(" ")
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'zebra'
|
8
|
+
|
9
|
+
command_line = Zebra::CommandLine.new
|
10
|
+
command_line.execute
|
data/contrib/config.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
#log_file: "/tmp/zebra.log"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Hash
|
2
|
+
def symbolize_keys!
|
3
|
+
t=self.dup
|
4
|
+
self.clear
|
5
|
+
t.each_pair do |k,v|
|
6
|
+
case v
|
7
|
+
when Hash
|
8
|
+
v.symbolize_keys!
|
9
|
+
when Array
|
10
|
+
v.each do |e|
|
11
|
+
if e.kind_of?(Hash)
|
12
|
+
e.symbolize_keys!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
self[k.to_sym] = v
|
17
|
+
self
|
18
|
+
end
|
19
|
+
self
|
20
|
+
end
|
21
|
+
end
|
data/lib/zebra.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'core_ext/hash'
|
2
|
+
require 'zebra/config'
|
3
|
+
require 'zebra/command_line'
|
4
|
+
require 'zebra/proxy_server'
|
5
|
+
require 'zebra/proxy_worker'
|
6
|
+
require 'zebra/queue'
|
7
|
+
|
8
|
+
module Zebra
|
9
|
+
@@config = Config.instance
|
10
|
+
@@log = Logger.new(STDERR)
|
11
|
+
|
12
|
+
def self.config=(config)
|
13
|
+
@@config=config
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.config
|
17
|
+
@@config
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.log=(log)
|
21
|
+
@@log=log
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.log
|
25
|
+
@@log
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'daemons'
|
3
|
+
|
4
|
+
module Zebra
|
5
|
+
|
6
|
+
class MissingArgumentException < Exception; end
|
7
|
+
class UnsupportedArgumentException < Exception; end
|
8
|
+
|
9
|
+
#Loads command line options and assigns them to singleton Config object
|
10
|
+
class CommandLine
|
11
|
+
attr_reader :controller
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
Zebra.config.chdir = File.dirname(__FILE__)
|
15
|
+
Zebra.config.tmp_dir = '/tmp'
|
16
|
+
Zebra.config.daemonize = false
|
17
|
+
|
18
|
+
begin
|
19
|
+
@parser = OptionParser.new() do |opts|
|
20
|
+
opts.banner = "Usage: #{$0} --config CONFIG [worker|server|queue]"
|
21
|
+
|
22
|
+
opts.on('-c', '--config CONFIG', 'Configuration file') do |config_file|
|
23
|
+
Zebra.config.config_file = config_file
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on('-d', '--daemonize', 'Daemonize the process') do |daemonize|
|
27
|
+
Zebra.config.daemonize = daemonize
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-p', '--pid-file PID-FILE', 'Pid-File to save the process id') do |pid_file|
|
31
|
+
Zebra.config.pid_file = pid_file
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on('-l', '--log-file LOG-FILE', 'Log File') do |log_file|
|
35
|
+
Zebra.config.log_file = log_file
|
36
|
+
end
|
37
|
+
end
|
38
|
+
@parser.parse!
|
39
|
+
Zebra.config.mode = ARGV.shift if ARGV.length > 0
|
40
|
+
raise MissingArgumentException.new("Missing --config parameter") unless Zebra.config.config_file?
|
41
|
+
raise MissingArgumentException.new("Missing mode of operation: server|proxy|queue") unless Zebra.config.mode?
|
42
|
+
rescue MissingArgumentException => e
|
43
|
+
puts usage(e)
|
44
|
+
exit 1
|
45
|
+
rescue ArgumentError => e
|
46
|
+
puts usage(e)
|
47
|
+
exit 1
|
48
|
+
rescue Exception => e
|
49
|
+
puts e.message
|
50
|
+
puts e.backtrace.join("\n\t")
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def usage(e = nil)
|
56
|
+
output = ''
|
57
|
+
case e
|
58
|
+
when MissingArgumentException
|
59
|
+
output += "#{e.message}\n"
|
60
|
+
when Exception
|
61
|
+
output += "#{e.class}: #{e.message}\n"
|
62
|
+
when Nil
|
63
|
+
# Do nothing
|
64
|
+
end
|
65
|
+
output += @parser.to_s
|
66
|
+
output
|
67
|
+
end
|
68
|
+
|
69
|
+
def daemonize
|
70
|
+
# Become a daemon
|
71
|
+
if RUBY_VERSION < "1.9"
|
72
|
+
exit if fork
|
73
|
+
Process.setsid
|
74
|
+
exit if fork
|
75
|
+
Dir.chdir "/"
|
76
|
+
STDIN.reopen "/dev/null"
|
77
|
+
STDOUT.reopen "/dev/null", "a"
|
78
|
+
STDERR.reopen "/dev/null", "a"
|
79
|
+
else
|
80
|
+
Process.daemon
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def execute
|
85
|
+
#If log file is specified logs messages to that file, else on stdout
|
86
|
+
log_file = Zebra.config.log_file
|
87
|
+
fh = nil
|
88
|
+
if log_file
|
89
|
+
fh = File.open(log_file, 'a')
|
90
|
+
else
|
91
|
+
fh = STDERR
|
92
|
+
end
|
93
|
+
|
94
|
+
fh.sync = true
|
95
|
+
Zebra.log = Logger.new(fh)
|
96
|
+
|
97
|
+
Zebra.log.datetime_format = "%Y-%m-%d %H:%M:%S"
|
98
|
+
Zebra.log.formatter = proc { |severity, datetime, progname, msg| sprintf "%-15s | %5s | %s\n", datetime.strftime(Zebra.log.datetime_format), severity, msg }
|
99
|
+
Zebra.config.namespace ||= $0.to_s
|
100
|
+
|
101
|
+
# ZMQ sockets are not thread/process safe
|
102
|
+
daemonize if Zebra.config.daemonize
|
103
|
+
|
104
|
+
begin
|
105
|
+
case Zebra.config.mode.to_sym
|
106
|
+
when :server
|
107
|
+
config = Zebra.config.server || {}
|
108
|
+
config[:logger] = Zebra.log
|
109
|
+
@controller = ProxyServer.new
|
110
|
+
when :worker
|
111
|
+
config = Zebra.config.worker || {}
|
112
|
+
config[:logger] = Zebra.log
|
113
|
+
@controller = ProxyWorker.new(config)
|
114
|
+
when :queue
|
115
|
+
config = Zebra.config.queue || {}
|
116
|
+
config[:logger] = Zebra.log
|
117
|
+
@controller = Queue.new(config)
|
118
|
+
else
|
119
|
+
raise UnsupportedArgumentException.new("Cannot handle #{Zebra.config.mode} mode")
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
if Zebra.config.pid_file?
|
124
|
+
Zebra.log.debug("Writing pid file #{Zebra.config.pid_file}")
|
125
|
+
File.open(Zebra.config.pid_file, 'w') do |f|
|
126
|
+
f.write(Process.pid)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
@controller.dispatch
|
132
|
+
rescue Interrupt => e
|
133
|
+
Zebra.log.info e.message
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/lib/zebra/config.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'deep_merge'
|
3
|
+
require 'yaml'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'net/http'
|
6
|
+
|
7
|
+
module Zebra
|
8
|
+
class UnsupportedURISchemeException < Exception; end
|
9
|
+
class ResourceNotFoundException < Exception; end
|
10
|
+
|
11
|
+
class Config
|
12
|
+
include Singleton
|
13
|
+
@config = nil
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@config = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def read_config_file(config_file)
|
20
|
+
if File.exists?(config_file)
|
21
|
+
yaml = File.read(config_file)
|
22
|
+
else
|
23
|
+
raise ResourceNotFoundException.new("Unable to open #{config_file}")
|
24
|
+
end
|
25
|
+
return yaml
|
26
|
+
end
|
27
|
+
|
28
|
+
def config_file= config_file
|
29
|
+
@config[:config_file] = config_file
|
30
|
+
|
31
|
+
yaml = read_config_file(config_file)
|
32
|
+
config = YAML.load(yaml)
|
33
|
+
@config.deep_merge!(config)
|
34
|
+
@config.symbolize_keys!
|
35
|
+
return @config[:config_file]
|
36
|
+
end
|
37
|
+
|
38
|
+
def base_path
|
39
|
+
File.expand_path(File.dirname(__FILE__) + '/../../')
|
40
|
+
end
|
41
|
+
|
42
|
+
def nil?
|
43
|
+
return @config.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def empty?
|
47
|
+
return @config.empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(id, *args)
|
51
|
+
return nil unless @config.instance_of?(Hash)
|
52
|
+
|
53
|
+
method_name = id.id2name
|
54
|
+
if method_name =~ /^(.*?)\?$/
|
55
|
+
return @config.has_key?($1.to_sym) && !@config[$1.to_sym].nil?
|
56
|
+
elsif method_name =~ /^(.*?)\=$/
|
57
|
+
return @config[$1.to_sym] = args[0]
|
58
|
+
elsif @config.has_key?(method_name.to_sym)
|
59
|
+
return @config[method_name.to_sym]
|
60
|
+
else
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'goliath'
|
2
|
+
require 'goliath/plugins/latency'
|
3
|
+
require 'em-synchrony'
|
4
|
+
require 'em-zeromq'
|
5
|
+
require 'yajl'
|
6
|
+
require 'json'
|
7
|
+
require 'base64'
|
8
|
+
|
9
|
+
module Zebra
|
10
|
+
trap('INT') do
|
11
|
+
EM::stop() if EM::reactor_running?
|
12
|
+
end
|
13
|
+
|
14
|
+
class Goliath::Server
|
15
|
+
def load_config(file = nil)
|
16
|
+
config[:context] = EM::ZeroMQ::Context.new(1)
|
17
|
+
config[:connection_pool] = EM::Synchrony::ConnectionPool.new(:size => 20) do
|
18
|
+
config[:context].socket(ZMQ::REQ) do |socket|
|
19
|
+
socket.connect("tcp://localhost:5559")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Goliath::Runner
|
26
|
+
def run
|
27
|
+
$LOADED_FEATURES.unshift(File.basename($0))
|
28
|
+
Dir.chdir(File.expand_path(Zebra.config.chdir))
|
29
|
+
@port = 8000
|
30
|
+
if Zebra.config.server?
|
31
|
+
@port = ::Zebra.config.server[:port]
|
32
|
+
end
|
33
|
+
run_server
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class ProxyServer < Goliath::API
|
38
|
+
attr_accessor :log
|
39
|
+
|
40
|
+
use Goliath::Rack::Tracer # log trace statistics
|
41
|
+
use Goliath::Rack::DefaultMimeType # cleanup accepted media types
|
42
|
+
use Goliath::Rack::Render, 'json' # auto-negotiate response format
|
43
|
+
use Goliath::Rack::Params # parse & merge query and body parameters
|
44
|
+
use Goliath::Rack::Heartbeat # respond to /status with 200, OK (monitoring, etc)
|
45
|
+
|
46
|
+
# If you are using Golaith version <=0.9.1 you need to Goliath::Rack::ValidationError
|
47
|
+
# to prevent the request from remaining open after an error occurs
|
48
|
+
#use Goliath::Rack::ValidationError
|
49
|
+
use Goliath::Rack::Validation::RequestMethod, %w(GET POST PUT DELETE) # allow GET and POST requests only
|
50
|
+
|
51
|
+
plugin Goliath::Plugin::Latency # output reactor latency every second
|
52
|
+
|
53
|
+
def response(env)
|
54
|
+
#ap env
|
55
|
+
json = env.select { |k,v| v.instance_of?(String) }.to_json
|
56
|
+
@log.debug "Sending #{json}"
|
57
|
+
|
58
|
+
config[:connection_pool].execute(false) do |conn|
|
59
|
+
handler = EM::Protocols::ZMQConnectionHandler.new(conn)
|
60
|
+
reply = handler.send_msg(json).first
|
61
|
+
#ap reply
|
62
|
+
if reply.eql?('null')
|
63
|
+
response = [500, {}, 'Server Error']
|
64
|
+
else
|
65
|
+
response = JSON.parse(reply)
|
66
|
+
response[2] = Base64.decode64(response[2])
|
67
|
+
end
|
68
|
+
#ap response
|
69
|
+
return response
|
70
|
+
#[200, {}, resp]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def initialize(config={})
|
75
|
+
super
|
76
|
+
@log = config[:logger] || Logger.new(STDERR)
|
77
|
+
end
|
78
|
+
|
79
|
+
def dispatch
|
80
|
+
# Don't need to do anything, handled by Goliath
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class EM::Protocols::ZMQConnectionHandler
|
85
|
+
attr_reader :received
|
86
|
+
|
87
|
+
def initialize(connection)
|
88
|
+
@connection = connection
|
89
|
+
@client_fiber = Fiber.current
|
90
|
+
@connection.setsockopt(ZMQ::IDENTITY, "req-#{@client_fiber.object_id}")
|
91
|
+
@connection.handler = self
|
92
|
+
end
|
93
|
+
|
94
|
+
def send_msg(*parts)
|
95
|
+
queued = @connection.send_msg(*parts)
|
96
|
+
@connection.register_readable
|
97
|
+
messages = Fiber.yield
|
98
|
+
messages.map(&:copy_out_string)
|
99
|
+
end
|
100
|
+
|
101
|
+
def on_readable(socket, messages)
|
102
|
+
@client_fiber.resume(messages)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ffi-rzmq'
|
3
|
+
require 'json'
|
4
|
+
require 'logger'
|
5
|
+
require 'uuid'
|
6
|
+
require 'em-zeromq'
|
7
|
+
require 'em-synchrony'
|
8
|
+
require 'em-synchrony/em-http'
|
9
|
+
require 'em-synchrony/fiber_iterator'
|
10
|
+
require 'fiber'
|
11
|
+
require 'base64'
|
12
|
+
require 'preforker'
|
13
|
+
|
14
|
+
module Zebra
|
15
|
+
class ProxyWorkerReceiveMessageHandler
|
16
|
+
attr_reader :received
|
17
|
+
|
18
|
+
def initialize(config)
|
19
|
+
@config = config
|
20
|
+
@logger = config[:logger] || Logger.new(STDERR)
|
21
|
+
@conns = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_headers(response_headers)
|
25
|
+
raw_headers = {}
|
26
|
+
response_headers.select { |k,v| k =~ /^[A-Z0-9_]+$/ }.each_pair { |k,v| raw_headers[ k.downcase.split('_').collect { |e| e.capitalize }.join('-') ] = v}
|
27
|
+
raw_headers
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_conn(url)
|
31
|
+
uri = URI.parse(url)
|
32
|
+
conn_key = uri.scheme + '://' + uri.host
|
33
|
+
return EM::HttpRequest.new(conn_key, :connect_timeout => 1, :inactivity_timeout => 1)
|
34
|
+
if @conns.has_key?(conn_key)
|
35
|
+
return @conns[conn_key]
|
36
|
+
else
|
37
|
+
return @conns[conn_key] = EM::HttpRequest.new(conn_key, :connect_timeout => 1, :inactivity_timeout => 1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def fetch(method, url, request_headers = {})
|
42
|
+
@response = nil
|
43
|
+
@logger.debug "Proxying #{method} #{url} #{request_headers.inspect}"
|
44
|
+
t_start = Time.now
|
45
|
+
uri = URI.parse(url)
|
46
|
+
conn = get_conn(url)
|
47
|
+
request_headers['Host'] = uri.host
|
48
|
+
http = conn.send(method, path: uri.path, query: uri.query, head: request_headers, :keepalive => true)
|
49
|
+
@logger.debug "Request finished"
|
50
|
+
response_headers = to_headers(http.response_header)
|
51
|
+
#ap response_headers
|
52
|
+
response_headers['X-Proxied-By'] = 'Zebra'
|
53
|
+
response_headers.delete('Connection')
|
54
|
+
response_headers.delete('Content-Length')
|
55
|
+
response_headers.delete('Transfer-Encoding')
|
56
|
+
t_end = Time.now
|
57
|
+
elapsed = t_end.to_f - t_start.to_f
|
58
|
+
@logger.info "#{elapsed} elapsed"
|
59
|
+
@logger.info "Received #{http.response_header.status} from server, #{http.response.length} bytes"
|
60
|
+
[http.response_header.status, response_headers, Base64.encode64(http.response)]
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_writeable(socket)
|
64
|
+
@logger.debug("Writable")
|
65
|
+
end
|
66
|
+
|
67
|
+
def handle_message(m)
|
68
|
+
#ap m.copy_out_string
|
69
|
+
env = JSON.parse(m.copy_out_string)
|
70
|
+
#url = 'http://' + env['HTTP_HOST'] + env['REQUEST_URI']
|
71
|
+
url = env['REQUEST_URI']
|
72
|
+
method = env['REQUEST_METHOD'].downcase.to_sym
|
73
|
+
fetch(method, url)
|
74
|
+
end
|
75
|
+
|
76
|
+
def on_readable(socket, messages)
|
77
|
+
@logger.debug "on_readable #{messages.inspect}"
|
78
|
+
fiber = Fiber.new do
|
79
|
+
m = messages.first
|
80
|
+
response = handle_message(m).to_json
|
81
|
+
socket.send_msg response
|
82
|
+
end
|
83
|
+
fiber.resume
|
84
|
+
@logger.debug "Finished on_readable"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class ProxyWorker
|
89
|
+
attr_accessor :log, :workers, :app_name, :timeout
|
90
|
+
|
91
|
+
def initialize(config = {})
|
92
|
+
@log = config[:logger] || Logger.new(STDERR)
|
93
|
+
@workers = config[:workers] || 10
|
94
|
+
@timeout = config[:timeout] || 3600
|
95
|
+
@app_name = config[:app_name] || File.basename($0.split(/ /)[0], '.rb')
|
96
|
+
end
|
97
|
+
|
98
|
+
def dispatch
|
99
|
+
params = { :workers => @workers,
|
100
|
+
:app_name => @app_name,
|
101
|
+
:logger => @log,
|
102
|
+
:timeout => @timeout }
|
103
|
+
workers = Preforker.new(params) do |master|
|
104
|
+
config = {:logger => @log}
|
105
|
+
handler = ProxyWorkerReceiveMessageHandler.new(config)
|
106
|
+
while master.wants_me_alive? do
|
107
|
+
EM.synchrony do
|
108
|
+
master.logger.info "Server started..."
|
109
|
+
|
110
|
+
timer = EventMachine::PeriodicTimer.new(5) do
|
111
|
+
master.logger.info "ping #{Process.pid}"
|
112
|
+
end
|
113
|
+
|
114
|
+
context = EM::ZeroMQ::Context.new(1)
|
115
|
+
# connection_pool = EM::Synchrony::ConnectionPool.new(:size => 1) do
|
116
|
+
socket = context.socket(ZMQ::REP, handler)
|
117
|
+
socket.connect('tcp://localhost:5560')
|
118
|
+
# end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
workers.run
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/zebra/queue.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'ffi-rzmq'
|
2
|
+
|
3
|
+
module Zebra
|
4
|
+
class Queue
|
5
|
+
attr_accessor :context, :frontend, :backend, :poller, :log, :frontend_uri, :backend_uri
|
6
|
+
def initialize(config)
|
7
|
+
@log = config[:logger] || Logger.new(STDERR)
|
8
|
+
@frontend_uri = config[:frontend] || 'tcp://*:5559'
|
9
|
+
@backend_uri = config[:backend] || 'tcp://*:5560'
|
10
|
+
@context = ZMQ::Context.new
|
11
|
+
|
12
|
+
# Socket facing clients
|
13
|
+
@frontend = context.socket(ZMQ::ROUTER)
|
14
|
+
|
15
|
+
# Socket facing services
|
16
|
+
@backend = context.socket(ZMQ::DEALER)
|
17
|
+
trap("INT") do
|
18
|
+
@log.info "Shutting down."
|
19
|
+
@frontend.close unless @frontend.nil?
|
20
|
+
@backend.close unless @backend.nil?
|
21
|
+
@context.terminate unless @context.nil?
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def dispatch
|
27
|
+
@frontend.bind(@frontend_uri)
|
28
|
+
@backend.bind(@backend_uri)
|
29
|
+
begin
|
30
|
+
# Start built-in device
|
31
|
+
@poller = ZMQ::Device.new(ZMQ::QUEUE,frontend,backend)
|
32
|
+
rescue Interrupt => e
|
33
|
+
@log.info("Caught interrupt signal...")
|
34
|
+
end
|
35
|
+
|
36
|
+
@frontend.close
|
37
|
+
@backend.close
|
38
|
+
@context.terminate
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/zebra.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "zebra/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "zebra"
|
7
|
+
s.version = Zebra::VERSION
|
8
|
+
s.authors = ["osterman"]
|
9
|
+
s.email = ["e@osterman.com"]
|
10
|
+
s.homepage = "https://github.com/osterman/zebra"
|
11
|
+
s.summary = %q{A Goliath ZMQ Reverse HTTP Proxy Implementation}
|
12
|
+
s.description = %q{A proxy that uses ZMQ as the wire protocol between HTTP proxy gateway and backend worker nodes. This allows for each load distribution to worker nodes behind a firewall.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "zebra"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency "bundler"
|
22
|
+
s.add_runtime_dependency "deep_merge", ">= 1.0.0"
|
23
|
+
s.add_runtime_dependency "em-synchrony", ">= 1.0.0"
|
24
|
+
s.add_runtime_dependency "em-http-request", ">=1.0.2"
|
25
|
+
s.add_runtime_dependency "em-zeromq", ">= 0.3.0"
|
26
|
+
s.add_runtime_dependency "ffi-rzmq", ">= 0.9.3"
|
27
|
+
s.add_runtime_dependency "goliath", ">= 0.9.4"
|
28
|
+
s.add_runtime_dependency "json", ">= 1.6.5"
|
29
|
+
s.add_runtime_dependency "preforker", ">= 0.1.1"
|
30
|
+
s.add_runtime_dependency "uuid", ">= 2.3.5"
|
31
|
+
s.add_runtime_dependency "yajl-ruby", ">= 1.1.0"
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zebra
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- osterman
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-07-03 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: bundler
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: deep_merge
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 23
|
44
|
+
segments:
|
45
|
+
- 1
|
46
|
+
- 0
|
47
|
+
- 0
|
48
|
+
version: 1.0.0
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: em-synchrony
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 23
|
60
|
+
segments:
|
61
|
+
- 1
|
62
|
+
- 0
|
63
|
+
- 0
|
64
|
+
version: 1.0.0
|
65
|
+
type: :runtime
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: em-http-request
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 19
|
76
|
+
segments:
|
77
|
+
- 1
|
78
|
+
- 0
|
79
|
+
- 2
|
80
|
+
version: 1.0.2
|
81
|
+
type: :runtime
|
82
|
+
version_requirements: *id004
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: em-zeromq
|
85
|
+
prerelease: false
|
86
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 19
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
- 3
|
95
|
+
- 0
|
96
|
+
version: 0.3.0
|
97
|
+
type: :runtime
|
98
|
+
version_requirements: *id005
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: ffi-rzmq
|
101
|
+
prerelease: false
|
102
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
hash: 61
|
108
|
+
segments:
|
109
|
+
- 0
|
110
|
+
- 9
|
111
|
+
- 3
|
112
|
+
version: 0.9.3
|
113
|
+
type: :runtime
|
114
|
+
version_requirements: *id006
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: goliath
|
117
|
+
prerelease: false
|
118
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
hash: 51
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
- 9
|
127
|
+
- 4
|
128
|
+
version: 0.9.4
|
129
|
+
type: :runtime
|
130
|
+
version_requirements: *id007
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: json
|
133
|
+
prerelease: false
|
134
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
hash: 5
|
140
|
+
segments:
|
141
|
+
- 1
|
142
|
+
- 6
|
143
|
+
- 5
|
144
|
+
version: 1.6.5
|
145
|
+
type: :runtime
|
146
|
+
version_requirements: *id008
|
147
|
+
- !ruby/object:Gem::Dependency
|
148
|
+
name: preforker
|
149
|
+
prerelease: false
|
150
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
151
|
+
none: false
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
hash: 25
|
156
|
+
segments:
|
157
|
+
- 0
|
158
|
+
- 1
|
159
|
+
- 1
|
160
|
+
version: 0.1.1
|
161
|
+
type: :runtime
|
162
|
+
version_requirements: *id009
|
163
|
+
- !ruby/object:Gem::Dependency
|
164
|
+
name: uuid
|
165
|
+
prerelease: false
|
166
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
167
|
+
none: false
|
168
|
+
requirements:
|
169
|
+
- - ">="
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
hash: 9
|
172
|
+
segments:
|
173
|
+
- 2
|
174
|
+
- 3
|
175
|
+
- 5
|
176
|
+
version: 2.3.5
|
177
|
+
type: :runtime
|
178
|
+
version_requirements: *id010
|
179
|
+
- !ruby/object:Gem::Dependency
|
180
|
+
name: yajl-ruby
|
181
|
+
prerelease: false
|
182
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
183
|
+
none: false
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
hash: 19
|
188
|
+
segments:
|
189
|
+
- 1
|
190
|
+
- 1
|
191
|
+
- 0
|
192
|
+
version: 1.1.0
|
193
|
+
type: :runtime
|
194
|
+
version_requirements: *id011
|
195
|
+
description: A proxy that uses ZMQ as the wire protocol between HTTP proxy gateway and backend worker nodes. This allows for each load distribution to worker nodes behind a firewall.
|
196
|
+
email:
|
197
|
+
- e@osterman.com
|
198
|
+
executables:
|
199
|
+
- zebra
|
200
|
+
extensions: []
|
201
|
+
|
202
|
+
extra_rdoc_files: []
|
203
|
+
|
204
|
+
files:
|
205
|
+
- .gitignore
|
206
|
+
- Gemfile
|
207
|
+
- Rakefile
|
208
|
+
- bin/zebra
|
209
|
+
- contrib/config.yml
|
210
|
+
- lib/core_ext/hash.rb
|
211
|
+
- lib/zebra.rb
|
212
|
+
- lib/zebra/command_line.rb
|
213
|
+
- lib/zebra/config.rb
|
214
|
+
- lib/zebra/proxy_server.rb
|
215
|
+
- lib/zebra/proxy_worker.rb
|
216
|
+
- lib/zebra/queue.rb
|
217
|
+
- lib/zebra/version.rb
|
218
|
+
- zebra.gemspec
|
219
|
+
has_rdoc: true
|
220
|
+
homepage: https://github.com/osterman/zebra
|
221
|
+
licenses: []
|
222
|
+
|
223
|
+
post_install_message:
|
224
|
+
rdoc_options: []
|
225
|
+
|
226
|
+
require_paths:
|
227
|
+
- lib
|
228
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
229
|
+
none: false
|
230
|
+
requirements:
|
231
|
+
- - ">="
|
232
|
+
- !ruby/object:Gem::Version
|
233
|
+
hash: 3
|
234
|
+
segments:
|
235
|
+
- 0
|
236
|
+
version: "0"
|
237
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
238
|
+
none: false
|
239
|
+
requirements:
|
240
|
+
- - ">="
|
241
|
+
- !ruby/object:Gem::Version
|
242
|
+
hash: 3
|
243
|
+
segments:
|
244
|
+
- 0
|
245
|
+
version: "0"
|
246
|
+
requirements: []
|
247
|
+
|
248
|
+
rubyforge_project: zebra
|
249
|
+
rubygems_version: 1.3.7
|
250
|
+
signing_key:
|
251
|
+
specification_version: 3
|
252
|
+
summary: A Goliath ZMQ Reverse HTTP Proxy Implementation
|
253
|
+
test_files: []
|
254
|
+
|