|
|
@@ -1,78 +1,80 @@ |
|
|
#!/usr/bin/env ruby |
|
|
# |
|
|
# forking supervisor - forks workers and replaces dead workers. |
|
|
# start it in one console, send signals to it from another console. |
|
|
# |
|
|
|
|
|
require 'rubygems' |
|
|
require 'daemons/daemonize' |
|
|
|
|
|
|
|
|
def logit(msg) |
|
|
if $worker |
|
|
puts "#{Time.now} worker[#{$$}] #{msg}" |
|
|
else |
|
|
puts "#{Time.now} supervisor[#{$$}] #{msg}" |
|
|
end |
|
|
end |
|
|
|
|
|
def myworker |
|
|
# this is your worker, it does something useful |
|
|
loop { |
|
|
logit "ping" |
|
|
sleep 20 |
|
|
} |
|
|
end |
|
|
|
|
|
def start_worker_daemon |
|
|
p = Process.fork { $worker = true; myworker } |
|
|
logit "started worker pid=#{p}" |
|
|
p |
|
|
end |
|
|
|
|
|
|
|
|
if __FILE__ == $0 |
|
|
# starts supervisor |
|
|
Daemonize.daemonize(logfile_name="/tmp/my.log", app_name="foo") if |
|
|
ARGV[0] == '-d' # do you want supervisor as a daemon? |
|
|
|
|
|
logit "started" |
|
|
pids = Array.new |
|
|
workers = 3 # how many workers would you like today? |
|
|
logit "workers = #{workers}" |
|
|
$shutdown = false |
|
|
|
|
|
Signal.trap("TERM") { |
|
|
$shutdown = true |
|
|
if $worker |
|
|
# can do some worker cleanup here |
|
|
logit "Caught SIGTERM. Exiting." |
|
|
else |
|
|
logit "caught SIGTERM. Signaling child processes: #{pids.inspect}" |
|
|
pids.each { |pid| |
|
|
logit "sending SIGTERM to pid=#{pid}" |
|
|
Process.kill('TERM', pid) |
|
|
Process.waitpid(pid) |
|
|
logit "pid=#{pid} died." |
|
|
} |
|
|
logit "All child processes died." |
|
|
# can do some supervisor cleanup here |
|
|
end |
|
|
exit |
|
|
} |
|
|
|
|
|
Signal.trap('CLD') { |
|
|
unless $shutdown |
|
|
pids.delete(Process.wait) |
|
|
pids << start_worker_daemon |
|
|
end |
|
|
} |
|
|
|
|
|
while pids.size < workers |
|
|
pids << start_worker_daemon |
|
|
end |
|
|
|
|
|
loop { sleep 0x1000000 } rescue exit |
|
|
|
|
|
end |
|
|
|
|
|
class Daemons |
|
|
def initialize |
|
|
@num_worker = 3 |
|
|
@worker_pids = [] |
|
|
@signal_queue = [] |
|
|
@handle_signals = %i[INT CLD] |
|
|
@self_pipe_reader, @self_pipe_writer = IO.pipe |
|
|
end |
|
|
|
|
|
def start |
|
|
$PROGRAM_NAME = 'Daemons (Parent)' |
|
|
spawn_workers |
|
|
trap_signals |
|
|
|
|
|
loop do |
|
|
IO.select([@self_pipe_reader]) |
|
|
@self_pipe_reader.read(1) |
|
|
|
|
|
case @signal_queue.shift |
|
|
when :INT |
|
|
graceful_shutdown |
|
|
when :CLD |
|
|
respawn_worker |
|
|
end |
|
|
end |
|
|
end |
|
|
|
|
|
def respawn_worker |
|
|
@worker_pids.delete(Process.wait) |
|
|
spawn_worker(99) |
|
|
puts 'CLD' |
|
|
end |
|
|
|
|
|
def graceful_shutdown |
|
|
@worker_pids.each do |wpid| |
|
|
Process.kill(:INT, wpid) |
|
|
end |
|
|
|
|
|
sleep 1 |
|
|
@worker_pids.each do |wpid| |
|
|
Process.kill(:KILL, wpid) unless Process.waitpid(wpid, Process::WNOHANG) |
|
|
end |
|
|
|
|
|
exit |
|
|
end |
|
|
|
|
|
def spawn_workers |
|
|
@num_worker.times do |num| |
|
|
spawn_worker(num) |
|
|
end |
|
|
end |
|
|
|
|
|
def trap_signals |
|
|
@handle_signals.each do |sig| |
|
|
Signal.trap(sig) do |
|
|
@signal_queue << sig |
|
|
@self_pipe_writer.write('.') |
|
|
end |
|
|
end |
|
|
end |
|
|
|
|
|
def spawn_worker(num) |
|
|
@worker_pids << Process.fork do |
|
|
$PROGRAM_NAME = "Daemons (Worker ##{num})" |
|
|
|
|
|
Signal.trap(:INT) { exit } |
|
|
|
|
|
worker_loop |
|
|
end |
|
|
end |
|
|
|
|
|
def worker_loop |
|
|
loop do |
|
|
sleep 60 |
|
|
puts 'KEKA' |
|
|
end |
|
|
end |
|
|
end |
|
|
|
|
|
Daemons.new.start |