swiftiply 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +126 -0
- data/bin/mongrel_rails +254 -0
- data/bin/swiftiply +136 -0
- data/bin/swiftiply_mongrel_rails +54 -0
- data/external/package.rb +672 -0
- data/external/test_support.rb +58 -0
- data/setup.rb +40 -0
- data/src/ramaze/adapter/evented_mongrel.rb +2 -0
- data/src/ramaze/adapter/swiftiplied_mongrel.rb +2 -0
- data/src/swiftcore/Swiftiply.rb +444 -0
- data/src/swiftcore/Swiftiply.rb.orig +390 -0
- data/src/swiftcore/evented_mongrel.rb +182 -0
- data/src/swiftcore/swiftiplied_mongrel.rb +212 -0
- data/swiftiply.gemspec +41 -0
- data/test/rails/README +182 -0
- data/test/rails/Rakefile +10 -0
- data/test/rails/app/controllers/application.rb +6 -0
- data/test/rails/app/controllers/tests_controller.rb +15 -0
- data/test/rails/app/helpers/application_helper.rb +3 -0
- data/test/rails/config/boot.rb +45 -0
- data/test/rails/config/database.yml +36 -0
- data/test/rails/config/environment.rb +60 -0
- data/test/rails/config/environments/development.rb +21 -0
- data/test/rails/config/environments/production.rb +18 -0
- data/test/rails/config/environments/production_no_caching.rb +18 -0
- data/test/rails/config/environments/test.rb +19 -0
- data/test/rails/config/routes.rb +23 -0
- data/test/rails/doc/README_FOR_APP +2 -0
- data/test/rails/observe_ram.rb +10 -0
- data/test/rails/public/404.html +30 -0
- data/test/rails/public/500.html +30 -0
- data/test/rails/public/dispatch.cgi +10 -0
- data/test/rails/public/dispatch.fcgi +24 -0
- data/test/rails/public/dispatch.rb +10 -0
- data/test/rails/public/favicon.ico +0 -0
- data/test/rails/public/images/rails.png +0 -0
- data/test/rails/public/index.html +277 -0
- data/test/rails/public/javascripts/application.js +2 -0
- data/test/rails/public/javascripts/controls.js +833 -0
- data/test/rails/public/javascripts/dragdrop.js +942 -0
- data/test/rails/public/javascripts/effects.js +1088 -0
- data/test/rails/public/javascripts/prototype.js +2515 -0
- data/test/rails/public/robots.txt +1 -0
- data/test/rails/script/about +3 -0
- data/test/rails/script/breakpointer +3 -0
- data/test/rails/script/console +3 -0
- data/test/rails/script/destroy +3 -0
- data/test/rails/script/generate +3 -0
- data/test/rails/script/performance/benchmarker +3 -0
- data/test/rails/script/performance/profiler +3 -0
- data/test/rails/script/plugin +3 -0
- data/test/rails/script/process/inspector +3 -0
- data/test/rails/script/process/reaper +3 -0
- data/test/rails/script/process/spawner +3 -0
- data/test/rails/script/runner +3 -0
- data/test/rails/script/server +3 -0
- data/test/rails/test/test_helper.rb +28 -0
- data/test/ramaze/conf/benchmark.yaml +35 -0
- data/test/ramaze/conf/debug.yaml +34 -0
- data/test/ramaze/conf/live.yaml +33 -0
- data/test/ramaze/conf/silent.yaml +31 -0
- data/test/ramaze/conf/stage.yaml +33 -0
- data/test/ramaze/main.rb +18 -0
- data/test/ramaze/public/404.jpg +0 -0
- data/test/ramaze/public/css/coderay.css +105 -0
- data/test/ramaze/public/css/ramaze_error.css +42 -0
- data/test/ramaze/public/error.zmr +77 -0
- data/test/ramaze/public/favicon.ico +0 -0
- data/test/ramaze/public/js/jquery.js +1923 -0
- data/test/ramaze/public/ramaze.png +0 -0
- data/test/ramaze/src/controller/main.rb +8 -0
- data/test/ramaze/src/element/page.rb +17 -0
- data/test/ramaze/src/model.rb +6 -0
- data/test/ramaze/template/index.xhtml +6 -0
- data/test/ramaze/yaml.db +0 -0
- metadata +189 -0
data/README
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
Swiftiply v. 0.5.0 (http://swiftiply.swiftcore.org)
|
2
|
+
|
3
|
+
Swiftiply is a backend agnostic clustering proxy for web applications that is
|
4
|
+
specifically designed to support HTTP traffic from web frameworks. Unlike Pen
|
5
|
+
(http://siag.nu/pen/), Swiftiply is not intended as a general purpose load
|
6
|
+
balancer for tcp protocols and unlike HAProxy (http://haproxy.1wt.eu/), it is
|
7
|
+
not a highly configurable general purpose proxy overflowing with features.
|
8
|
+
|
9
|
+
What it is, though, is a very fast, narrowly targetted clustering proxy.
|
10
|
+
In back to back comparisons of Swiftiply to HAProxy, Swiftiply reliably
|
11
|
+
outperforms HAProxy (tested using IOWA, Rails, Merb, and Ramaze backend
|
12
|
+
processes running Mongrel).
|
13
|
+
|
14
|
+
Swiftiply works differently from a traditional proxy. In Swiftiply, the
|
15
|
+
backend processes are clients of the Swiftiply server -- they make persistent
|
16
|
+
socket connections to Swiftiply. One of the major advantages to this
|
17
|
+
architecture is that it allows one to start or stop backend processes at will,
|
18
|
+
with no configuration of the proxy. The obvious disadvantage is that this is
|
19
|
+
not behavior that backends typically expect.
|
20
|
+
|
21
|
+
Because Mongrel is the preferred deployment method for most Ruby frameworks,
|
22
|
+
Swiftiply includes a version of Mongrel (found in swiftcore/swiftiplied_mongrel.rb)
|
23
|
+
that has been modified to work as a swiftiply client. This should be
|
24
|
+
transparent to any existing Mongrel handlers, allowing them all to with
|
25
|
+
Swiftiply.
|
26
|
+
|
27
|
+
In addition, as an offshoot of the swiftiplied_mongrel, there is a second
|
28
|
+
version that is available. This other version is found in
|
29
|
+
swiftcore/evented_mongrel.rb; it is a version of Mongrel that has its network
|
30
|
+
traffic handled by EventMachine, creating a Mongrel that runs in an event
|
31
|
+
based mode instead of a threaded mode. For many applications, running in an
|
32
|
+
event based mode will give better throughput than running in a threaded mode,
|
33
|
+
especially when there are concurrent requests coming in.
|
34
|
+
|
35
|
+
This is because the event based operation handles requests efficiently, on
|
36
|
+
a first come, first served basis, without the overhead of threads. For the
|
37
|
+
typical Rails application, this means that request handling may be slightly
|
38
|
+
faster than the threaded Mongrel for single, non-concurrent requests. When
|
39
|
+
there are concurrent requests, though, the differential increases quickly.
|
40
|
+
|
41
|
+
|
42
|
+
FRAMEWORK SUPPORT
|
43
|
+
|
44
|
+
|
45
|
+
Swiftcore IOWA
|
46
|
+
|
47
|
+
IOWA has built in support for running in evented and clustered modes.
|
48
|
+
|
49
|
+
|
50
|
+
Rails
|
51
|
+
|
52
|
+
Swiftiply provides a _REPLACEMENT_ to mongrel_rails that, throught the use
|
53
|
+
of an environment variable, can be told to run in either the evented mode or
|
54
|
+
the swiftiplied mode.
|
55
|
+
|
56
|
+
To run a Rails app in evented mode, set the EVENT environment variable. On
|
57
|
+
a unixlike system:
|
58
|
+
|
59
|
+
env EVENT=1 mongrel_rails
|
60
|
+
|
61
|
+
will do it.
|
62
|
+
|
63
|
+
To run in swiftiplied mode:
|
64
|
+
|
65
|
+
env SWIFTIPLY=1 mongrel_rails
|
66
|
+
|
67
|
+
Because Swiftiply backends connect to the Swiftiply server, they all connect
|
68
|
+
on the same port. This is important. Each of the backends runs against the
|
69
|
+
same port. To make it easier to start multiple Rails backends, a helper
|
70
|
+
script, swiftiply_mongrel_rails, is provided. It is just a light wrapper
|
71
|
+
around mongrel_rails that will let one start N backends, with proper pid
|
72
|
+
files, and stop them.
|
73
|
+
|
74
|
+
|
75
|
+
Merb
|
76
|
+
|
77
|
+
The merb source (trunk only, at this point), has Swiftiply support that works
|
78
|
+
just like the Rails support, built in.
|
79
|
+
|
80
|
+
|
81
|
+
Ramaze
|
82
|
+
|
83
|
+
A couple adapters for Ramaze are included, to allow Ramaze to run with either
|
84
|
+
the evented or the swiftiplied mongrels. They are installed into
|
85
|
+
|
86
|
+
ramaze/adapter/evented_mongrel.rb
|
87
|
+
ramaze/adapter/swiftiplied_mongrel.rb
|
88
|
+
|
89
|
+
|
90
|
+
Other Frameworks
|
91
|
+
|
92
|
+
Swiftiply has been tested with Camping and Nitro, as well. Direct support for
|
93
|
+
them is not yet bundled, but will be in an upcoming release.
|
94
|
+
|
95
|
+
|
96
|
+
CONFIGURATION
|
97
|
+
|
98
|
+
Swiftiply takes a single configuration file which defines for it where it
|
99
|
+
should listen for incoming connections, whether it should daemonize itself,
|
100
|
+
and then provides a map of incoming domain names and the address/port to
|
101
|
+
proxy that traffic to. That outgoing address/port is where the backends for
|
102
|
+
that site will connect to.
|
103
|
+
|
104
|
+
Here's an example:
|
105
|
+
|
106
|
+
cluster_address: swiftcore.org
|
107
|
+
cluster_port: 80
|
108
|
+
daemonize: true
|
109
|
+
map:
|
110
|
+
- incoming:
|
111
|
+
- swiftcore.org
|
112
|
+
- www.swiftcore.org
|
113
|
+
outgoing: 127.0.0.1:30000
|
114
|
+
default: true
|
115
|
+
- incoming: iowa.swiftcore.org
|
116
|
+
outgoing: 127.0.0.1:30010
|
117
|
+
- incoming: analogger.swiftcore.org
|
118
|
+
outgoing: 127.0.0.1:30020
|
119
|
+
- incoming:
|
120
|
+
- swiftiply.com
|
121
|
+
- www.swiftiply.com
|
122
|
+
- swiftiply.swiftcore.org
|
123
|
+
outgoing: 127.0.0.1:30030
|
124
|
+
|
125
|
+
|
126
|
+
|
data/bin/mongrel_rails
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright (c) 2005 Zed A. Shaw
|
3
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
4
|
+
#
|
5
|
+
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
6
|
+
# for more information.
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'yaml'
|
10
|
+
if ENV['EVENT']
|
11
|
+
require 'swiftcore/evented_mongrel'
|
12
|
+
puts "Using Evented Mongrel"
|
13
|
+
elsif ENV['SWIFT']
|
14
|
+
require 'swiftcore/swiftiplied_mongrel'
|
15
|
+
puts "Using Evented Mongrel"
|
16
|
+
else
|
17
|
+
require 'mongrel'
|
18
|
+
end
|
19
|
+
require 'mongrel/rails'
|
20
|
+
require 'etc'
|
21
|
+
require 'cgi_multipart_eof_fix' rescue nil
|
22
|
+
|
23
|
+
module Mongrel
|
24
|
+
class Start < GemPlugin::Plugin "/commands"
|
25
|
+
include Mongrel::Command::Base
|
26
|
+
|
27
|
+
def configure
|
28
|
+
options [
|
29
|
+
["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],
|
30
|
+
["-d", "--daemonize", "Run daemonized in the background", :@daemon, false],
|
31
|
+
['-p', '--port PORT', "Which port to bind to", :@port, 3000],
|
32
|
+
['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"],
|
33
|
+
['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"],
|
34
|
+
['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"],
|
35
|
+
['-n', '--num-procs INT', "Number of processors active before clients denied", :@num_procs, 1024],
|
36
|
+
['-t', '--timeout TIME', "Timeout all requests after 100th seconds time", :@timeout, 0],
|
37
|
+
['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil],
|
38
|
+
['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
|
39
|
+
['-r', '--root PATH', "Set the document root (default 'public')", :@docroot, "public"],
|
40
|
+
['-B', '--debug', "Enable debugging mode", :@debug, false],
|
41
|
+
['-C', '--config PATH', "Use a config file", :@config_file, nil],
|
42
|
+
['-S', '--script PATH', "Load the given file as an extra config script", :@config_script, nil],
|
43
|
+
['-G', '--generate PATH', "Generate a config file for use with -C", :@generate, nil],
|
44
|
+
['', '--user USER', "User to run as", :@user, nil],
|
45
|
+
['', '--group GROUP', "Group to run as", :@group, nil],
|
46
|
+
['', '--prefix PATH', "URL prefix for Rails app", :@prefix, nil]
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate
|
51
|
+
@cwd = File.expand_path(@cwd)
|
52
|
+
valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
|
53
|
+
|
54
|
+
# Change there to start, then we'll have to come back after daemonize
|
55
|
+
Dir.chdir(@cwd)
|
56
|
+
|
57
|
+
valid?(@prefix[0].chr == "/" && @prefix[-1].chr != "/", "Prefix must begin with / and not end in /") if @prefix
|
58
|
+
valid_dir? File.dirname(@log_file), "Path to log file not valid: #@log_file"
|
59
|
+
valid_dir? File.dirname(@pid_file), "Path to pid file not valid: #@pid_file"
|
60
|
+
valid_dir? @docroot, "Path to docroot not valid: #@docroot"
|
61
|
+
valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" if @mime_map
|
62
|
+
valid_exists? @config_file, "Config file not there: #@config_file" if @config_file
|
63
|
+
valid_dir? File.dirname(@generate), "Problem accessing directory to #@generate" if @generate
|
64
|
+
valid_user? @user if @user
|
65
|
+
valid_group? @group if @group
|
66
|
+
|
67
|
+
return @valid
|
68
|
+
end
|
69
|
+
|
70
|
+
def run
|
71
|
+
# Config file settings will override command line settings
|
72
|
+
settings = { :host => @address, :port => @port, :cwd => @cwd,
|
73
|
+
:log_file => @log_file, :pid_file => @pid_file, :environment => @environment,
|
74
|
+
:docroot => @docroot, :mime_map => @mime_map, :daemon => @daemon,
|
75
|
+
:debug => @debug, :includes => ["mongrel"], :config_script => @config_script,
|
76
|
+
:num_processors => @num_procs, :timeout => @timeout,
|
77
|
+
:user => @user, :group => @group, :prefix => @prefix, :config_file => @config_file
|
78
|
+
}
|
79
|
+
|
80
|
+
if @generate
|
81
|
+
STDERR.puts "** Writing config to \"#@generate\"."
|
82
|
+
open(@generate, "w") {|f| f.write(settings.to_yaml) }
|
83
|
+
STDERR.puts "** Finished. Run \"mongrel_rails -C #@generate\" to use the config file."
|
84
|
+
exit 0
|
85
|
+
end
|
86
|
+
|
87
|
+
if @config_file
|
88
|
+
settings.merge! YAML.load_file(@config_file)
|
89
|
+
STDERR.puts "** Loading settings from #{@config_file} (they override command line)." unless settings[:daemon]
|
90
|
+
end
|
91
|
+
|
92
|
+
config = Mongrel::Rails::RailsConfigurator.new(settings) do
|
93
|
+
if defaults[:daemon]
|
94
|
+
if File.exist? defaults[:pid_file]
|
95
|
+
log "!!! PID file #{defaults[:pid_file]} already exists. Mongrel could be running already. Check your #{defaults[:log_file]} for errors."
|
96
|
+
log "!!! Exiting with error. You must stop mongrel and clear the .pid before I'll attempt a start."
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
|
100
|
+
daemonize
|
101
|
+
log "Daemonized, any open files are closed. Look at #{defaults[:pid_file]} and #{defaults[:log_file]} for info."
|
102
|
+
log "Settings loaded from #{@config_file} (they override command line)." if @config_file
|
103
|
+
end
|
104
|
+
|
105
|
+
log "Starting Mongrel listening at #{defaults[:host]}:#{defaults[:port]}"
|
106
|
+
|
107
|
+
listener do
|
108
|
+
mime = {}
|
109
|
+
if defaults[:mime_map]
|
110
|
+
log "Loading additional MIME types from #{defaults[:mime_map]}"
|
111
|
+
mime = load_mime_map(defaults[:mime_map], mime)
|
112
|
+
end
|
113
|
+
|
114
|
+
if defaults[:debug]
|
115
|
+
log "Installing debugging prefixed filters. Look in log/mongrel_debug for the files."
|
116
|
+
debug "/"
|
117
|
+
end
|
118
|
+
|
119
|
+
log "Starting Rails with #{defaults[:environment]} environment..."
|
120
|
+
log "Mounting Rails at #{defaults[:prefix]}..." if defaults[:prefix]
|
121
|
+
uri defaults[:prefix] || "/", :handler => rails(:mime => mime, :prefix => defaults[:prefix])
|
122
|
+
log "Rails loaded."
|
123
|
+
|
124
|
+
log "Loading any Rails specific GemPlugins"
|
125
|
+
load_plugins
|
126
|
+
|
127
|
+
if defaults[:config_script]
|
128
|
+
log "Loading #{defaults[:config_script]} external config script"
|
129
|
+
run_config(defaults[:config_script])
|
130
|
+
end
|
131
|
+
|
132
|
+
setup_rails_signals
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
config.run
|
137
|
+
config.log "Mongrel available at #{settings[:host]}:#{settings[:port]}"
|
138
|
+
|
139
|
+
if config.defaults[:daemon]
|
140
|
+
config.write_pid_file
|
141
|
+
else
|
142
|
+
config.log "Use CTRL-C to stop."
|
143
|
+
end
|
144
|
+
|
145
|
+
config.join
|
146
|
+
|
147
|
+
if config.needs_restart
|
148
|
+
if RUBY_PLATFORM !~ /mswin/
|
149
|
+
cmd = "ruby #{__FILE__} start #{original_args.join(' ')}"
|
150
|
+
config.log "Restarting with arguments: #{cmd}"
|
151
|
+
config.stop
|
152
|
+
config.remove_pid_file
|
153
|
+
|
154
|
+
if config.defaults[:daemon]
|
155
|
+
system cmd
|
156
|
+
else
|
157
|
+
STDERR.puts "Can't restart unless in daemon mode."
|
158
|
+
exit 1
|
159
|
+
end
|
160
|
+
else
|
161
|
+
config.log "Win32 does not support restarts. Exiting."
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def Mongrel::send_signal(signal, pid_file)
|
168
|
+
pid = open(pid_file).read.to_i
|
169
|
+
print "Sending #{signal} to Mongrel at PID #{pid}..."
|
170
|
+
begin
|
171
|
+
Process.kill(signal, pid)
|
172
|
+
rescue Errno::ESRCH
|
173
|
+
puts "Process does not exist. Not running."
|
174
|
+
end
|
175
|
+
|
176
|
+
puts "Done."
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
class Stop < GemPlugin::Plugin "/commands"
|
181
|
+
include Mongrel::Command::Base
|
182
|
+
|
183
|
+
def configure
|
184
|
+
options [
|
185
|
+
['-c', '--chdir PATH', "Change to dir before starting (will be expanded).", :@cwd, "."],
|
186
|
+
['-f', '--force', "Force the shutdown (kill -9).", :@force, false],
|
187
|
+
['-w', '--wait SECONDS', "Wait SECONDS before forcing shutdown", :@wait, "0"],
|
188
|
+
['-P', '--pid FILE', "Where the PID file is located.", :@pid_file, "log/mongrel.pid"]
|
189
|
+
]
|
190
|
+
end
|
191
|
+
|
192
|
+
def validate
|
193
|
+
@cwd = File.expand_path(@cwd)
|
194
|
+
valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
|
195
|
+
|
196
|
+
Dir.chdir @cwd
|
197
|
+
|
198
|
+
valid_exists? @pid_file, "PID file #@pid_file does not exist. Not running?"
|
199
|
+
return @valid
|
200
|
+
end
|
201
|
+
|
202
|
+
def run
|
203
|
+
if @force
|
204
|
+
@wait.to_i.times do |waiting|
|
205
|
+
exit(0) if not File.exist? @pid_file
|
206
|
+
sleep 1
|
207
|
+
end
|
208
|
+
|
209
|
+
Mongrel::send_signal("KILL", @pid_file) if File.exist? @pid_file
|
210
|
+
else
|
211
|
+
Mongrel::send_signal("TERM", @pid_file)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
class Restart < GemPlugin::Plugin "/commands"
|
218
|
+
include Mongrel::Command::Base
|
219
|
+
|
220
|
+
def configure
|
221
|
+
options [
|
222
|
+
['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, '.'],
|
223
|
+
['-s', '--soft', "Do a soft restart rather than a process exit restart", :@soft, false],
|
224
|
+
['-P', '--pid FILE', "Where the PID file is located", :@pid_file, "log/mongrel.pid"]
|
225
|
+
]
|
226
|
+
end
|
227
|
+
|
228
|
+
def validate
|
229
|
+
@cwd = File.expand_path(@cwd)
|
230
|
+
valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
|
231
|
+
|
232
|
+
Dir.chdir @cwd
|
233
|
+
|
234
|
+
valid_exists? @pid_file, "PID file #@pid_file does not exist. Not running?"
|
235
|
+
return @valid
|
236
|
+
end
|
237
|
+
|
238
|
+
def run
|
239
|
+
if @soft
|
240
|
+
Mongrel::send_signal("HUP", @pid_file)
|
241
|
+
else
|
242
|
+
Mongrel::send_signal("USR2", @pid_file)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE
|
250
|
+
|
251
|
+
|
252
|
+
if not Mongrel::Command::Registry.instance.run ARGV
|
253
|
+
exit 1
|
254
|
+
end
|
data/bin/swiftiply
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
#!ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
begin
|
7
|
+
load_attempted ||= false
|
8
|
+
require 'swiftcore/Swiftiply'
|
9
|
+
rescue LoadError => e
|
10
|
+
unless load_attempted
|
11
|
+
load_attempted = true
|
12
|
+
require 'rubygems'
|
13
|
+
retry
|
14
|
+
end
|
15
|
+
raise e
|
16
|
+
end
|
17
|
+
|
18
|
+
module Swiftcore
|
19
|
+
class SwiftiplyExec
|
20
|
+
Ccluster_address = 'cluster_address'.freeze
|
21
|
+
Ccluster_port = 'cluster_port'.freeze
|
22
|
+
Cbackend_address = 'backend_address'.freeze
|
23
|
+
Cbackend_port = 'backend_port'.freeze
|
24
|
+
Cmap = 'map'.freeze
|
25
|
+
Cincoming = 'incoming'.freeze
|
26
|
+
Coutgoing = 'outgoing'.freeze
|
27
|
+
Ckeepalive = 'keepalive'.freeze
|
28
|
+
Cdaemonize = 'daemonize'.freeze
|
29
|
+
Curl = 'url'.freeze
|
30
|
+
Chost = 'host'.freeze
|
31
|
+
Cport = 'port'.freeze
|
32
|
+
Ctimeout = 'timeout'.freeze
|
33
|
+
|
34
|
+
#####
|
35
|
+
#
|
36
|
+
# --cluster-address
|
37
|
+
# --cluster-port
|
38
|
+
# --config-file -c (default swiftiply.cnf)
|
39
|
+
# --daemonize -d
|
40
|
+
#
|
41
|
+
# Config file format (YAML):
|
42
|
+
#
|
43
|
+
# cluster_address:
|
44
|
+
# cluster_port:
|
45
|
+
# daemonize:
|
46
|
+
# map:
|
47
|
+
# - incoming:
|
48
|
+
# - 127.0.0.1:8080
|
49
|
+
# - foo.bar.com:8090
|
50
|
+
# url:
|
51
|
+
# keepalive:
|
52
|
+
# outgoing:
|
53
|
+
#
|
54
|
+
#####
|
55
|
+
def self.parse_options(config = {})
|
56
|
+
OptionParser.new do |opts|
|
57
|
+
opts.banner = 'Usage: swiftiply.rb [options]'
|
58
|
+
opts.separator ''
|
59
|
+
opts.on('-c','--config CONFFILE',"The configuration file to read.") do |conf|
|
60
|
+
config = YAML.load(File.open(conf))
|
61
|
+
postprocess_config_load(config)
|
62
|
+
end
|
63
|
+
opts.on('--cluster-address [ADDRESS]',String,'The hostname/IP address that swiftiply will listen for connections on.') do |address|
|
64
|
+
config[Ccluster_address] = address
|
65
|
+
end
|
66
|
+
opts.on('--cluster-port [PORT]',Integer,'The port that swiftiply will listen for connections on.') do |port|
|
67
|
+
config[Ccluster_port] = port
|
68
|
+
end
|
69
|
+
opts.on('--backend-address [ADDRESS]',String,'The hostname/IP address that swiftiply will listen for backend connections on.') do |address|
|
70
|
+
config[Cbackend_address] = address
|
71
|
+
end
|
72
|
+
opts.on('--backend-port [PORT]',Integer,'The port that swiftiply will listen for backend connections on.') do |port|
|
73
|
+
config[Cbackend_port] = port
|
74
|
+
end
|
75
|
+
opts.on('-d','--daemonize [YN]',[:y,:yes,:n,:no],'Whether swiftiply should put itself into the background.') do |yn|
|
76
|
+
config[Cdaemonize] = yn.to_s =~ /^y/i
|
77
|
+
end
|
78
|
+
opts.on('-t','--timeout [SECONDS]',Integer,'The server unavailable timeout. Defaults to 3 seconds.') do |timeout|
|
79
|
+
config[Ctimeout] = timeout
|
80
|
+
end
|
81
|
+
opts.on('-p','--print-config','Print the full configuration.') do
|
82
|
+
verify_config(config)
|
83
|
+
require 'pp'
|
84
|
+
pp config
|
85
|
+
exit
|
86
|
+
end
|
87
|
+
|
88
|
+
end.parse!
|
89
|
+
puts("Configuration failed validation; exiting.") && exit unless verify_config(config)
|
90
|
+
config
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.daemonize
|
94
|
+
if (child_pid = fork)
|
95
|
+
puts "PID #{child_pid}"
|
96
|
+
exit!
|
97
|
+
end
|
98
|
+
|
99
|
+
Process.setsid
|
100
|
+
|
101
|
+
rescue Exception
|
102
|
+
puts "Platform (#{RUBY_PLATFORM}) does not appear to support fork/setsid; skipping"
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.postprocess_config_load(config)
|
106
|
+
config[Cmap] = [] unless Array === config[Cmap]
|
107
|
+
config[Ctimeout] ||= 3
|
108
|
+
config[Cmap].each do |m|
|
109
|
+
m[Ckeepalive] = true unless m.has_key?(Ckeepalive)
|
110
|
+
m[Ckeepalive] = !!m[Ckeepalive]
|
111
|
+
m[Coutgoing] = [m[Coutgoing]] unless Array === m[Coutgoing]
|
112
|
+
m[Cincoming] = [m[Cincoming]] unless Array === m[Cincoming]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.verify_config(config)
|
117
|
+
if config[Cbackend_address] and config[Cbackend_port]
|
118
|
+
config[Cmap] << {Cincoming => nil, Curl => nil, Coutgoing => "#{config[Cbackend_address]}:#{config[Cbackend_port]}", Ckeepalive => true}
|
119
|
+
end
|
120
|
+
true
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.run
|
124
|
+
config = parse_options
|
125
|
+
daemonize if config[Cdaemonize]
|
126
|
+
Swiftcore::Swiftiply.run(config)
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.on_windows?
|
130
|
+
return @on_windows unless @on_windows.nil?
|
131
|
+
@on_windows = !![/mswin/i, /cygwin/i, /mingw/i, /bccwin/i, /wince/i].find {|p| RUBY_PLATFORM =~ p}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
Swiftcore::SwiftiplyExec.run
|