Skip to content

Instantly share code, notes, and snippets.

@trddddd
Forked from somic/forking_supervisor.rb
Last active August 13, 2020 23:23
Show Gist options
  • Select an option

  • Save trddddd/dbc0bf4e98cf81c1d3addb935bdfa71e to your computer and use it in GitHub Desktop.

Select an option

Save trddddd/dbc0bf4e98cf81c1d3addb935bdfa71e to your computer and use it in GitHub Desktop.

Revisions

  1. trddddd revised this gist Aug 13, 2020. 1 changed file with 80 additions and 78 deletions.
    158 changes: 80 additions & 78 deletions forking_supervisor.rb
    Original file line number Diff line number Diff line change
    @@ -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
  2. @somic somic created this gist Dec 3, 2008.
    78 changes: 78 additions & 0 deletions forking_supervisor.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,78 @@
    #!/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