servolux 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +3 -3
- data/README.md +74 -0
- data/Rakefile +11 -7
- data/examples/beanstalk.rb +14 -3
- data/examples/server_beanstalk.rb +6 -2
- data/lib/servolux.rb +1 -10
- data/lib/servolux/child.rb +1 -1
- data/lib/servolux/daemon.rb +4 -4
- data/lib/servolux/piper.rb +4 -5
- data/lib/servolux/prefork.rb +8 -2
- data/lib/servolux/server.rb +14 -5
- data/lib/servolux/threaded.rb +52 -3
- data/lib/servolux/version.rb +8 -0
- data/script/bootstrap +9 -0
- data/spec/child_spec.rb +15 -18
- data/spec/daemon_spec.rb +3 -9
- data/spec/piper_spec.rb +17 -20
- data/spec/prefork_spec.rb +26 -30
- data/spec/server_spec.rb +45 -24
- data/spec/servolux_spec.rb +2 -6
- data/spec/spec_helper.rb +0 -3
- data/spec/threaded_spec.rb +59 -25
- metadata +51 -44
- data/README.rdoc +0 -68
- data/version.txt +0 -1
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c744b0af533fabea800e187f1008917cf2c7aacf
|
4
|
+
data.tar.gz: e46bfba16ebbcfac37aa4e4d0fa44bb6e090e403
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1f8e41c57443b49a4a4ba6766daa95933e7e6a9827e759aee2284828589e29c13536041ba70e196ac945fa5cc1ee96c61124cfd0726881990634c4b4f9b32372
|
7
|
+
data.tar.gz: 555e8ad6764063743afca6635515f445e8e8e04b509a4143fbda4026cc9074b98d9310f36da0f37c59083f127cba8a10df44738dda4b1590d52ea9a76d83af9c
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
## Serv-O-Lux
|
2
|
+
by Tim Pease [![](https://secure.travis-ci.org/TwP/servolux.png)](http://travis-ci.org/TwP/servolux)
|
3
|
+
|
4
|
+
* [Homepage](http://rubygems.org/gems/servolux)
|
5
|
+
* [Github Project](http://github.com/TwP/servolux)
|
6
|
+
|
7
|
+
### Description
|
8
|
+
|
9
|
+
Serv-O-Lux is a collection of Ruby classes that are useful for daemon and
|
10
|
+
process management, and for writing your own Ruby services. The code is well
|
11
|
+
documented and tested. It works with Ruby and JRuby supporting both 1.8 and 1.9
|
12
|
+
interpreters.
|
13
|
+
|
14
|
+
### Features
|
15
|
+
|
16
|
+
[Servolux::Threaded](http://www.rubydoc.info/github/TwP/servolux/Servolux/Threaded)
|
17
|
+
-- when included into your own class, it gives you an activity thread that will
|
18
|
+
run some code at a regular interval. Provides methods to start and stop the
|
19
|
+
thread, report on the running state, and join the thread to wait for it to
|
20
|
+
complete.
|
21
|
+
|
22
|
+
[Servolux::Server](http://www.rubydoc.info/github/TwP/servolux/Servolux/Server)
|
23
|
+
-- a template server class that handles the mundane work of creating / deleting
|
24
|
+
a PID file, reporting running state, logging errors, starting the service, and
|
25
|
+
gracefully shutting down the service.
|
26
|
+
|
27
|
+
[Servolux::Piper](http://www.rubydoc.info/github/TwP/servolux/Servolux/Piper)
|
28
|
+
-- an extension of the standard Ruby fork method that opens a pipe for
|
29
|
+
communication between parent and child processes. Ruby objects are passed
|
30
|
+
between parent and child allowing, for example, exceptions in the child process
|
31
|
+
to be passed to the parent and raised there.
|
32
|
+
|
33
|
+
[Servolux::Daemon](http://www.rubydoc.info/github/TwP/servolux/Servolux/Daemon)
|
34
|
+
-- a robust class for starting and stopping daemon processes.
|
35
|
+
|
36
|
+
[Servolux::Child](http://www.rubydoc.info/github/TwP/servolux/Servolux/Child)
|
37
|
+
-- adds some much needed functionality to child processes created via Ruby's
|
38
|
+
IO#popen method. Specifically, a timeout thread is used to signal the child
|
39
|
+
process to die if it does not exit in a given amount of time.
|
40
|
+
|
41
|
+
[Servolux::Prefork](http://www.rubydoc.info/github/TwP/servolux/Servolux/Prefork)
|
42
|
+
-- provides a pre-forking worker pool for executing tasks in parallel using
|
43
|
+
multiple processes.
|
44
|
+
|
45
|
+
All the documentation is available online at http://rdoc.info/projects/TwP/servolux
|
46
|
+
|
47
|
+
### Install
|
48
|
+
|
49
|
+
gem install servolux
|
50
|
+
|
51
|
+
### License
|
52
|
+
|
53
|
+
The MIT License
|
54
|
+
|
55
|
+
Copyright (c) 2015 Tim Pease
|
56
|
+
|
57
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
58
|
+
a copy of this software and associated documentation files (the
|
59
|
+
'Software'), to deal in the Software without restriction, including
|
60
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
61
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
62
|
+
permit persons to whom the Software is furnished to do so, subject to
|
63
|
+
the following conditions:
|
64
|
+
|
65
|
+
The above copyright notice and this permission notice shall be
|
66
|
+
included in all copies or substantial portions of the Software.
|
67
|
+
|
68
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
69
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
70
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
71
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
72
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
73
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
74
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -5,20 +5,24 @@ rescue LoadError
|
|
5
5
|
abort '### please install the "bones" gem ###'
|
6
6
|
end
|
7
7
|
|
8
|
+
lib = File.expand_path('../lib', __FILE__)
|
9
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
10
|
+
require 'servolux/version'
|
11
|
+
|
8
12
|
task :default => 'spec:run'
|
9
13
|
task 'gem:release' => 'spec:run'
|
10
14
|
|
11
15
|
Bones {
|
12
16
|
name 'servolux'
|
17
|
+
summary 'A collection of tools for working with processes'
|
13
18
|
authors 'Tim Pease'
|
14
19
|
email 'tim.pease@gmail.com'
|
15
|
-
url 'http://
|
16
|
-
|
17
|
-
spec.opts << '--color' << '--format documentation'
|
20
|
+
url 'http://rubygems.org/gems/servolux'
|
21
|
+
version Servolux::VERSION
|
18
22
|
|
19
|
-
|
23
|
+
spec.opts << '--color' << '--format documentation'
|
20
24
|
|
21
|
-
depend_on 'bones-rspec', :development => true
|
22
|
-
depend_on 'bones-git',
|
23
|
-
depend_on 'logging',
|
25
|
+
depend_on 'bones-rspec', '~> 2.0', :development => true
|
26
|
+
depend_on 'bones-git', '~> 1.3', :development => true
|
27
|
+
depend_on 'logging', '~> 2.0', :development => true
|
24
28
|
}
|
data/examples/beanstalk.rb
CHANGED
@@ -25,7 +25,9 @@ module JobProcessor
|
|
25
25
|
# Open a connection to our beanstalk queue. This method is called once just
|
26
26
|
# before entering the child run loop.
|
27
27
|
def before_executing
|
28
|
-
|
28
|
+
host = config[:host]
|
29
|
+
port = config[:port]
|
30
|
+
@beanstalk = Beanstalk::Pool.new(["#{host}:#{port}"])
|
29
31
|
end
|
30
32
|
|
31
33
|
# Close the connection to our beanstalk queue. This method is called once
|
@@ -65,7 +67,13 @@ module JobProcessor
|
|
65
67
|
end
|
66
68
|
|
67
69
|
# Create our preforking worker pool. Each worker will run the code found in
|
68
|
-
# the JobProcessor module.
|
70
|
+
# the JobProcessor module.
|
71
|
+
#
|
72
|
+
# The `:config` Hash is passed to each worker when it is created. The values
|
73
|
+
# here are available to the JobProcessor module. We use this config hash to pass
|
74
|
+
# the `:host` and `:port` where the beanstalkd server can be found.
|
75
|
+
#
|
76
|
+
# We set a timeout of 10 minutes for the worker pool. The child process
|
69
77
|
# must send a "heartbeat" message to the parent within this timeout period;
|
70
78
|
# otherwise, the parent will halt the child process.
|
71
79
|
#
|
@@ -75,7 +83,10 @@ end
|
|
75
83
|
#
|
76
84
|
# This also means that if any job processed by a worker takes longer than 10
|
77
85
|
# minutes to run, that child worker will be killed.
|
78
|
-
pool = Servolux::Prefork.new
|
86
|
+
pool = Servolux::Prefork.new \
|
87
|
+
:timeout => 600,
|
88
|
+
:module => JobProcessor,
|
89
|
+
:config => {:host => '127.0.0.1', :port => 11300}
|
79
90
|
|
80
91
|
# Start up 7 child processes to handle jobs
|
81
92
|
pool.start 7
|
@@ -32,7 +32,9 @@ module BeanstalkWorkerPool
|
|
32
32
|
# Before we start the server run loop, allocate our pool of child workers
|
33
33
|
# and prefork seven JobProcessors to pull work from the beanstalk queue.
|
34
34
|
def before_starting
|
35
|
-
@pool = Servolux::Prefork.new
|
35
|
+
@pool = Servolux::Prefork.new \
|
36
|
+
:module => JobProcessor,
|
37
|
+
:config => {:host => '127.0.0.1', :port => 11300}
|
36
38
|
@pool.start 7
|
37
39
|
end
|
38
40
|
|
@@ -57,7 +59,9 @@ end
|
|
57
59
|
# See the beanstalk.rb example for an explanation of the JobProcessor
|
58
60
|
module JobProcessor
|
59
61
|
def before_executing
|
60
|
-
|
62
|
+
host = config[:host]
|
63
|
+
port = config[:port]
|
64
|
+
@beanstalk = Beanstalk::Pool.new(["#{host}:#{port}"])
|
61
65
|
end
|
62
66
|
|
63
67
|
def after_executing
|
data/lib/servolux.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
module Servolux
|
3
2
|
|
4
3
|
# :stopdoc:
|
@@ -9,14 +8,6 @@ module Servolux
|
|
9
8
|
# Generic Servolux Error class.
|
10
9
|
Error = Class.new(StandardError)
|
11
10
|
|
12
|
-
# Returns the version string for the library.
|
13
|
-
#
|
14
|
-
# @return [String]
|
15
|
-
#
|
16
|
-
def self.version
|
17
|
-
@version ||= File.read(path('version.txt')).strip
|
18
|
-
end
|
19
|
-
|
20
11
|
# Returns the library path for the module. If any arguments are given,
|
21
12
|
# they will be joined to the end of the library path using
|
22
13
|
# <tt>File.join</tt>.
|
@@ -47,7 +38,7 @@ module Servolux
|
|
47
38
|
|
48
39
|
end
|
49
40
|
|
50
|
-
%w[threaded server piper daemon child prefork].each do |lib|
|
41
|
+
%w[version threaded server piper daemon child prefork].each do |lib|
|
51
42
|
require Servolux.libpath('servolux', lib)
|
52
43
|
end
|
53
44
|
|
data/lib/servolux/child.rb
CHANGED
data/lib/servolux/daemon.rb
CHANGED
@@ -126,7 +126,7 @@ class Servolux::Daemon
|
|
126
126
|
# still being used by the parent process. This prevents zombie processes.
|
127
127
|
#
|
128
128
|
# @option opts [Numeric, String, Array<String>, Proc, Method, Servolux::Server] :shutdown_command (nil)
|
129
|
-
# Assign the
|
129
|
+
# Assign the shutdown command. Different calling semantics are used for
|
130
130
|
# each type of command.
|
131
131
|
#
|
132
132
|
# @option opts [String] :log_file (nil)
|
@@ -363,9 +363,9 @@ private
|
|
363
363
|
|
364
364
|
skip = [STDIN, STDOUT, STDERR]
|
365
365
|
skip << @piper.socket if @piper
|
366
|
-
ObjectSpace.each_object(IO) { |
|
367
|
-
next if skip.include?
|
368
|
-
|
366
|
+
ObjectSpace.each_object(IO) { |io|
|
367
|
+
next if skip.include? io
|
368
|
+
io.close unless io.closed?
|
369
369
|
}
|
370
370
|
|
371
371
|
before_exec.call if before_exec.respond_to? :call
|
data/lib/servolux/piper.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
require 'socket'
|
3
2
|
|
4
3
|
# == Synopsis
|
@@ -55,7 +54,7 @@ class Servolux::Piper
|
|
55
54
|
# Creates a new Piper with the child process configured as a daemon. The
|
56
55
|
# +pid+ method of the piper returns the PID of the daemon process.
|
57
56
|
#
|
58
|
-
#
|
57
|
+
# By default a daemon process will release its current working directory
|
59
58
|
# and the stdout/stderr/stdin file descriptors. This allows the parent
|
60
59
|
# process to exit cleanly. This behavior can be overridden by setting the
|
61
60
|
# _nochdir_ and _noclose_ flags to true. The first will keep the current
|
@@ -154,7 +153,7 @@ class Servolux::Piper
|
|
154
153
|
# @return [Piper] self
|
155
154
|
#
|
156
155
|
def close
|
157
|
-
@socket.close
|
156
|
+
@socket.close unless @socket.closed?
|
158
157
|
self
|
159
158
|
end
|
160
159
|
|
@@ -173,7 +172,7 @@ class Servolux::Piper
|
|
173
172
|
#
|
174
173
|
def readable?
|
175
174
|
return false if @socket.closed?
|
176
|
-
r,
|
175
|
+
r,_,_ = Kernel.select([@socket], nil, nil, @timeout) rescue nil
|
177
176
|
return !(r.nil? or r.empty?)
|
178
177
|
end
|
179
178
|
|
@@ -184,7 +183,7 @@ class Servolux::Piper
|
|
184
183
|
#
|
185
184
|
def writeable?
|
186
185
|
return false if @socket.closed?
|
187
|
-
|
186
|
+
_,w,_ = Kernel.select(nil, [@socket], nil, @timeout) rescue nil
|
188
187
|
return !(w.nil? or w.empty?)
|
189
188
|
end
|
190
189
|
|
data/lib/servolux/prefork.rb
CHANGED
@@ -141,6 +141,7 @@ class Servolux::Prefork
|
|
141
141
|
attr_accessor :timeout # Communication timeout in seconds.
|
142
142
|
attr_accessor :min_workers # Minimum number of workers
|
143
143
|
attr_accessor :max_workers # Maximum number of workers
|
144
|
+
attr_accessor :config # Worker configuration options (a Hash)
|
144
145
|
|
145
146
|
# call-seq:
|
146
147
|
# Prefork.new { block }
|
@@ -171,6 +172,7 @@ class Servolux::Prefork
|
|
171
172
|
@module = opts.fetch(:module, nil)
|
172
173
|
@max_workers = opts.fetch(:max_workers, nil)
|
173
174
|
@min_workers = opts.fetch(:min_workers, nil)
|
175
|
+
@config = opts.fetch(:config, {})
|
174
176
|
@module = Module.new { define_method :execute, &block } if block
|
175
177
|
@workers = []
|
176
178
|
|
@@ -245,7 +247,7 @@ class Servolux::Prefork
|
|
245
247
|
def add_workers( number = 1 )
|
246
248
|
number.times do
|
247
249
|
break if at_max_workers?
|
248
|
-
worker = Worker.new(
|
250
|
+
worker = Worker.new(self, @config)
|
249
251
|
worker.extend @module
|
250
252
|
worker.start
|
251
253
|
@workers << worker
|
@@ -360,12 +362,16 @@ private
|
|
360
362
|
|
361
363
|
attr_reader :error
|
362
364
|
|
365
|
+
attr_reader :config
|
366
|
+
|
363
367
|
# Create a new worker that belongs to the _prefork_ pool.
|
364
368
|
#
|
365
369
|
# @param [Prefork] prefork The prefork pool that created this worker.
|
370
|
+
# @param [Hash] config The worker configuration options.
|
366
371
|
#
|
367
|
-
def initialize( prefork )
|
372
|
+
def initialize( prefork, config )
|
368
373
|
@timeout = prefork.timeout
|
374
|
+
@config = config
|
369
375
|
@thread = nil
|
370
376
|
@piper = nil
|
371
377
|
@error = nil
|
data/lib/servolux/server.rb
CHANGED
@@ -190,9 +190,9 @@ class Servolux::Server
|
|
190
190
|
}
|
191
191
|
|
192
192
|
begin
|
193
|
+
trap_signals
|
193
194
|
create_pid_file
|
194
195
|
start
|
195
|
-
trap_signals
|
196
196
|
join
|
197
197
|
wait_for_shutdown if wait
|
198
198
|
ensure
|
@@ -266,10 +266,19 @@ class Servolux::Server
|
|
266
266
|
|
267
267
|
def trap_signals
|
268
268
|
SIGNALS.each do |sig|
|
269
|
-
|
270
|
-
|
269
|
+
method = sig.downcase.to_sym
|
270
|
+
if self.respond_to? method
|
271
|
+
Signal.trap(sig) do
|
272
|
+
Thread.new do
|
273
|
+
begin
|
274
|
+
self.send(method)
|
275
|
+
rescue StandardError => err
|
276
|
+
logger.error "Exception in signal handler: #{method}"
|
277
|
+
logger.error err
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
271
282
|
end
|
272
283
|
end
|
273
|
-
|
274
284
|
end
|
275
|
-
|
data/lib/servolux/threaded.rb
CHANGED
@@ -152,6 +152,27 @@ module Servolux::Threaded
|
|
152
152
|
_activity_thread.interval
|
153
153
|
end
|
154
154
|
|
155
|
+
# Signals the activity thread to treat the sleep interval with strict
|
156
|
+
# semantics. The time it takes for the 'run' method to execute will be
|
157
|
+
# subtracted from the sleep interval.
|
158
|
+
#
|
159
|
+
# If the sleep interval is 60 seconds and the 'run' method takes 2.2 seconds
|
160
|
+
# to execute, then the activity thread will sleep for 57.2 seconds. The
|
161
|
+
# subsequent invocation of the 'run' will occur as close as possible to 60
|
162
|
+
# seconds after the previous invocation.
|
163
|
+
#
|
164
|
+
def use_strict_interval=( value )
|
165
|
+
_activity_thread.use_strict_interval = (value ? true : false)
|
166
|
+
end
|
167
|
+
|
168
|
+
# When true, the activity thread will treat the sleep interval with strict
|
169
|
+
# semantics. See the setter method for an in depth explanation.
|
170
|
+
#
|
171
|
+
def use_strict_interval
|
172
|
+
_activity_thread.use_strict_interval
|
173
|
+
end
|
174
|
+
alias :use_strict_interval? :use_strict_interval
|
175
|
+
|
155
176
|
# Sets the maximum number of invocations of the threaded object's
|
156
177
|
# 'run' method
|
157
178
|
#
|
@@ -199,11 +220,11 @@ module Servolux::Threaded
|
|
199
220
|
|
200
221
|
# :stopdoc:
|
201
222
|
def _activity_thread
|
202
|
-
@_activity_thread ||= ::Servolux::Threaded::ThreadContainer.new(60, 0, nil, false);
|
223
|
+
@_activity_thread ||= ::Servolux::Threaded::ThreadContainer.new(60, false, 0, nil, false);
|
203
224
|
end # @private
|
204
225
|
|
205
226
|
# @private
|
206
|
-
ThreadContainer = Struct.new( :interval, :iterations, :maximum_iterations, :continue_on_error, :thread, :running ) {
|
227
|
+
ThreadContainer = Struct.new( :interval, :use_strict_interval, :iterations, :maximum_iterations, :continue_on_error, :thread, :running ) {
|
207
228
|
def start( threaded )
|
208
229
|
self.running = true
|
209
230
|
self.iterations = 0
|
@@ -219,6 +240,7 @@ module Servolux::Threaded
|
|
219
240
|
def run( threaded )
|
220
241
|
loop do
|
221
242
|
begin
|
243
|
+
mark_time
|
222
244
|
break unless running?
|
223
245
|
threaded.run
|
224
246
|
|
@@ -230,7 +252,7 @@ module Servolux::Threaded
|
|
230
252
|
end
|
231
253
|
end
|
232
254
|
|
233
|
-
sleep
|
255
|
+
sleep if running?
|
234
256
|
rescue SystemExit; raise
|
235
257
|
rescue Exception => err
|
236
258
|
if continue_on_error
|
@@ -259,6 +281,33 @@ module Servolux::Threaded
|
|
259
281
|
end # @private
|
260
282
|
|
261
283
|
alias :running? :running
|
284
|
+
|
285
|
+
# Mark the start time of the run loop.
|
286
|
+
#
|
287
|
+
def mark_time
|
288
|
+
@mark = Time.now if use_strict_interval
|
289
|
+
end # @private
|
290
|
+
|
291
|
+
# Sleep for "interval" seconds adjusting for the run time of the "run"
|
292
|
+
# method if the "use_strict_interval" flag is set. If the run time of the
|
293
|
+
# "run" method exceeds our sleep "interval", then log a warning and just
|
294
|
+
# use the interval as normal for this sleep period.
|
295
|
+
#
|
296
|
+
def sleep
|
297
|
+
time_to_sleep = interval
|
298
|
+
|
299
|
+
if use_strict_interval and interval > 0
|
300
|
+
diff = Time.now - @mark
|
301
|
+
time_to_sleep = interval - diff
|
302
|
+
|
303
|
+
if time_to_sleep < 0
|
304
|
+
time_to_sleep = interval
|
305
|
+
logger.warn "Run time [#{diff} s] exceeded strict interval [#{interval} s]"
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
::Kernel.sleep time_to_sleep
|
310
|
+
end # @private
|
262
311
|
}
|
263
312
|
# :startdoc:
|
264
313
|
|