sponges 0.0.1
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.
- data/lib/sponges.rb +30 -0
- data/lib/sponges/cli.rb +44 -0
- data/lib/sponges/configuration.rb +20 -0
- data/lib/sponges/cpu_infos.rb +8 -0
- data/lib/sponges/runner.rb +71 -0
- data/lib/sponges/supervisor.rb +84 -0
- data/lib/sponges/version.rb +3 -0
- data/lib/sponges/worker_builder.rb +23 -0
- metadata +87 -0
data/lib/sponges.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'boson/runner'
|
3
|
+
require 'logger'
|
4
|
+
require 'nest'
|
5
|
+
require_relative 'sponges/configuration'
|
6
|
+
require_relative 'sponges/cpu_infos'
|
7
|
+
require_relative 'sponges/worker_builder'
|
8
|
+
require_relative 'sponges/supervisor'
|
9
|
+
require_relative 'sponges/runner'
|
10
|
+
require_relative 'sponges/cli'
|
11
|
+
|
12
|
+
module Sponges
|
13
|
+
SIGNALS = [:INT, :QUIT, :TERM]
|
14
|
+
|
15
|
+
def configure(&block)
|
16
|
+
Sponges::Configuration.configure &block
|
17
|
+
end
|
18
|
+
module_function :configure
|
19
|
+
|
20
|
+
def start
|
21
|
+
Sponges::Cli.start
|
22
|
+
end
|
23
|
+
module_function :start
|
24
|
+
|
25
|
+
def logger
|
26
|
+
return @logger if @logger
|
27
|
+
@logger = Sponges::Configuration.logger || Logger.new(STDOUT)
|
28
|
+
end
|
29
|
+
module_function :logger
|
30
|
+
end
|
data/lib/sponges/cli.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Sponges
|
3
|
+
class Cli < Boson::Runner
|
4
|
+
option :daemonize, type: :boolean
|
5
|
+
option :size, type: :numeric
|
6
|
+
desc "Start workers"
|
7
|
+
def start(options = {})
|
8
|
+
Sponges::Runner.new(Sponges::Configuration.worker_name, options).
|
9
|
+
work(Sponges::Configuration.worker, Sponges::Configuration.worker_method)
|
10
|
+
end
|
11
|
+
|
12
|
+
option :gracefully, type: :boolean
|
13
|
+
desc "Stop workers"
|
14
|
+
def stop(options = {})
|
15
|
+
Sponges::Runner.new(Sponges::Configuration.worker_name, options).
|
16
|
+
rest
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Show running processes"
|
20
|
+
def list
|
21
|
+
redis = Nest.new('sponges')
|
22
|
+
puts %q{
|
23
|
+
___ _ __ ___ _ __ __ _ ___ ___
|
24
|
+
/ __| '_ \ / _ \| '_ \ / _` |/ _ \/ __|
|
25
|
+
\__ \ |_) | (_) | | | | (_| | __/\__ \
|
26
|
+
|___/ .__/ \___/|_| |_|\__, |\___||___/
|
27
|
+
| | __/ |
|
28
|
+
|_| |___/
|
29
|
+
}.gsub(/^\n/, '') + "\n"
|
30
|
+
puts "Workers:"
|
31
|
+
Array(redis[:workers].smembers).each do |worker|
|
32
|
+
puts worker.rjust(6)
|
33
|
+
puts "supervisor".rjust(15)
|
34
|
+
puts redis[:worker][worker][:supervisor].get.rjust(12)
|
35
|
+
puts "children".rjust(13)
|
36
|
+
Array(redis[:worker][worker][:pids].smembers).each do |pid|
|
37
|
+
puts pid.rjust(12)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
puts "\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Sponges
|
3
|
+
class Configuration
|
4
|
+
class << self
|
5
|
+
ACCESSOR = [:worker_name, :worker, :worker_method, :logger]
|
6
|
+
attr_accessor *ACCESSOR
|
7
|
+
|
8
|
+
def configure
|
9
|
+
yield self
|
10
|
+
end
|
11
|
+
|
12
|
+
def configuration
|
13
|
+
ACCESSOR.inject({}) do |conf, method|
|
14
|
+
conf[method] = send(method)
|
15
|
+
conf
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Sponges
|
3
|
+
class Runner
|
4
|
+
def initialize(name, options = {})
|
5
|
+
@name = name
|
6
|
+
@options = default_options.merge options
|
7
|
+
@redis = Nest.new('sponges')
|
8
|
+
end
|
9
|
+
|
10
|
+
def work(worker, method, *args, &block)
|
11
|
+
Sponges.logger.info "Runner #{@name} start message received."
|
12
|
+
@supervisor = fork_supervisor(worker, method, *args, &block)
|
13
|
+
@redis[:worker][@name][:supervisor].set @supervisor
|
14
|
+
trap_signals
|
15
|
+
Sponges.logger.info "Supervisor started with #{@supervisor} pid."
|
16
|
+
if daemonize?
|
17
|
+
Sponges.logger.info "Supervisor daemonized."
|
18
|
+
Process.daemon
|
19
|
+
else
|
20
|
+
Process.waitpid(@supervisor) unless daemonize?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def rest
|
25
|
+
Sponges.logger.info "Runner #{@name} stop message received."
|
26
|
+
if pid = @redis[:worker][@name][:supervisor].get
|
27
|
+
begin
|
28
|
+
Process.kill gracefully? ? :HUP : :QUIT, pid.to_i
|
29
|
+
rescue Errno::ESRCH => e
|
30
|
+
Sponges.logger.error e
|
31
|
+
end
|
32
|
+
else
|
33
|
+
Sponges.logger.info "No supervisor found."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def trap_signals
|
40
|
+
Sponges::SIGNALS.each do |signal|
|
41
|
+
trap(signal) {|signal| kill_supervisor(signal) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def kill_supervisor(signal)
|
46
|
+
Sponges.logger.info "Supervisor receive a #{signal} signal."
|
47
|
+
Process.kill :USR1, @supervisor
|
48
|
+
end
|
49
|
+
|
50
|
+
def default_options
|
51
|
+
{
|
52
|
+
size: CpuInfo.cores_size
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def fork_supervisor(worker, method, *args, &block)
|
57
|
+
fork do
|
58
|
+
$PROGRAM_NAME = "#{@name}_supervisor"
|
59
|
+
Supervisor.new(@name, @options, worker, method, *args, &block).start
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def daemonize?
|
64
|
+
!!@options[:daemonize]
|
65
|
+
end
|
66
|
+
|
67
|
+
def gracefully?
|
68
|
+
!!@options[:gracefully]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Sponges
|
3
|
+
class Supervisor
|
4
|
+
def initialize(name, options, worker, method, *args, &block)
|
5
|
+
@name, @options = name, options
|
6
|
+
@worker, @method, @args, @block = worker, method, args, block
|
7
|
+
@redis = Nest.new('sponges')
|
8
|
+
@redis[:workers].sadd name
|
9
|
+
@pids = @redis[:worker][name][:pids]
|
10
|
+
@children_seen = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
@options[:size].times do
|
15
|
+
fork_children
|
16
|
+
end
|
17
|
+
trap_signals
|
18
|
+
sleep
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def fork_children
|
24
|
+
name = children_name
|
25
|
+
pid = fork do
|
26
|
+
$PROGRAM_NAME = name
|
27
|
+
Sponges::WorkerBuilder.new(@worker, @method, *@args, &@block).start
|
28
|
+
end
|
29
|
+
Sponges.logger.info "Supervisor create a child with #{pid} pid."
|
30
|
+
@pids.sadd pid
|
31
|
+
end
|
32
|
+
|
33
|
+
def children_name
|
34
|
+
"#{@name}_child_#{@children_seen +=1}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def trap_signals
|
38
|
+
(Sponges::SIGNALS + [:HUP]).each do |signal|
|
39
|
+
trap(signal) do
|
40
|
+
handle_signal signal
|
41
|
+
end
|
42
|
+
end
|
43
|
+
trap(:CHLD) do
|
44
|
+
pids.each do |pid|
|
45
|
+
begin
|
46
|
+
dead = Process.waitpid(pid.to_i, Process::WNOHANG)
|
47
|
+
if dead
|
48
|
+
Sponges.logger.warn "Child #{dead} died. Restarting a new one..."
|
49
|
+
@pids.srem dead
|
50
|
+
fork_children
|
51
|
+
end
|
52
|
+
rescue Errno::ECHILD => e
|
53
|
+
Sponges.logger.error e
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def handle_signal(signal)
|
60
|
+
Sponges.logger.info "Supervisor received #{signal} signal."
|
61
|
+
kill_them_all(signal)
|
62
|
+
Process.waitall
|
63
|
+
Sponges.logger.info "Children shutdown complete."
|
64
|
+
Sponges.logger.info "Supervisor shutdown. Exiting..."
|
65
|
+
Process.kill :USR1, @redis[:worker][@name][:supervisor].to_i
|
66
|
+
end
|
67
|
+
|
68
|
+
def kill_them_all(signal)
|
69
|
+
pids.each do |pid|
|
70
|
+
begin
|
71
|
+
Process.kill signal, pid.to_i
|
72
|
+
@pids.srem pid
|
73
|
+
Sponges.logger.info "Child #{pid} receive a #{signal} signal."
|
74
|
+
rescue Errno::ESRCH => e
|
75
|
+
Sponges.logger.error e
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def pids
|
81
|
+
Array(@pids.smembers)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Sponges
|
3
|
+
class WorkerBuilder
|
4
|
+
def initialize(worker, method, *args, &block)
|
5
|
+
@worker, @method, @args, @block = worker, method, args, block
|
6
|
+
end
|
7
|
+
|
8
|
+
def start
|
9
|
+
trap_signals
|
10
|
+
@worker.send(@method, *@args, &@block)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def trap_signals
|
16
|
+
Sponges::SIGNALS.each do |signal|
|
17
|
+
trap(signal) do
|
18
|
+
exit 0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sponges
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- chatgris
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: boson
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: nest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: When I build some worker, I want them to be like an army of spongebob,
|
47
|
+
always stressed and eager to work. sponges helps you to build this army of sponge,
|
48
|
+
to control them, and, well, kill them gracefully.
|
49
|
+
email:
|
50
|
+
- jboyer@af83.com
|
51
|
+
executables: []
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- lib/sponges.rb
|
56
|
+
- lib/sponges/cli.rb
|
57
|
+
- lib/sponges/configuration.rb
|
58
|
+
- lib/sponges/cpu_infos.rb
|
59
|
+
- lib/sponges/runner.rb
|
60
|
+
- lib/sponges/supervisor.rb
|
61
|
+
- lib/sponges/version.rb
|
62
|
+
- lib/sponges/worker_builder.rb
|
63
|
+
homepage: https://github.com/AF83/sponges
|
64
|
+
licenses: []
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ! '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.8.24
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: Turn any ruby object in a daemons controlling an army of sponges.
|
87
|
+
test_files: []
|