serverengine 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,14 @@
1
+ Copyright (C) 2011 FURUHASHI Sadayuki
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
data/Changelog ADDED
@@ -0,0 +1,5 @@
1
+
2
+ 2013-06-01 version 1.5.0:
3
+
4
+ * First release
5
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,332 @@
1
+ # ServerEngine
2
+
3
+ ServerEngine is a framework to implement robust multiprocess servers like Unicorn.
4
+
5
+ **Main features:**
6
+
7
+ ```
8
+ Heartbeat via pipe
9
+ & auto-restart
10
+ / \ ---+
11
+ +------------+ / +----------+ \ +--------+ |
12
+ | Supervisor |------| Server |------| Worker | |
13
+ +------------+ +----------+\ +--------+ | Multi-process
14
+ / \ | or multi-thread
15
+ / \ +--------+ |
16
+ Dynamic reconfiguration | Worker | |
17
+ and live restart support +--------+ |
18
+ ---+
19
+ ```
20
+
21
+ **Other features:**
22
+
23
+ - logging and log rotation
24
+ - signal handlers
25
+ - stacktrace and heap dump on signal
26
+ - chuser, chgroup and chumask
27
+ - changing process names
28
+
29
+
30
+ ## API
31
+
32
+ ### Simplest server
33
+
34
+ What you need to implement at least is a worker module which has `run` and `stop` methods.
35
+
36
+ ```ruby
37
+ require 'serverengine'
38
+
39
+ module MyWorker
40
+ def run
41
+ until @stop
42
+ puts "Awesome work!"
43
+ sleep 1
44
+ end
45
+ end
46
+
47
+ def stop
48
+ @stop = true
49
+ end
50
+ end
51
+
52
+ se = ServerEngine.create(nil, MyWorker, {
53
+ :daemonize => true,
54
+ :pid_path => 'myserver.pid'
55
+ })
56
+ se.run
57
+ ```
58
+
59
+ Send `TERM` signal to kill the daemon. See also **Signals** section bellow.
60
+
61
+
62
+ ### Multiprocess server
63
+
64
+ Simply set **process** or **thread** to `worker_type` parameter and number of workers to `workers` parameter.
65
+
66
+ ```ruby
67
+ se = ServerEngine.create(nil, MyWorker, {
68
+ :daemonize => true,
69
+ :pid_path => 'myserver.pid',
70
+ :workers => 4,
71
+ :worker_type => 'process',
72
+ })
73
+ se.run
74
+ ```
75
+
76
+ See also **Worker types** section bellow.
77
+
78
+ #### Multiprocess TCP server
79
+
80
+ One of typical implementation styles of TCP servers is that a parent process listens socket and child processes accept connections from clients.
81
+
82
+ You can optionally implement a server module to control the parent process.
83
+
84
+ ```ruby
85
+ module MyServer
86
+ def before_run
87
+ @sock = TCPServer.new(config[:bind], config[:port])
88
+ end
89
+
90
+ attr_reader :sock
91
+ end
92
+
93
+ module MyWorker
94
+ def run
95
+ until @stop
96
+ # you should use Cool.io or EventMachine actually
97
+ c = server.sock.accept
98
+ c.write "Awesome work!"
99
+ c.close
100
+ end
101
+ end
102
+
103
+ def stop
104
+ @stop = true
105
+ end
106
+ end
107
+
108
+ se = ServerEngine.create(MyServer, MyWorker, {
109
+ :daemonize => true,
110
+ :pid_path => 'myserver.pid',
111
+ :workers => 4,
112
+ :worker_type => 'process',
113
+ :bind => '0.0.0.0',
114
+ :port => 9071,
115
+ })
116
+ se.run
117
+ ```
118
+
119
+
120
+ ### Logging
121
+
122
+ ServerEngine logger rotates logs by 1MB and keeps 5 generations by default.
123
+
124
+ ```ruby
125
+ se = ServerEngine.create(MyServer, MyWorker, {
126
+ :log => 'myserver.log',
127
+ :log_level => 'debug',
128
+ :log_rotate_age => 5,
129
+ :log_rotate_size => 1*1024*1024,
130
+ })
131
+ se.run
132
+ ```
133
+
134
+ See also **Configuration** section bellow.
135
+
136
+
137
+ ### Enabling supervisor process
138
+
139
+ Server programs running 24x7 hours need to survive even if a process stalled because of unexpected memory swapping or network errors.
140
+
141
+ Supervisor automatically reboots the server process if heartbeat breaks out.
142
+
143
+ ```ruby
144
+ se = ServerEngine.create(nil, MyWorker, {
145
+ :daemonize => true,
146
+ :pid_path => 'myserver.pid',
147
+ :supervisor => true, # enable supervisor process
148
+ })
149
+ se.run
150
+ ```
151
+
152
+ ### Live restart
153
+
154
+ You can restart a server process without waiting for completion of shutdown process (if `supervisor` and `enable_detach` parameters are enabled).
155
+ This feature is useful to minimize downtime where workers take long time to complete tasks.
156
+
157
+ ```
158
+ # 1. start server
159
+ +------------+ +----------+ +-----------+
160
+ | Supervisor |----| Server |----| Worker(s) |
161
+ +------------+ +----------+ +-----------+
162
+
163
+ # 2. detach (SIGINT) and waits for completion for several seconds
164
+ +------------+ +----------+ +-----------+
165
+ | Supervisor | | Server |----| Worker(s) |
166
+ +------------+ +----------+ +-----------+
167
+
168
+ # 3. start new server if the server doesn't exit in a short time
169
+ +------------+ +----------+ +-----------+
170
+ | Supervisor |\ | Server |----| Worker(s) |
171
+ +------------+ | +----------+ +-----------+
172
+ |
173
+ | +----------+ +-----------+
174
+ \--| Server |----| Worker(s) |
175
+ +----------+ +-----------+
176
+
177
+ # 4. old server exits
178
+ +------------+
179
+ | Supervisor |\
180
+ +------------+ |
181
+ |
182
+ | +----------+ +-----------+
183
+ \--| Server |----| Worker(s) |
184
+ +----------+ +-----------+
185
+ ```
186
+
187
+ Note that network servers (which listen sockets) shouldn't use live restart because it causes "Address already in use" error. Instead, simply use `worker_type=process` configuration and send `USR1` to restart only workers. USR1 signal doesn't restart server (by default. See also `restart_server_process` parameter). Restarting workers don't wait for completion of all running workers.
188
+
189
+
190
+ ### Dynamic configuration reloading
191
+
192
+ Robust servers should not restart only to update configuration parameters.
193
+
194
+ ```ruby
195
+ module MyWorker
196
+ def initialize
197
+ reload
198
+ end
199
+
200
+ def reload
201
+ @message = config[:message] || "Awesome work!"
202
+ @sleep = config[:sleep] || 1
203
+ end
204
+
205
+ def run
206
+ until @stop
207
+ puts @message
208
+ sleep @sleep
209
+ end
210
+ end
211
+
212
+ def stop
213
+ @stop = true
214
+ end
215
+ end
216
+
217
+ se = ServerEngine.create(nil, MyWorker) do
218
+ YAML.load_file("config.yml").merge({
219
+ :daemonize => true,
220
+ :worker_type => 'process',
221
+ })
222
+ end
223
+ se.run
224
+ ```
225
+
226
+ Send `USR2` signal to reload configuration file.
227
+
228
+
229
+ ## Utilities
230
+
231
+ ### BlockingFlag
232
+
233
+ `ServerEngine::BlockingFlag` is recommended to stop workers because `stop` methods is called by a different thread from the `run` thread.
234
+
235
+ ```ruby
236
+ module MyWorker
237
+ def initialize
238
+ @stop_flag = ServerEngine::BlockingFlag.new
239
+ end
240
+
241
+ def run
242
+ until @stop_flag.wait_for_set(1.0) # or @stop_flag.set?
243
+ puts @message
244
+ end
245
+ end
246
+
247
+ def stop
248
+ @stop_flag.set!
249
+ end
250
+ end
251
+
252
+ se = ServerEngine.create(nil, MyWorker) do
253
+ YAML.load_file(config).merge({
254
+ :daemonize => true,
255
+ :worker_type => 'process'
256
+ })
257
+ end
258
+ se.run
259
+ ```
260
+
261
+
262
+ ## Worker types
263
+
264
+ ServerEngine supports 3 worker types:
265
+
266
+ - **embedded**: uses a thread to run worker module (default). This type doesn't support immediate shutdown and immediate restart.
267
+ - **thread**: uses threads to run worker modules. This type doesn't support immediate shutdown and immediate restart.
268
+ - **process**: uses processes to run worker modules. This type doesn't work on Win32 platform.
269
+
270
+
271
+ ## Signals
272
+
273
+ - **TERM:** graceful shutdown
274
+ - **QUIT:** immediate shutdown (available only when `worker_type` is "process")
275
+ - **USR1:** graceful restart
276
+ - **HUP:** immediate restart (available only when `worker_type` is "process")
277
+ - **USR2:** reload config file and reopen log file
278
+ - **INT:** detach process for live restarting (available only when `supervisor` and `enable_detach` parameters are true. otherwise graceful shutdown)
279
+ - **CONT:** dump stacktrace and memory information to /tmp/sigdump-<pid>.log file
280
+
281
+ Immediate shutdown and restart send SIGQUIT signal to worker processes which killes the processes.
282
+ Graceful shutdown and restart call `Worker#stop` method and wait for completion of `Worker#run` method.
283
+
284
+
285
+ ## Configuration
286
+
287
+ - Daemon
288
+ - **daemonize** enables daemonize (default: false) (not dynamic reloadable)
289
+ - **pid_path** sets the path to pid file (default: don't create pid file) (not dynamic reloadable)
290
+ - **supervisor** enables supervisor if it's true (default: false) (not dynamic reloadable)
291
+ - **daemon_process_name** changes process name ($0) of server or supervisor process (not dynamic reloadable)
292
+ - **chuser** changes execution user (not dynamic reloadable)
293
+ - **chgroup** changes execution group (not dynamic reloadable)
294
+ - **chumask** changes umask (not dynamic reloadable)
295
+ - Supervisor: available only when `supervisor` parameters is true
296
+ - **server_process_name** changes process name ($0) of server process (not dynamic reloadable)
297
+ - **restart_server_process** restarts server process when it receives USR1 or HUP signal. (default: false) (not dynamic reloadable)
298
+ - **enable_detach** enables INT signal (default: true) (not dynamic reloadable)
299
+ - **exit_on_detach** exits supervisor after detaching server process instead of restarting it (default: false) (not dynamic reloadable)
300
+ - **disable_reload** disables USR2 signal (default: false) (not dynamic reloadable)
301
+ - **serverrestart_wait** sets wait time before restarting server after last restarting (default: 1.0)
302
+ - **server_detach_wait** sets wait time before starting live restart (default: 10.0)
303
+ - Multithread server and multiprocess server: available only when `worker_type` is thread or process
304
+ - **workers** sets number of workers (default: 1)
305
+ - **start_worker_delay** sets wait time before starting a new worker (default: 0)
306
+ - **start_worker_delay_rand** randomizes start_worker_delay at this ratio (default: 0.2)
307
+ - Multiprocess server: available only when `worker_type` is "process"
308
+ - **worker_heartbeat_interval**
309
+ - **worker_heartbeat_timeout**
310
+ - **worker_graceful_kill_interval**
311
+ - **worker_graceful_kill_interval_increment**
312
+ - **worker_graceful_kill_timeout**
313
+ - **worker_immediate_kill_interval**
314
+ - **worker_immediate_kill_interval_increment**
315
+ - **worker_immediate_kill_timeout**
316
+ - Logger
317
+ - **log** sets path to log file. Set "-" for STDOUT (default: STDERR)
318
+ - **log_level** log level: debug, info, warn, error or fatal. (default: debug)
319
+ - **log_rotate_age** generations to keep rotated log files (default: 5) (not dynamic reloadable)
320
+ - **log_rotate_size** sets the size to rotate log files (default: 1048576) (not dynamic reloadable)
321
+ - **log_stdout** hooks STDOUT to log file (default: true) (not dynamic reloadable)
322
+ - **log_stdout** hooks STDERR to log file (default: true) (not dynamic reloadable)
323
+ - **logger_class** class of the logger instance (default: ServerEngine::DaemonLogger)
324
+
325
+ ---
326
+
327
+ ```
328
+ Author: Sadayuki Furuhashi
329
+ Copyright: Copyright (c) 2012-2013 FURUHASHI Sadayuki
330
+ License: Apache License, Version 2.0
331
+ ```
332
+
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+ require 'rake/clean'
6
+
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new(:spec) do |t|
10
+ t.fail_on_error = false
11
+ t.rspec_opts = %w[-rspec_helper]
12
+ end
13
+
14
+ task :default => [:spec, :build]
@@ -0,0 +1,52 @@
1
+ #
2
+ # ServerEngine
3
+ #
4
+ # Copyright (C) 2012-2013 FURUHASHI Sadayuki
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module ServerEngine
19
+
20
+ require 'sigdump'
21
+
22
+ here = File.expand_path(File.dirname(__FILE__))
23
+
24
+ {
25
+ :BlockingFlag => 'serverengine/blocking_flag',
26
+ :SignalThread => 'serverengine/signal_thread',
27
+ :DaemonLogger => 'serverengine/daemon_logger',
28
+ :ConfigLoader => 'serverengine/config_loader',
29
+ :Daemon => 'serverengine/daemon',
30
+ :Supervisor => 'serverengine/supervisor',
31
+ :Server => 'serverengine/server',
32
+ :EmbeddedServer => 'serverengine/embedded_server',
33
+ :MultiWorkerServer => 'serverengine/multi_worker_server',
34
+ :MultiProcessServer => 'serverengine/multi_process_server',
35
+ :MultiThreadServer => 'serverengine/multi_thread_server',
36
+ :ProcessManager => 'serverengine/process_manager',
37
+ :Worker => 'serverengine/worker',
38
+ :VERSION => 'serverengine/version',
39
+ }.each_pair {|k,v|
40
+ autoload k, File.expand_path(v, File.dirname(__FILE__))
41
+ }
42
+
43
+ [
44
+ 'serverengine/utils',
45
+ ].each {|v|
46
+ require File.join(here, v)
47
+ }
48
+
49
+ def self.create(server_module, worker_module, &config_load_proc)
50
+ Daemon.new(server_module, worker_module, &config_load_proc)
51
+ end
52
+ end
@@ -0,0 +1,78 @@
1
+ #
2
+ # ServerEngine
3
+ #
4
+ # Copyright (C) 2012-2013 FURUHASHI Sadayuki
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module ServerEngine
19
+
20
+ require 'thread'
21
+
22
+ class BlockingFlag
23
+ def initialize
24
+ @set = false
25
+ @mutex = Mutex.new
26
+ @cond = ConditionVariable.new
27
+ end
28
+
29
+ def set!
30
+ toggled = false
31
+ @mutex.synchronize do
32
+ unless @set
33
+ @set = true
34
+ toggled = true
35
+ end
36
+ @cond.broadcast
37
+ end
38
+ return toggled
39
+ end
40
+
41
+ def reset!
42
+ toggled = false
43
+ @mutex.synchronize do
44
+ if @set
45
+ @set = false
46
+ toggled = true
47
+ end
48
+ @cond.broadcast
49
+ end
50
+ return toggled
51
+ end
52
+
53
+ def set?
54
+ @set
55
+ end
56
+
57
+ def wait_for_set(timeout=nil)
58
+ @mutex.synchronize do
59
+ unless @set
60
+ @cond.wait(@mutex, timeout)
61
+ end
62
+ return @set
63
+ end
64
+ end
65
+
66
+ alias_method :wait, :wait_for_set
67
+
68
+ def wait_for_reset(timeout=nil)
69
+ @mutex.synchronize do
70
+ if @set
71
+ @cond.wait(@mutex, timeout)
72
+ end
73
+ return !@set
74
+ end
75
+ end
76
+ end
77
+
78
+ end