zebra 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|