Created
November 24, 2009 10:38
-
-
Save koyachi/241782 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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