rabbit_jobs 0.2.0.pre4 → 0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,11 +18,11 @@ module RabbitJobs::Job
18
18
 
19
19
  def run_perform
20
20
  begin
21
- RJ.logger.info "Running perform on #{self.inspect}"
21
+ RJ.logger.info "rj_worker[##{Process.pid}] performing #{self.to_ruby_string}"
22
22
  self.class.perform(*params)
23
23
  rescue
24
- RJ.logger.warn(self.inspect)
25
24
  RJ.logger.warn $!.message
25
+ RJ.logger.warn(self.to_ruby_string)
26
26
  RJ.logger.warn RJ::Util.cleanup_backtrace($!.backtrace).join("\n")
27
27
  run_on_error_hooks($!)
28
28
  RabbitJobs::ErrorMailer.report_error(self, $!)
@@ -70,6 +70,19 @@ module RabbitJobs::Job
70
70
  false
71
71
  end
72
72
  end
73
+
74
+ def to_ruby_string
75
+ rs = self.class.name
76
+ if params.count > 0
77
+ rs << "("
78
+ rs << params.map(&:to_s).join(", ")
79
+ rs << ")"
80
+ end
81
+ if opts.count > 0
82
+ rs << ", opts: "
83
+ rs << opts.inspect
84
+ end
85
+ end
73
86
  end
74
87
 
75
88
  module ClassMethods
@@ -99,12 +112,16 @@ module RabbitJobs::Job
99
112
  rescue NameError
100
113
  RJ.logger.error "Cannot find job class '#{encoded['class']}'"
101
114
  :not_found
115
+ rescue JSON::ParserError
116
+ RJ.logger.error "rj[##{Process.pid}] Cannot initialize job. Json parsing error."
117
+ RJ.logger.error "rj[##{Process.pid}] Data received: #{payload.inspect}"
118
+ :parsing_error
102
119
  rescue
103
- RJ.logger.error "JOB INIT ERROR at #{Time.now.to_s}:"
120
+ RJ.logger.warn "rj[##{Process.pid}] Cannot initialize job."
104
121
  RJ.logger.warn $!.message
105
122
  RJ.logger.warn RJ::Util.cleanup_backtrace($!.backtrace).join("\n")
106
- RJ.logger.error "message: #{payload.inspect}"
107
- nil
123
+ RJ.logger.warn "Data received: #{payload.inspect}"
124
+ :error
108
125
  end
109
126
  end
110
127
  end
@@ -9,8 +9,8 @@ module RabbitJobs
9
9
  module Publisher
10
10
  extend self
11
11
 
12
- def publish(klass, *params)
13
- publish_to(RJ.config.default_queue, klass, *params)
12
+ def publish(klass, *params, &block)
13
+ publish_to(RJ.config.default_queue, klass, *params, &block)
14
14
  end
15
15
 
16
16
  def publish_to(routing_key, klass, *params)
@@ -18,55 +18,48 @@ module RabbitJobs
18
18
  raise ArgumentError.new("routing_key=#{routing_key}") unless routing_key && (routing_key.is_a?(Symbol) || routing_key.is_a?(String)) && !!RJ.config[:queues][routing_key.to_s]
19
19
 
20
20
  begin
21
- AmqpHelper.with_amqp do |stop_em|
22
- AmqpHelper.prepare_channel
21
+ payload = {
22
+ 'class' => klass.to_s,
23
+ 'opts' => {'created_at' => Time.now.to_i},
24
+ 'params' => params
25
+ }.to_json
23
26
 
24
- payload = {
25
- 'class' => klass.to_s,
26
- 'opts' => {'created_at' => Time.now.to_i},
27
- 'params' => params
28
- }.to_json
27
+ AmqpHelper.prepare_channel
29
28
 
30
- AMQP::Exchange.default.publish(payload, Configuration::DEFAULT_MESSAGE_PARAMS.merge({key: RJ.config.queue_name(routing_key.to_s)})) do
31
- if stop_em
32
- AMQP.connection.disconnect { EM.stop }
33
- end
34
- end
29
+ AMQP.channel.default_exchange.publish(payload, Configuration::DEFAULT_MESSAGE_PARAMS.merge({key: RJ.config.queue_name(routing_key.to_s)})) do
30
+ yield if block_given?
35
31
  end
36
32
  rescue
37
- raise $!
38
- RJ.logger.warn $!.inspect
33
+ RJ.logger.warn $!.message
39
34
  RJ.logger.warn $!.backtrace.join("\n")
35
+ raise $!
40
36
  end
41
37
 
42
38
  true
43
39
  end
44
40
 
45
- def purge_queue(*routing_keys)
41
+ def purge_queue(*routing_keys, &block)
46
42
  raise ArgumentError unless routing_keys && routing_keys.count > 0
47
43
 
48
44
  messages_count = 0
49
-
50
45
  count = routing_keys.count
51
46
 
52
- AmqpHelper.with_amqp do |stop_em|
53
- routing_keys.each do |routing_key|
54
- queue = AMQP.channel.queue(RJ.config.queue_name(routing_key), RJ.config[:queues][routing_key.to_s])
55
- queue.status do |messages, consumers|
56
- # messages_count += messages
57
- queue.purge do |ret|
58
- raise "Cannot purge queue #{routing_key.to_s}." unless ret.is_a?(AMQ::Protocol::Queue::PurgeOk)
59
- messages_count += ret.message_count
60
- count -= 1
61
- if count == 0 && stop_em
62
- AMQP.connection.disconnect { EM.stop }
63
- end
47
+ AmqpHelper.prepare_channel
48
+
49
+ routing_keys.each do |routing_key|
50
+ queue = AMQP.channel.queue(RJ.config.queue_name(routing_key), RJ.config[:queues][routing_key.to_s])
51
+ queue.status do |messages, consumers|
52
+ # messages_count += messages
53
+ queue.purge do |ret|
54
+ raise "Cannot purge queue #{routing_key.to_s}." unless ret.is_a?(AMQ::Protocol::Queue::PurgeOk)
55
+ messages_count += ret.message_count
56
+ count -= 1
57
+ if count == 0
58
+ yield messages_count if block_given?
64
59
  end
65
60
  end
66
61
  end
67
62
  end
68
-
69
- return messages_count
70
63
  end
71
64
  end
72
65
  end
@@ -70,6 +70,7 @@ module RabbitJobs
70
70
  end
71
71
 
72
72
  def rufus_scheduler
73
+ raise "Cannot start without eventmachine running." unless EM.reactor_running?
73
74
  @rufus_scheduler ||= Rufus::Scheduler.start_new
74
75
  end
75
76
 
@@ -83,44 +84,48 @@ module RabbitJobs
83
84
 
84
85
  # Subscribes to channel and working on jobs
85
86
  def work(time = 0)
86
- return false unless startup
87
+ begin
88
+ return false unless startup
87
89
 
88
- $0 = self.process_name || "rj_scheduler"
90
+ $0 = self.process_name || "rj_scheduler"
89
91
 
90
- processed_count = 0
91
- AmqpHelper.with_amqp do |stop_em|
92
- AmqpHelper.prepare_channel
92
+ processed_count = 0
93
+ RJ.run do
94
+ AmqpHelper.prepare_channel
93
95
 
94
- load_schedule!
96
+ load_schedule!
95
97
 
96
- check_shutdown = Proc.new {
97
- if @shutdown
98
- AMQP.connection.disconnect {
99
- File.delete(self.pidfile) if self.pidfile
100
- EM.stop { exit! }
101
- }
102
- end
103
- }
98
+ check_shutdown = Proc.new {
99
+ if @shutdown
100
+ RJ.stop {
101
+ File.delete(self.pidfile) if self.pidfile
102
+ }
103
+ RJ.logger.info "rj_scheduler[##{Process.pid}] stopped."
104
+ end
105
+ }
104
106
 
105
- if time > 0
106
- # for debugging
107
- EM.add_timer(time) do
108
- self.shutdown
107
+ if time > 0
108
+ EM.add_timer(time) do
109
+ self.shutdown
110
+ end
109
111
  end
110
- end
111
112
 
112
- RJ.logger.info "Scheduler started."
113
+ EM.add_periodic_timer(1) do
114
+ check_shutdown.call
115
+ end
113
116
 
114
- EM.add_periodic_timer(1) do
115
- check_shutdown.call
117
+ RJ.logger.info "rj_scheduler[##{Process.pid}] started."
116
118
  end
119
+ rescue => e
120
+ RJ.logger.error e.message
121
+ RJ.logger.error e.backtrace.join("\r\n")
117
122
  end
118
123
 
119
124
  true
120
125
  end
121
126
 
122
127
  def shutdown
123
- RJ.logger.warn "Stopping scheduler..."
128
+ RJ.logger.info "rj_scheduler[##{Process.pid}] stopping..."
124
129
  @shutdown = true
125
130
  end
126
131
 
@@ -138,14 +143,14 @@ module RabbitJobs
138
143
  end
139
144
  end
140
145
 
141
- if self.pidfile
142
- File.open(self.pidfile, 'w') { |f| f << Process.pid }
143
- end
144
-
145
146
  # Fix buffering so we can `rake rj:work > resque.log` and
146
147
  # get output from the child in there.
147
148
  $stdout.sync = true
148
149
 
150
+ if self.pidfile
151
+ File.open(self.pidfile, 'w') { |f| f << Process.pid }
152
+ end
153
+
149
154
  @shutdown = false
150
155
 
151
156
  Signal.trap('TERM') { shutdown }
@@ -1,46 +1,228 @@
1
- # require 'resque/tasks'
1
+ # require 'rabbit_jobs/tasks'
2
2
  # will give you the resque tasks
3
3
 
4
4
  require 'rabbit_jobs'
5
5
  require 'logger'
6
+ require 'rake'
6
7
 
7
- namespace :rj do
8
- def initialize_rj_daemon(daemon)
9
- daemon.pidfile = ENV['PIDFILE']
10
- daemon.background = %w(yes true).include? ENV['BACKGROUND']
11
- RJ.logger = ::Logger.new(ENV['LOGFILE'] || $stdout)
12
- RJ.logger.level = ENV['VERBOSE'] ? Logger::INFO : Logger::WARN
8
+ def rails_env
9
+ $my_rails_env ||= defined?(Rails) ? Rails.env : (ENV['RAILS_ENV'] || 'development')
10
+ end
11
+
12
+ def app_root
13
+ $my_rails_root ||= Pathname.new(ENV['RAILS_ROOT'] || Rails.root)
14
+ end
13
15
 
14
- daemon
16
+ def make_dirs
17
+ ["log", "tmp", "tmp/pids"].each do |subdir|
18
+ dir = app_root.join(subdir)
19
+ Dir.mkdir(dir) unless File.directory?(dir)
15
20
  end
21
+ end
16
22
 
17
- desc "Start a Rabbit Jobs worker"
18
- task :worker => :environment do
19
- require 'rabbit_jobs'
23
+ namespace :rj do
24
+ task :environment do
25
+ # Rails.application.eager_load!
26
+ Rails.application.require_environment!
27
+ end
20
28
 
21
- queues = (ENV['QUEUES'] || ENV['QUEUE']).to_s.split(',')
22
- worker = initialize_rj_daemon(RJ::Worker.new(*queues))
29
+ # MULTIPROCESS
30
+ namespace :worker do
31
+ desc "Start a Rabbit Jobs workers from config/rj_workers.yml"
32
+ task :start => [:environment, :load_config] do
33
+ make_dirs
34
+ @rj_config.each do |worker_name, worker_props|
35
+ worker_num = 1
36
+ worker_props['instances'].to_i.times do
37
+ unless @do_only.count > 0 && !@do_only.include?("#{worker_name}-#{worker_num}")
38
+ queues = (worker_props['queue'] || worker_props['queues'] || "").split(' ')
23
39
 
24
- exit(0) if worker.work
25
- end
40
+ worker = RJ::Worker.new(*queues)
41
+ worker.background = true
42
+ worker.process_name = "rj_worker #{worker_name}##{worker_num} #{rails_env} [#{queues.join(',')}]"
43
+ worker.pidfile = app_root.join("tmp/pids/rj_worker_#{rails_env}_#{worker_name}_#{worker_num}.pid")
44
+ RJ.logger = ::Logger.new(app_root.join("log/rj_worker_#{rails_env}_#{worker_name}_#{worker_num}.log"), 'daily')
45
+ # RJ.logger.level = ENV['VERBOSE'] ? Logger::INFO : Logger::WARN
46
+ puts "Starting #{worker_name}##{worker_num}"
47
+
48
+ # завершаем копию процесса, если воркер уже отработал
49
+ exit! if worker.work
50
+ end
51
+ worker_num += 1
52
+ end
53
+ end
54
+ end
55
+
56
+ task :stop => :load_config do
57
+ # получаем идентификаторы процессов
58
+ pids = {}
59
+ errors = []
60
+
61
+ @rj_config.each do |worker_name, worker_props|
62
+ worker_num = 1
63
+ worker_props['instances'].to_i.times do
64
+ unless (@do_only.count > 0) && !@do_only.include?("#{worker_name}-#{worker_num}")
65
+ pidfile = app_root.join("tmp/pids/rj_worker_#{rails_env}_#{worker_name}_#{worker_num}.pid")
66
+
67
+ unless File.exists?(pidfile)
68
+ msg = "Pidfile not found: #{pidfile}"
69
+ errors << msg
70
+ $stderr.puts msg
71
+ else
72
+ pid = open(pidfile).read.to_i
73
+ pids[pid] = pidfile
74
+ queues = (worker_props['queue'] || worker_props['queues'] || "").split(' ')
75
+ puts "Stopping rj_worker #{worker_name}##{worker_num} #{rails_env} [#{queues.join(',')}]"
76
+ end
77
+ end
78
+ worker_num += 1
79
+ end
80
+ end
81
+
82
+ # пытаемся их убить
83
+ killed_pids = []
84
+ pids.each do |pid, pidfile|
85
+ begin
86
+ puts "try killing ##{pid}"
87
+ Process.kill("TERM", pid)
88
+ rescue => e
89
+ errors << "Not found process: #{pid} from #{pidfile}"
90
+ $stderr.puts errors.last
91
+ $stderr.puts "Removing pidfile ..."
92
+ File.delete(pidfile) if File.exist?(pidfile)
93
+ end
94
+ end
95
+
96
+ while killed_pids.count != pids.keys.count
97
+ pids.each_key do |pid|
98
+ begin
99
+ Process.kill(0, pid)
100
+ stopped = false
101
+ break
102
+ rescue
103
+ killed_pids.push(pid) unless killed_pids.include?(pid)
104
+ end
105
+ end
106
+ print '.'
107
+ sleep 1
108
+ end
109
+
110
+ exit(1) if not errors.empty?
111
+
112
+ puts "\nrj_worker stopped."
113
+ exit(0)
114
+ end
115
+
116
+ task :status => :load_config do
117
+
118
+ errors ||= 0
26
119
 
27
- desc "Start a Rabbit Jobs scheduler"
28
- task :scheduler => :environment do
29
- scheduler = initialize_rj_daemon(RabbitJobs::Scheduler.new)
120
+ @rj_config.each do |worker_name, worker_props|
121
+ worker_num = 1
122
+ worker_props['instances'].to_i.times do
123
+ pidfile = app_root.join("tmp/pids/rj_worker_#{rails_env}_#{worker_name}_#{worker_num}.pid")
30
124
 
31
- scheduler.work
125
+ unless File.exists?(pidfile)
126
+ puts "Pidfile not found: #{pidfile}"
127
+ errors += 1
128
+ else
129
+ pid = open(pidfile).read.to_i
130
+ begin
131
+ raise "must return 1" unless Process.kill(0, pid) == 1
132
+ rescue
133
+ puts "Pidfile found but process not respond: #{pidfile}\r\nRemoving pidfile."
134
+ File.delete(pidfile) if File.exist?(pidfile)
135
+ errors += 1
136
+ end
137
+ end
138
+ worker_num += 1
139
+ end
140
+ end
141
+
142
+ puts "ok" if errors == 0
143
+ exit errors
144
+ end
145
+
146
+ task :load_config do
147
+ @rj_config = YAML.load(open app_root.join("config/rj_workers.yml"))
148
+ @rj_config = @rj_config[rails_env]
149
+
150
+ @do_only = ENV["WORKERS"] ? ENV["WORKERS"].strip.split : []
151
+ @do_only.each do |worker|
152
+ worker_name, worker_num = worker.split('-')
153
+ unless @rj_config.keys.include?(worker_name) && @rj_config[worker_name]['instances'].to_i >= worker_num.to_i
154
+ raise "Worker #{worker} not found."
155
+ end
156
+ end
157
+ end
32
158
  end
33
159
 
34
- # Preload app files if this is Rails
35
- task :environment do
36
- if defined?(Rails) && Rails.respond_to?(:application)
37
- # Rails 3
38
- # Rails.application.eager_load!
39
- Rails.application.require_environment!
40
- elsif defined?(Rails::Initializer)
41
- # Rails 2.3
42
- $rails_rake_task = false
43
- Rails::Initializer.run :load_application_classes
160
+ namespace :scheduler do
161
+ task :start do
162
+ make_dirs
163
+
164
+ scheduler = RabbitJobs::Scheduler.new
165
+
166
+ scheduler.background = true
167
+ scheduler.pidfile = app_root.join('tmp/pids/rj_scheduler.pid')
168
+ RJ.logger = ::Logger.new(app_root.join('log/rj_scheduler.log'), 'daily')
169
+ # RJ.logger.level = ENV['VERBOSE'] ? Logger::INFO : Logger::WARN
170
+
171
+ scheduler.work
172
+ puts "rj_scheduler started."
173
+ end
174
+
175
+ task :stop do
176
+ pidfile = app_root.join('tmp/pids/rj_scheduler.pid')
177
+
178
+ unless File.exists?(pidfile)
179
+ msg = "Pidfile not found: #{pidfile}"
180
+ $stderr.puts msg
181
+ exit(1)
182
+ else
183
+ pid = open(pidfile).read.to_i
184
+ begin
185
+ Process.kill("TERM", pid)
186
+ rescue => e
187
+ $stderr.puts "Not found process: #{pid} from #{pidfile}"
188
+ $stderr.puts "Removing pidfile ..."
189
+ File.delete(pidfile) if File.exist?(pidfile)
190
+ exit(1)
191
+ end
192
+
193
+ while true
194
+ begin
195
+ Process.kill(0, pid)
196
+ sleep 0.1
197
+ rescue
198
+ puts "rj_scheduler[##{pid}] stopped."
199
+ exit(0)
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ task :status do
206
+ pidfile = app_root.join('tmp/pids/rj_scheduler.pid')
207
+
208
+ unless File.exists?(pidfile)
209
+ puts "Pidfile not found: #{pidfile}"
210
+ exit(1)
211
+ else
212
+ pid = open(pidfile).read.to_i
213
+ begin
214
+ raise "must return 1" unless Process.kill(0, pid) == 1
215
+ puts "ok"
216
+ rescue
217
+ puts "Pidfile found but process not respond: #{pidfile}"
218
+ puts "Removing pidfile."
219
+ File.delete(pidfile) if File.exist?(pidfile)
220
+ exit(1)
221
+ end
222
+ end
223
+ exit(0)
44
224
  end
45
225
  end
226
+
227
+ # desc "Start a Rabbit Jobs scheduler"
46
228
  end