servolux 0.10.0 → 0.11.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 +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 [](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
|
|