serverengine 1.5.11 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9d0f894b59ef81b2998f4d6ab09a3c84f8c4e7c1
4
- data.tar.gz: 56a730fe75ba58f16c783c61834342f6074e9cbc
3
+ metadata.gz: 104467c419789df193725e70a8d3de43e6351c73
4
+ data.tar.gz: 0d10ddbca870bb40408d8bf56e9b2d52c024636d
5
5
  SHA512:
6
- metadata.gz: cd028d040dd71a518538363ccaa641ce96e9a83a800067ae268e34a48aad7327dbc19301294b89d6d9d0e9fb17acf3b905aaa5d3c0c41c6d3d89a959accc3515
7
- data.tar.gz: 44e2e9ee06c2168d7758507385f4e2ad04caf91248814f8297e05be53ac4c3c710a64390a83a89379422de33190435925f94aa7ac8fe355700107ba26fcac4a1
6
+ metadata.gz: 8d33c621c6bc34a2029f100bfd61cf1a5f56c5bf81446702e1e28e2e814515ca312e5267816384b4e000da7a48a2977178297275018847fa4ef356cf650fe361
7
+ data.tar.gz: 334a1f7311e02f4d37b660ff5cfe832d17fd2f755120c04c509f2997b77186b28581614c5ba13193b0affb1afe7a0da6b9cd4bde2dde4984e8d6daf1839eb94c
data/.travis.yml CHANGED
@@ -1,9 +1,6 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - 1.9.3
5
- - 2.0
6
- - 2.1
7
4
  - 2.2
8
5
 
9
6
  branches:
@@ -11,3 +8,6 @@ branches:
11
8
  - master
12
9
 
13
10
  script: bundle exec rake spec
11
+
12
+ before_install:
13
+ - gem update bundler
data/Changelog CHANGED
@@ -1,3 +1,12 @@
1
+ 2015-01-07 version 1.6.0:
2
+
3
+ * Added SocketManager, a utility class for multiprocess servers to listen on
4
+ the same TCP or UDP port dynamically.
5
+ * Added a new attr_reader accessor at Daemon#server and Supervisor#server
6
+ * Added ServerEngine.windows? method to check Windows platform
7
+ * ProcessManager now considers Windows platform
8
+
9
+
1
10
  2015-09-28 version 1.5.11:
2
11
 
3
12
  * Fix unexpected logger option handling [#22]
data/README.md CHANGED
@@ -18,16 +18,27 @@ ServerEngine is a framework to implement robust multiprocess servers like Unicor
18
18
  ---+
19
19
  ```
20
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
21
+ ServerEngine also provides useful options and utilities such as logging, signal handlers, changing process names shown by `ps` command, chuser, stacktrace and heap dump on signal.
22
+
23
+ * [Examples](#examples)
24
+ * [Simplest server](#simplest-server)
25
+ * [Multiprocess server](#multiprocess-server)
26
+ * [Multiprocess TCP server](#multiprocess-tcp-server)
27
+ * [Multiprocess server on Windows and JRuby platforms](#multiprocess-server-on-windows-and-jruby-platforms)
28
+ * [Logging](#logging)
29
+ * [Supervisor auto restart](#supervisor-auto-restart)
30
+ * [Live restart](#live-restart)
31
+ * [Dynamic config reloading](#dynamic-config-reloading)
32
+ * [Signals](#signals)
33
+ * [Utilities](#utilities)
34
+ * [BlockingFlag](#blockingflag)
35
+ * [SocketManager](#socketmanager)
36
+ * [Module API](#module-api)
37
+ * [Worker module](#worker-module)
38
+ * [Server module](#server-module)
39
+ * [List of all configurations](#list-of-all-configurations)
40
+
41
+ ## Examples
31
42
 
32
43
  ### Simplest server
33
44
 
@@ -62,7 +73,7 @@ Send `TERM` signal to kill the daemon. See also **Signals** section bellow for d
62
73
 
63
74
  ### Multiprocess server
64
75
 
65
- Simply set **worker_type=process** or **worker_type=thread** parameter, and set number of workers to `workers` parameter.
76
+ Simply set **worker_type: "process"** or **worker_type: "thread"** parameter, and set number of workers to `workers` parameter.
66
77
 
67
78
  ```ruby
68
79
  se = ServerEngine.create(nil, MyWorker, {
@@ -123,12 +134,12 @@ se.run
123
134
  ```
124
135
 
125
136
 
126
- ### Multiprocess server on Windows and JRuby platform
137
+ ### Multiprocess server on Windows and JRuby platforms
127
138
 
128
- Above **worker_type=process** depends on `fork` system call, which doesn't work on Windows or JRuby platform.
129
- ServerEngine provides **worker_type=spawn** for those platforms (This is still EXPERIMENTAL). However, unfortunately, you need to implement different worker module because `worker_type=spawn` is not compatible with **worker_type=process** in terms of API.
139
+ Above **worker_type: "process"** depends on `fork` system call, which doesn't work on Windows or JRuby platform.
140
+ ServerEngine provides **worker_type: "spawn"** for those platforms (This is still EXPERIMENTAL). However, unfortunately, you need to implement different worker module because `worker_type: "spawn"` is not compatible with **worker_type: "process"** in terms of API.
130
141
 
131
- What you need to implement at least to use worker_type=spawn is `spawn(process_manager)` method. You will call `process_manager.spawn` at the method, where `spawn` is same with `Process.spawn` excepting return value.
142
+ What you need to implement at least to use worker_type: "spawn" is `def spawn(process_manager)` method. You will call `process_manager.spawn` at the method, where `spawn` is same with `Process.spawn` excepting return value.
132
143
 
133
144
  ```ruby
134
145
  module MyWorker
@@ -164,7 +175,7 @@ se.run
164
175
  ```
165
176
 
166
177
 
167
- ### Logging
178
+ ## Logging
168
179
 
169
180
  ServerEngine logger rotates logs by 1MB and keeps 5 generations by default.
170
181
 
@@ -187,25 +198,25 @@ ServerEngine's default logger extends from Ruby's standard Logger library to:
187
198
  See also **Configuration** section bellow.
188
199
 
189
200
 
190
- ### Enabling supervisor process
201
+ ## Supervisor auto restart
191
202
 
192
203
  Server programs running 24x7 hours need to survive even if a process stalled because of unexpected memory swapping or network errors.
193
204
 
194
- Supervisor process runs as the parent process of the server process and monitor it to restart automatically.
205
+ Supervisor process runs as the parent process of the server process and monitor it to restart automatically. You can enable supervisor process by setting `supervisor: true` parameter:
195
206
 
196
207
  ```ruby
197
208
  se = ServerEngine.create(nil, MyWorker, {
198
209
  daemonize: true,
199
210
  pid_path: 'myserver.pid',
200
- supervisor: true, # enable supervisor process
211
+ supervisor: true, # enables supervisor process
201
212
  })
202
213
  se.run
203
214
  ```
204
215
 
205
216
 
206
- ### Live restart
217
+ ## Live restart
207
218
 
208
- You can restart a server process without waiting for completion of all workers using `INT` signal (`supervisor=true` and `enable_detach=true` parameters must be enabled).
219
+ You can restart a server process without waiting for completion of all workers using `INT` signal (`supervisor: true` and `enable_detach: true` parameters must be enabled).
209
220
  This feature allows you to minimize downtime where workers take long time to complete a task.
210
221
 
211
222
  ```
@@ -238,10 +249,10 @@ This feature allows you to minimize downtime where workers take long time to com
238
249
  +----------+ +-----------+
239
250
  ```
240
251
 
241
- Note that network servers (which listen sockets) shouldn't use live restart because it causes "Address already in use" error at the server process. Instead, simply use `worker_type=process` configuration and send `USR1` to restart workers instead of the server. It restarts a worker without waiting for shutdown of the other workers. This way doesn't cause downtime because server process doesn't close listening sockets and keeps accepting new clients (See also `restart_server_process` parameter if necessary).
252
+ Note that network servers (which listen sockets) shouldn't use live restart because it causes "Address already in use" error at the server process. Instead, simply use `worker_type: "process"` configuration and send `USR1` to restart workers instead of the server. It restarts a worker without waiting for shutdown of the other workers. This way doesn't cause downtime because server process doesn't close listening sockets and keeps accepting new clients (See also `restart_server_process` parameter if necessary).
242
253
 
243
254
 
244
- ### Dynamic configuration reloading
255
+ ## Dynamic config reloading
245
256
 
246
257
  Robust servers should not restart only to update configuration parameters.
247
258
 
@@ -280,6 +291,20 @@ se.run
280
291
  Send `USR2` signal to reload configuration file.
281
292
 
282
293
 
294
+ ## Signals
295
+
296
+ - **TERM:** graceful shutdown
297
+ - **QUIT:** immediate shutdown (available only when `worker_type` is "process")
298
+ - **USR1:** graceful restart
299
+ - **HUP:** immediate restart (available only when `worker_type` is "process")
300
+ - **USR2:** reload config file and reopen log file
301
+ - **INT:** detach process for live restarting (available only when `supervisor` and `enable_detach` parameters are true. otherwise graceful shutdown)
302
+ - **CONT:** dump stacktrace and memory information to /tmp/sigdump-<pid>.log file
303
+
304
+ Immediate shutdown and restart send SIGQUIT signal to worker processes which kills the processes.
305
+ Graceful shutdown and restart call `Worker#stop` method and wait for completion of `Worker#run` method.
306
+
307
+
283
308
  ## Utilities
284
309
 
285
310
  ### BlockingFlag
@@ -313,11 +338,67 @@ se.run
313
338
  ```
314
339
 
315
340
 
316
- ## Module methods
341
+ ### SocketManager
317
342
 
318
- ### Worker module
343
+ `ServerEngine::SocketManager` is a powerful library to listen on the same port across multiple worker processes dynamically.
344
+
345
+ ```ruby
346
+ module MyServer
347
+ def before_run
348
+ @socket_manager_path = ServerEngine::SocketManager.generate_path
349
+ @socket_manager_server = ServerEngine::SocketManager::Server.open(@socket_manager_path)
350
+ end
351
+
352
+ def after_run
353
+ @socket_manager_server.close
354
+ end
355
+
356
+ attr_reader :socket_manager_path
357
+ end
358
+
359
+ module MyWorker
360
+ def initialize
361
+ @stop_flag = ServerEngine::BlockingFlag.new
362
+ @socket_manager = ServerEngine::SocketManager::Client.new(server.socket_manager_path)
363
+ end
364
+
365
+ def run
366
+ lsock = @socket_manager.listen_tcp('0.0.0.0', 12345)
367
+ until @stop
368
+ c = lsock.accept
369
+ c.write "Awesome work!"
370
+ c.close
371
+ end
372
+ end
319
373
 
320
- Available methods are different depending on `worker_type`.
374
+ def stop
375
+ @stop = true
376
+ end
377
+ end
378
+
379
+ se = ServerEngine.create(MyServer, MyWorker, {
380
+ daemonize: true,
381
+ log: 'myserver.log',
382
+ pid_path: 'myserver.pid',
383
+ worker_type: 'process',
384
+ workers: 4,
385
+ bind: '0.0.0.0',
386
+ port: 9071,
387
+ })
388
+ se.run
389
+ ```
390
+
391
+
392
+ ## Module API
393
+
394
+ Available methods are different depending on `worker_type`. ServerEngine supports 3 worker types:
395
+
396
+ - **embedded**: uses a thread to run worker module (default). This type doesn't support immediate shutdown or immediate restart.
397
+ - **thread**: uses threads to run worker modules. This type doesn't support immediate shutdown or immediate restart.
398
+ - **process**: uses processes to run worker modules. This type doesn't work on Windows or JRuby platform.
399
+ - **spawn**: uses processes to run worker modules. This type works on Windows and JRuby platform but available interface of worker module is limited (See also Worker module section).
400
+
401
+ ### Worker module
321
402
 
322
403
  - interface
323
404
  - `initialize` is called in the parent process (or thread) in contrast to the other methods
@@ -350,31 +431,7 @@ Available methods are different depending on `worker_type`.
350
431
  - `logger` logger
351
432
 
352
433
 
353
- ## Worker types
354
-
355
- ServerEngine supports 3 worker types:
356
-
357
- - **embedded**: uses a thread to run worker module (default). This type doesn't support immediate shutdown or immediate restart.
358
- - **thread**: uses threads to run worker modules. This type doesn't support immediate shutdown or immediate restart.
359
- - **process**: uses processes to run worker modules. This type doesn't work on Windows or JRuby platform.
360
- - **spawn**: uses processes to run worker modules. This type works on Windows and JRuby platform but available interface of worker module is limited (See also Worker module section).
361
-
362
-
363
- ## Signals
364
-
365
- - **TERM:** graceful shutdown
366
- - **QUIT:** immediate shutdown (available only when `worker_type` is "process")
367
- - **USR1:** graceful restart
368
- - **HUP:** immediate restart (available only when `worker_type` is "process")
369
- - **USR2:** reload config file and reopen log file
370
- - **INT:** detach process for live restarting (available only when `supervisor` and `enable_detach` parameters are true. otherwise graceful shutdown)
371
- - **CONT:** dump stacktrace and memory information to /tmp/sigdump-<pid>.log file
372
-
373
- Immediate shutdown and restart send SIGQUIT signal to worker processes which kills the processes.
374
- Graceful shutdown and restart call `Worker#stop` method and wait for completion of `Worker#run` method.
375
-
376
-
377
- ## Configuration
434
+ ## List of all configurations
378
435
 
379
436
  - Daemon
380
437
  - **daemonize** enables daemonize (default: false)
data/appveyor.yml ADDED
@@ -0,0 +1,18 @@
1
+ ---
2
+ install:
3
+ - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
4
+ - ruby --version
5
+ - gem --version
6
+ - bundle install
7
+ build: off
8
+ test_script:
9
+ - bundle exec rake -rdevkit
10
+
11
+ environment:
12
+ matrix:
13
+ - ruby_version: "200"
14
+ - ruby_version: "200-x64"
15
+ - ruby_version: "21"
16
+ - ruby_version: "21-x64"
17
+ - ruby_version: "22"
18
+ - ruby_version: "22-x64"
@@ -58,10 +58,9 @@ module ServerEngine
58
58
  def create_logger
59
59
  if logger = @config[:logger]
60
60
  @logger = logger
61
- return
61
+ else
62
+ @logger = @logger_class.new(logdev_from_config(@config), @config)
62
63
  end
63
-
64
- @logger = @logger_class.new(logdev_from_config(@config), @config)
65
64
  end
66
65
 
67
66
  def logdev_from_config(config)
@@ -49,6 +49,9 @@ module ServerEngine
49
49
  @chumask = @config[:chumask]
50
50
  end
51
51
 
52
+ # server is available when run() is called. It is a Supervisor instance if supervisor is set to true. Otherwise a Server instance.
53
+ attr_reader :server
54
+
52
55
  module Signals
53
56
  GRACEFUL_STOP = :TERM
54
57
  IMMEDIATE_STOP = :QUIT
@@ -157,7 +160,7 @@ module ServerEngine
157
160
  private
158
161
 
159
162
  def create_server(logger)
160
- @create_server_proc.call(@load_config_proc, logger)
163
+ @server = @create_server_proc.call(@load_config_proc, logger)
161
164
  end
162
165
  end
163
166
  end
@@ -19,12 +19,21 @@ module ServerEngine
19
19
 
20
20
  class MultiSpawnServer < MultiWorkerServer
21
21
  def initialize(worker_module, load_config_proc={}, &block)
22
- @pm = ProcessManager.new(
23
- auto_tick: false,
24
- graceful_kill_signal: Daemon::Signals::GRACEFUL_STOP,
25
- immediate_kill_signal: Daemon::Signals::IMMEDIATE_STOP,
26
- enable_heartbeat: false,
27
- )
22
+ if ServerEngine.windows?
23
+ @pm = ProcessManager.new(
24
+ auto_tick: false,
25
+ graceful_kill_signal: Daemon::Signals::GRACEFUL_STOP,
26
+ immediate_kill_signal: false,
27
+ enable_heartbeat: false,
28
+ )
29
+ else
30
+ @pm = ProcessManager.new(
31
+ auto_tick: false,
32
+ graceful_kill_signal: Daemon::Signals::GRACEFUL_STOP,
33
+ immediate_kill_signal: Daemon::Signals::IMMEDIATE_STOP,
34
+ enable_heartbeat: false,
35
+ )
36
+ end
28
37
 
29
38
  super(worker_module, load_config_proc, &block)
30
39
 
@@ -34,6 +34,10 @@ module ServerEngine
34
34
  @auto_tick_interval = config[:auto_tick_interval] || 1
35
35
 
36
36
  @enable_heartbeat = !!config[:enable_heartbeat]
37
+ if ServerEngine.windows?
38
+ # heartbeat is not supported on Windows platform. See also spawn method.
39
+ @enable_heartbeat = false
40
+ end
37
41
  @auto_heartbeat = !!config.fetch(:auto_heartbeat, true)
38
42
 
39
43
  case op = config[:on_heartbeat_error]
@@ -93,6 +97,10 @@ module ServerEngine
93
97
  end
94
98
 
95
99
  def fork(&block)
100
+ if ServerEngine.windows?
101
+ raise NotImplementedError, "fork is not available on this platform. Please use spawn (worker_type: 'spawn')."
102
+ end
103
+
96
104
  rpipe, wpipe = new_pipe_pair
97
105
 
98
106
  begin
@@ -143,12 +151,14 @@ module ServerEngine
143
151
 
144
152
  # pipe is necessary even if @enable_heartbeat == false because
145
153
  # parent process detects shutdown of a child process using it
146
- rpipe, wpipe = new_pipe_pair
147
-
148
154
  begin
149
- options[[wpipe.fileno]] = wpipe
150
- if @enable_heartbeat
151
- env['SERVERENGINE_HEARTBEAT_PIPE'] = wpipe.fileno.to_s
155
+ unless ServerEngine.windows?
156
+ # heartbeat is not supported on Windows platform
157
+ rpipe, wpipe = new_pipe_pair
158
+ options[[wpipe.fileno]] = wpipe
159
+ if @enable_heartbeat
160
+ env['SERVERENGINE_HEARTBEAT_PIPE'] = wpipe.fileno.to_s
161
+ end
152
162
  end
153
163
 
154
164
  pid = Process.spawn(env, *args, options)
@@ -156,13 +166,16 @@ module ServerEngine
156
166
  m = Monitor.new(self, pid)
157
167
 
158
168
  @monitors << m
159
- @rpipes[rpipe] = m
160
- rpipe = nil
169
+
170
+ unless ServerEngine.windows?
171
+ @rpipes[rpipe] = m
172
+ rpipe = nil
173
+ end
161
174
 
162
175
  return m
163
176
 
164
177
  ensure
165
- wpipe.close
178
+ wpipe.close if wpipe
166
179
  rpipe.close if rpipe
167
180
  end
168
181
  end
@@ -199,30 +212,34 @@ module ServerEngine
199
212
  raise AlreadyClosedError.new
200
213
  end
201
214
 
202
- if @rpipes.empty?
203
- sleep blocking_timeout if blocking_timeout > 0
204
- return nil
205
- end
215
+ time ||= Time.now
206
216
 
207
- ready_pipes, _, _ = IO.select(@rpipes.keys, nil, nil, blocking_timeout)
217
+ unless ServerEngine.windows?
218
+ # heartbeat is not supported on Windows platform.
208
219
 
209
- time ||= Time.now
220
+ if @rpipes.empty?
221
+ sleep blocking_timeout if blocking_timeout > 0
222
+ return nil
223
+ end
210
224
 
211
- if ready_pipes
212
- ready_pipes.each do |r|
213
- begin
214
- r.read_nonblock(1024, @read_buffer)
215
- rescue Errno::EAGAIN, Errno::EINTR
216
- next
217
- rescue #EOFError
218
- m = @rpipes.delete(r)
219
- m.start_immediate_stop!
220
- r.close rescue nil
221
- next
222
- end
225
+ ready_pipes, _, _ = IO.select(@rpipes.keys, nil, nil, blocking_timeout)
226
+
227
+ if ready_pipes
228
+ ready_pipes.each do |r|
229
+ begin
230
+ r.read_nonblock(1024, @read_buffer)
231
+ rescue Errno::EAGAIN, Errno::EINTR
232
+ next
233
+ rescue #EOFError
234
+ m = @rpipes.delete(r)
235
+ m.start_immediate_stop!
236
+ r.close rescue nil
237
+ next
238
+ end
223
239
 
224
- if m = @rpipes[r]
225
- m.last_heartbeat_time = time
240
+ if m = @rpipes[r]
241
+ m.last_heartbeat_time = time
242
+ end
226
243
  end
227
244
  end
228
245
  end
@@ -275,6 +292,7 @@ module ServerEngine
275
292
  end
276
293
 
277
294
  attr_accessor :last_heartbeat_time
295
+ attr_reader :pid
278
296
 
279
297
  def heartbeat_delay
280
298
  now = Time.now
@@ -389,7 +407,11 @@ module ServerEngine
389
407
  end
390
408
 
391
409
  begin
392
- Process.kill(signal, pid)
410
+ if ServerEngine.windows? && (signal == :KILL || signal == :SIGKILL)
411
+ system("taskkill /f /pid #{pid}")
412
+ else
413
+ Process.kill(signal, pid)
414
+ end
393
415
  rescue #Errno::ECHILD, Errno::ESRCH, Errno::EPERM
394
416
  # assume that any errors mean the child process is dead
395
417
  @pid = nil