Skip to content

Instantly share code, notes, and snippets.

@koyachi
Created November 24, 2009 10:38
Show Gist options
  • Save koyachi/241782 to your computer and use it in GitHub Desktop.
Save koyachi/241782 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
#
# worker starter
# x process starter ?
# x process runner ?
# (元process-manager.pl)
#
# - 複数種類のworkerプロセスを複数まとめて起動する
# - configで指定するプロセスは任意のプログラム(rubyに限らない)
# - configに書かれたプロセスを起動後、一定時間毎に起動したプロセスチェックしていなくなってれば新たに起動する
# - なのでloopしないプロセスは再起動される
# - このスクリプトプロセス自体はdaemontoolsで管理するとdaemon化できていいと思います
# - プロセス管理自体はdaemontoolsで
#
# TODO:
# - god?
#
# 2009-11-24 koyachi
require 'rubygems'
require 'yaml'
require 'thread'
require 'logger'
HELP = <<HELP
USAGE
$ ruby process_manager.rb /path/to/your/config.yaml
CONFIG SAMPLE
#-- config.yaml --
worker_process:
- name: foo
cmd: 'echo "hello"'
num: 2
- name: foo
cmd: 'echo "hello 2"'
num: 3
- name: foo
cmd: 'ruby -e "loop {p \"loop!\"; sleep 2}"'
num: 3
HELP
config_file = ARGV[0] || abort("\n#{HELP}")
# managerっていうかstarter/runnerか
# まとめて開始する、的な言葉
module ProcessManager
class << self
$protect_from_signals_mutex = Mutex.new
def protect_from_signals
$protect_from_signals_mutex.synchronize do
interrupted = false
previous_handler = Signal.trap(:INT) {interrupted = true}
yield
Signal.trap(:INT, previous_handler)
# ガード中に割り込み受けたらガード対象処理終わってからハンドラを起動する
previous_handler.call if interrupted
end
end
def _huntsman
Signal.trap :CHLD, 'IGNORE'
ProcessManager.active.keys.each {|pid| Process.kill 'INT', pid}
exit
end
def _hup
Signal.trap :CHLD, 'IGNORE'
ProcessManager.active.keys.each {|pid| Process.kill 'HUP', pid}
end
attr_accessor :active, :logger
def init(config_file)
@logger = Logger.new(STDOUT)
@logger.level = Logger::WARN
@logger.info "start process runner: [#{$$}]"
@active = {}
config = YAML.load_file(config_file)
@logger.info config
config['worker_process'].each do |process|
process['num'].times {start_process process}
end
end
def start_process(process)
protect_from_signals do
pid = fork
if pid
# parent process
@logger.info "start: [#{pid}] #{process['name']}"
@active[pid] = process
else
# child process
exec process['cmd']
exit
end
end
end
def run(config_file)
Signal.trap(:INT) {self._huntsman}
Signal.trap(:TERM) {self._huntsman}
Signal.trap(:HUP) {self._hup}
self.init config_file
loop do
@logger.debug "active processes: [#{@active.keys.sort.join(",")}]"
sleep 10
@active.keys.each do |pid|
_pid = Process.waitpid pid, Process::WNOHANG
if _pid
@logger.info "process has gone. restart: [#{pid}] #{@active[pid]['name']}"
process = @active.delete pid
start_process process
end
end
end
end
end
end
ProcessManager.run(config_file)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment