serverengine 1.5.11 → 1.6.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.
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