Advertisement
Guest User

Untitled

a guest
Jan 21st, 2012
581
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.26 KB | None | 0 0
  1. # Rake task to launch multiple Resque workers in development/production with simple management included
  2.  
  3. require 'resque/tasks' # Require Resque tasks
  4.  
  5. namespace :workers do
  6.  
  7. # = $ rake workers:start
  8. #
  9. # Launch multiple Resque workers with the Rails environment loaded,
  10. # so they have access to your models, etc.
  11. #
  12. # Each worker is being run in their own separate process (_not_ thread).
  13. #
  14. # First started group of workers is considered "main group", whose PIDs are
  15. # saved into pid files in tmp/pids/resque/*.pid. When any worker from this
  16. # group exits, it takes the the master process down as well.
  17. # This is because we want to monitor this group as a whole.
  18. #
  19. # Any subsequently started groups are considered ad hoc. Their PIDs
  20. # are not saved. When workers from this group exit, the master process
  21. # and other workers continue running. This is because we don't care about
  22. # workers in this group.
  23. #
  24. # On clean shutdown (SIGINT / Ctrl-C, SIGQUIT, SIGTERM), the task will clean
  25. # after itself: kill its workers and delete PID files, when appropriate. It
  26. # will deal fine with already dead workers.
  27. #
  28. # Default options like COUNT can and should be over-ridden when invoking, of course:
  29. #
  30. # $ rake workers:start COUNT=10 QUEUE=my_queue
  31. #
  32. # To daemonize, simply run with nohup:
  33. #
  34. # nohup rake workers:start > log/workers.log 2>&1 &
  35. #
  36. # You can set up your monitoring tool to watch for process with PID
  37. # from `cat tmp/pids/resque/master.pid`.
  38. #
  39. # For proper monitoring of workers, use provided examples for God or Monit:
  40. # http://github.com/defunkt/resque/blob/master/examples/.
  41. #
  42. # NOTE: There are couple of reasons why we needed something like this and didn't
  43. # find provided in Resque.
  44. #
  45. # First, we wanted to have one single Rake task to launch multiple workers
  46. # in production and in development. This was inspired by the bundled
  47. # `rake resque:workers` task.
  48. #
  49. # Second, we wanted operations guys to simply guarantee that this Rake task
  50. # is running and that's all. We would rather configure and tune everything
  51. # (like the number of workers) inside the task.
  52. #
  53. # Third, we wanted an easy way how to very easily launch multiple workers
  54. # in production (on sudden spikes in jobs, etc).
  55.  
  56. desc "Run and manage group of Resque workers with some default options"
  57. task :start => :environment do
  58.  
  59. def Process.exists?(pid)
  60. kill(0, pid.to_i) rescue false
  61. end
  62.  
  63. def pid_directory
  64. @pid_directory ||= Rails.root.join('tmp', 'pids', "resque")
  65. end
  66.  
  67. def main_group_master_pid
  68. File.read pid_directory.join('master.pid').to_s rescue nil
  69. end
  70.  
  71. def main_group?
  72. !main_group_master_pid || main_group_master_pid.to_s == Process.pid.to_s
  73. end
  74.  
  75. def main_group_running?
  76. Process.exists?(main_group_master_pid)
  77. end
  78.  
  79. def kill_worker(pid)
  80. Process.kill("QUIT", pid)
  81. puts "Killed worker with PID #{pid}"
  82. rescue Errno::ESRCH => e
  83. puts " STALE worker with PID #{pid}"
  84. end
  85.  
  86. def kill_workers
  87. @pids.each { |pid| kill_worker(pid) }
  88. end
  89.  
  90. def kill_workers_and_remove_pids_for_main_group
  91. Dir.glob(pid_directory.join('worker_*.pid').to_s) do |pidfile|
  92. begin
  93. pid = pidfile[/(\d+)\.pid/, 1].to_i
  94. kill_worker(pid)
  95. ensure
  96. FileUtils.rm pidfile, :force => true
  97. end
  98. end
  99. FileUtils.rm pid_directory.join('master.pid').to_s if main_group_master_pid
  100. end
  101.  
  102. def shutdown
  103. puts "\n*** Exiting"
  104. if main_group?
  105. kill_workers_and_remove_pids_for_main_group
  106. else
  107. kill_workers
  108. end
  109. exit(0)
  110. end
  111.  
  112. # Clean up after dead main group from before -- and become one
  113. unless main_group_running?
  114. puts "--- Cleaning up after previous main group (PID: #{main_group_master_pid})"
  115. kill_workers_and_remove_pids_for_main_group
  116. end
  117.  
  118. # Handle exit
  119. trap('INT') { shutdown }
  120. trap('QUIT') { shutdown }
  121. trap('TERM') { shutdown }
  122.  
  123. # - CONFIGURATION ----
  124. ENV['QUEUE'] ||= '*'
  125. ENV['COUNT'] ||= '3'
  126. # --------------------
  127.  
  128. puts "=== Launching #{ENV['COUNT']} worker(s) on '#{ENV['QUEUE']}' queue(s) with PID #{Process.pid}"
  129.  
  130. # Launch workers in separate processes, saving their PIDs
  131. @pids = []
  132. ENV['COUNT'].to_i.times do
  133. @pids << Process.fork { Rake::Task['resque:work'].invoke }
  134. end
  135.  
  136. if main_group?
  137. # Make sure we have directory for pids
  138. FileUtils.mkdir_p pid_directory.to_s
  139. # Create PID files for workers
  140. File.open( pid_directory.join("master.pid").to_s, 'w' ) do |f| f.write Process.pid end
  141. @pids.each do |pid| File.open( pid_directory.join("worker_#{pid}.pid").to_s, 'w' ) { |f| f.write pid } end
  142. # Stay in foreground, if any of our workers dies, we'll get killed so
  143. # Monit/God etc can come to the resq^Hcue
  144. Process.wait
  145. else
  146. # Stay in foreground, if any of our workers dies, continue running
  147. Process.waitall
  148. end
  149. end
  150.  
  151. desc "Kill ALL workers on this machine"
  152. task :kilall do
  153. require 'resque'
  154. Resque::Worker.all.each do |worker|
  155. puts "Shutting down worker #{worker}"
  156. host, pid, queues = worker.id.split(':')
  157. Process.kill("QUIT", pid.to_i)
  158. end
  159. end
  160.  
  161. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement