rabbit_jobs 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/Gemfile +11 -2
- data/examples/configuration.rb +22 -0
- data/lib/rabbit_jobs/amqp_helpers.rb +45 -0
- data/lib/rabbit_jobs/configuration.rb +122 -0
- data/lib/rabbit_jobs/helpers.rb +53 -0
- data/lib/rabbit_jobs/job.rb +43 -0
- data/lib/rabbit_jobs/logger.rb +18 -0
- data/lib/rabbit_jobs/publisher.rb +58 -0
- data/lib/rabbit_jobs/tasks.rb +57 -0
- data/lib/rabbit_jobs/version.rb +1 -1
- data/lib/rabbit_jobs/worker.rb +104 -0
- data/lib/rabbit_jobs.rb +34 -3
- data/lib/tasks/rabbit_jobs.rake +3 -0
- data/rabbit_jobs.gemspec +3 -3
- data/spec/fixtures/config.yml +17 -0
- data/spec/integration/publisher_spec.rb +17 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/unit/configuration_spec.rb +89 -0
- data/spec/unit/logger_spec.rb +23 -0
- data/spec/unit/rabbit_jobs_spec.rb +11 -0
- data/spec/unit/worker_spec.rb +49 -0
- metadata +30 -16
data/.gitignore
CHANGED
data/.rspec
ADDED
data/Gemfile
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
source
|
1
|
+
source :rubygems
|
2
2
|
|
3
|
-
# Specify your gem's dependencies in rabbit_jobs.gemspec
|
4
3
|
gemspec
|
4
|
+
|
5
|
+
gem 'rabbit_jobs', :path => './'
|
6
|
+
|
7
|
+
group :development do
|
8
|
+
gem 'rspec', '~> 2.8'
|
9
|
+
gem 'rr'
|
10
|
+
gem 'autotest'
|
11
|
+
# gem 'fs-event'
|
12
|
+
gem 'simplecov', require: false
|
13
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rabbit_jobs'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
RabbitJobs.configure do |c|
|
7
|
+
c.host "127.0.0.1"
|
8
|
+
|
9
|
+
c.exchange 'test_exchange', durable: true, auto_delete: false
|
10
|
+
|
11
|
+
c.queue 'rabbit_jobs_test1', durable: true, auto_delete: false, ack: true, arguments: {'x-ha-policy' => 'all'}
|
12
|
+
c.queue 'rabbit_jobs_test2', durable: true, auto_delete: false, ack: true, arguments: {'x-ha-policy' => 'all'}
|
13
|
+
c.queue 'rabbit_jobs_test3', durable: true, auto_delete: false, ack: true, arguments: {'x-ha-policy' => 'all'}
|
14
|
+
end
|
15
|
+
|
16
|
+
puts JSON.pretty_generate(RabbitJobs.config.to_hash)
|
17
|
+
|
18
|
+
puts JSON.pretty_generate(RabbitJobs.config.queues)
|
19
|
+
|
20
|
+
# 10.times {
|
21
|
+
RabbitJobs.enqueue_to('rabbit_jobs_test1', Integer, 'to_s')
|
22
|
+
# }
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module RabbitJobs
|
4
|
+
module AmqpHelpers
|
5
|
+
|
6
|
+
# Calls given block with initialized amqp
|
7
|
+
|
8
|
+
def amqp_with_exchange(&block)
|
9
|
+
raise ArgumentError unless block
|
10
|
+
|
11
|
+
AMQP.start(host: RabbitJobs.config.host) do |connection|
|
12
|
+
channel = AMQP::Channel.new(connection)
|
13
|
+
|
14
|
+
channel.on_error do |ch, channel_close|
|
15
|
+
puts "Channel-level error: #{channel_close.reply_text}, shutting down..."
|
16
|
+
connection.close { EM.stop }
|
17
|
+
end
|
18
|
+
|
19
|
+
exchange = channel.direct(RabbitJobs.config[:exchange], RabbitJobs.config[:exchange_params])
|
20
|
+
|
21
|
+
# go work
|
22
|
+
block.call(connection, exchange)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def amqp_with_queue(routing_key, &block)
|
27
|
+
|
28
|
+
raise ArgumentError unless routing_key && block
|
29
|
+
|
30
|
+
amqp_with_exchange do |connection, exchange|
|
31
|
+
queue = exchange.channel.queue(RabbitJobs.config.queue_name(routing_key), RabbitJobs.config[:queues][routing_key])
|
32
|
+
queue.bind(exchange, :routing_key => routing_key)
|
33
|
+
|
34
|
+
# go work
|
35
|
+
block.call(connection, queue)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def make_queue(exchange, routing_key)
|
40
|
+
queue = exchange.channel.queue(RabbitJobs.config.queue_name(routing_key), RabbitJobs.config[:queues][routing_key])
|
41
|
+
queue.bind(exchange, :routing_key => routing_key)
|
42
|
+
queue
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module RabbitJobs
|
5
|
+
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def configure(&block)
|
9
|
+
@@configuration ||= Configuration.new
|
10
|
+
block.call(@@configuration)
|
11
|
+
end
|
12
|
+
|
13
|
+
def config
|
14
|
+
@@configuration ||= load_config
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_config
|
18
|
+
self.configure do |c|
|
19
|
+
c.host 'localhost'
|
20
|
+
c.exchange 'rabbit_jobs', auto_delete: false, durable: true
|
21
|
+
c.queue 'default', auto_delete: false, ack: true, durable: true
|
22
|
+
end
|
23
|
+
@@configuration
|
24
|
+
end
|
25
|
+
|
26
|
+
class Configuration
|
27
|
+
include Helpers
|
28
|
+
|
29
|
+
DEFAULT_QUEUE_PARAMS = {
|
30
|
+
auto_delete: false,
|
31
|
+
durable: true,
|
32
|
+
ack: true
|
33
|
+
}
|
34
|
+
|
35
|
+
DEFAULT_EXCHANGE_PARAMS = {
|
36
|
+
auto_delete: false,
|
37
|
+
durable: true
|
38
|
+
}
|
39
|
+
|
40
|
+
DEFAULT_MESSAGE_PARAMS = {
|
41
|
+
persistent: true,
|
42
|
+
nowait: false,
|
43
|
+
immediate: false
|
44
|
+
}
|
45
|
+
|
46
|
+
def to_hash
|
47
|
+
@data.dup
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
@data = {
|
52
|
+
host: 'localhost',
|
53
|
+
exchange: 'rabbit_jobs',
|
54
|
+
exchange_params: DEFAULT_EXCHANGE_PARAMS,
|
55
|
+
queues: {}
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def [](name)
|
60
|
+
@data[name]
|
61
|
+
end
|
62
|
+
|
63
|
+
def host(value = nil)
|
64
|
+
if value
|
65
|
+
raise ArgumentError unless value.is_a?(String) && value != ""
|
66
|
+
@data[:host] = value.to_s
|
67
|
+
else
|
68
|
+
@data[:host]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def exchange(value = nil, params = {})
|
73
|
+
if value
|
74
|
+
raise ArgumentError unless value.is_a?(String) && value != ""
|
75
|
+
@data[:exchange] = value
|
76
|
+
@data[:exchange_params] = DEFAULT_EXCHANGE_PARAMS.merge(params)
|
77
|
+
else
|
78
|
+
@data[:exchange]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def queue(name, params = {})
|
83
|
+
raise ArgumentError.new("name is #{name.inspect}") unless name && name.is_a?(String) && name != ""
|
84
|
+
raise ArgumentError.new("params is #{params.inspect}") unless params && params.is_a?(Hash)
|
85
|
+
|
86
|
+
if @data[:queues][name]
|
87
|
+
@data[:queues][name].merge!(params)
|
88
|
+
else
|
89
|
+
@data[:queues][name] = DEFAULT_QUEUE_PARAMS.merge(params)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def routing_keys
|
94
|
+
@data[:queues].keys
|
95
|
+
end
|
96
|
+
|
97
|
+
def queue_name(routing_key)
|
98
|
+
[@data[:exchange], routing_key].join('#')
|
99
|
+
end
|
100
|
+
|
101
|
+
def load_file(filename)
|
102
|
+
load_yaml(File.read(filename))
|
103
|
+
end
|
104
|
+
|
105
|
+
def load_yaml(text)
|
106
|
+
convert_yaml_config(YAML.load(text))
|
107
|
+
end
|
108
|
+
|
109
|
+
def convert_yaml_config(yaml)
|
110
|
+
if yaml['rabbit_jobs']
|
111
|
+
convert_yaml_config(yaml['rabbit_jobs'])
|
112
|
+
else
|
113
|
+
@data = {host: nil, exchange: nil, queues: {}}
|
114
|
+
host yaml['host']
|
115
|
+
exchange yaml['exchange'], symbolize_keys!(yaml['exchange_params'])
|
116
|
+
yaml['queues'].each do |name, params|
|
117
|
+
queue name, symbolize_keys!(params) || {}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module RabbitJobs
|
4
|
+
module Helpers
|
5
|
+
def symbolize_keys!(hash)
|
6
|
+
hash.inject({}) do |options, (key, value)|
|
7
|
+
options[(key.to_sym rescue key) || key] = value
|
8
|
+
options
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
# Tries to find a constant with the name specified in the argument string:
|
14
|
+
#
|
15
|
+
# constantize("Module") # => Module
|
16
|
+
# constantize("Test::Unit") # => Test::Unit
|
17
|
+
#
|
18
|
+
# The name is assumed to be the one of a top-level constant, no matter
|
19
|
+
# whether it starts with "::" or not. No lexical context is taken into
|
20
|
+
# account:
|
21
|
+
#
|
22
|
+
# C = 'outside'
|
23
|
+
# module M
|
24
|
+
# C = 'inside'
|
25
|
+
# C # => 'inside'
|
26
|
+
# constantize("C") # => 'outside', same as ::C
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# NameError is raised when the constant is unknown.
|
30
|
+
def constantize(camel_cased_word)
|
31
|
+
camel_cased_word = camel_cased_word.to_s
|
32
|
+
|
33
|
+
if camel_cased_word.include?('-')
|
34
|
+
camel_cased_word = classify(camel_cased_word)
|
35
|
+
end
|
36
|
+
|
37
|
+
names = camel_cased_word.split('::')
|
38
|
+
names.shift if names.empty? || names.first.empty?
|
39
|
+
|
40
|
+
constant = Object
|
41
|
+
names.each do |name|
|
42
|
+
args = Module.method(:const_get).arity != 1 ? [false] : []
|
43
|
+
|
44
|
+
if constant.const_defined?(name, *args)
|
45
|
+
constant = constant.const_get(name)
|
46
|
+
else
|
47
|
+
constant = constant.const_missing(name)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
constant
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module RabbitJobs
|
5
|
+
class Job
|
6
|
+
include RabbitJobs::Helpers
|
7
|
+
include Logger
|
8
|
+
|
9
|
+
attr_accessor :params, :klass, :child
|
10
|
+
|
11
|
+
def initialize(payload)
|
12
|
+
begin
|
13
|
+
self.params = JSON.parse(payload)
|
14
|
+
klass_name = params.delete_at(0)
|
15
|
+
self.klass = constantize(klass_name)
|
16
|
+
rescue
|
17
|
+
log "JOB INIT ERROR at #{Time.now.to_s}:"
|
18
|
+
log $!.inspect
|
19
|
+
log $!.backtrace
|
20
|
+
log "message: #{payload.inspect}"
|
21
|
+
# Mailer.send(klass_name, params, $!)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def perform
|
26
|
+
if @child = fork
|
27
|
+
srand # Reseeding
|
28
|
+
log! "Forked #{@child} at #{Time.now} to process #{klass}.perform(#{ params.map(&:inspect).join(', ') })"
|
29
|
+
Process.wait(@child)
|
30
|
+
yield if block_given?
|
31
|
+
else
|
32
|
+
begin
|
33
|
+
# log 'before perform'
|
34
|
+
klass.perform(*params)
|
35
|
+
# log 'after perform'
|
36
|
+
rescue
|
37
|
+
puts $!.inspect
|
38
|
+
end
|
39
|
+
exit!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RabbitJobs
|
2
|
+
module Logger
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def log(string)
|
6
|
+
puts string
|
7
|
+
end
|
8
|
+
|
9
|
+
def log!(string)
|
10
|
+
@@verbose ||= false
|
11
|
+
log(string) if RabbitJobs::Logger.verbose
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_accessor :verbose
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'amqp'
|
5
|
+
require 'eventmachine'
|
6
|
+
|
7
|
+
module RabbitJobs
|
8
|
+
module Publisher
|
9
|
+
extend self
|
10
|
+
extend AmqpHelpers
|
11
|
+
|
12
|
+
def enqueue(klass, *params)
|
13
|
+
key = RabbitJobs.config.routing_keys.first
|
14
|
+
enqueue_to(key, klass, *params)
|
15
|
+
end
|
16
|
+
|
17
|
+
def enqueue_to(routing_key, klass, *params)
|
18
|
+
raise ArgumentError unless klass && routing_key
|
19
|
+
|
20
|
+
payload = ([klass.to_s] + params).to_json
|
21
|
+
|
22
|
+
amqp_with_exchange do |connection, exchange|
|
23
|
+
|
24
|
+
queue = make_queue(exchange, routing_key)
|
25
|
+
|
26
|
+
exchange.publish(payload, Configuration::DEFAULT_MESSAGE_PARAMS.merge({routing_key: routing_key})) {
|
27
|
+
connection.close { EM.stop }
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# def spam
|
33
|
+
# payload = ([klass.to_s] + params).to_json
|
34
|
+
|
35
|
+
# amqp_with_exchange do |connection, exchange|
|
36
|
+
# 10000.times { |i| RabbitJobs.enqueue(RabbitJobs::TestJob, i) }
|
37
|
+
# exchange.publish(payload, RabbitJobs.config.publish_params.merge({routing_key: routing_key})) {
|
38
|
+
# connection.close { EM.stop }
|
39
|
+
# }
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
|
43
|
+
def purge_queue(routing_key)
|
44
|
+
raise ArgumentError unless routing_key
|
45
|
+
|
46
|
+
amqp_with_queue(routing_key) do |connection, queue|
|
47
|
+
queue.status do |number_of_messages, number_of_consumers|
|
48
|
+
queue.purge {
|
49
|
+
connection.close {
|
50
|
+
EM.stop
|
51
|
+
return number_of_messages
|
52
|
+
}
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# require 'resque/tasks'
|
2
|
+
# will give you the resque tasks
|
3
|
+
|
4
|
+
namespace :rj do
|
5
|
+
task :setup
|
6
|
+
|
7
|
+
desc "Start a Rabbit Jobs worker"
|
8
|
+
task :work => [ :preload, :setup ] do
|
9
|
+
require 'rabbit_jobs'
|
10
|
+
|
11
|
+
queues = (ENV['QUEUES'] || ENV['QUEUE']).to_s.split(',')
|
12
|
+
|
13
|
+
begin
|
14
|
+
worker = RabbitJobs::Worker.new(*queues)
|
15
|
+
RabbitJobs::Logger.verbose = true if ENV['VERBOSE']
|
16
|
+
# worker.very_verbose = ENV['VVERBOSE']
|
17
|
+
end
|
18
|
+
|
19
|
+
if ENV['BACKGROUND']
|
20
|
+
Process.daemon(true)
|
21
|
+
end
|
22
|
+
|
23
|
+
if ENV['PIDFILE']
|
24
|
+
File.open(ENV['PIDFILE'], 'w') { |f| f << worker.pid }
|
25
|
+
end
|
26
|
+
|
27
|
+
# worker.log "Starting worker #{worker.pid}"
|
28
|
+
# worker.verbose = true
|
29
|
+
worker.work 1
|
30
|
+
# worker.work(ENV['INTERVAL'] || 5) # interval, will block
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Start multiple Resque workers. Should only be used in dev mode."
|
34
|
+
task :workers do
|
35
|
+
threads = []
|
36
|
+
|
37
|
+
ENV['COUNT'].to_i.times do
|
38
|
+
threads << Thread.new do
|
39
|
+
system "rake resque:work"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
threads.each { |thread| thread.join }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Preload app files if this is Rails
|
47
|
+
task :preload => :setup do
|
48
|
+
if defined?(Rails) && Rails.respond_to?(:application)
|
49
|
+
# Rails 3
|
50
|
+
Rails.application.eager_load!
|
51
|
+
elsif defined?(Rails::Initializer)
|
52
|
+
# Rails 2.3
|
53
|
+
$rails_rake_task = false
|
54
|
+
Rails::Initializer.run :load_application_classes
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/rabbit_jobs/version.rb
CHANGED
@@ -0,0 +1,104 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module RabbitJobs
|
4
|
+
class Worker
|
5
|
+
include AmqpHelpers
|
6
|
+
include Logger
|
7
|
+
|
8
|
+
# Workers should be initialized with an array of string queue
|
9
|
+
# names. The order is important: a Worker will check the first
|
10
|
+
# queue given for a job. If none is found, it will check the
|
11
|
+
# second queue name given. If a job is found, it will be
|
12
|
+
# processed. Upon completion, the Worker will again check the
|
13
|
+
# first queue given, and so forth. In this way the queue list
|
14
|
+
# passed to a Worker on startup defines the priorities of queues.
|
15
|
+
#
|
16
|
+
# If passed a single "*", this Worker will operate on all queues
|
17
|
+
# in alphabetical order. Queues can be dynamically added or
|
18
|
+
# removed without needing to restart workers using this method.
|
19
|
+
def initialize(*queues)
|
20
|
+
@queues = queues.map { |queue| queue.to_s.strip }.flatten.uniq
|
21
|
+
if @queues == ['*'] || @queues.empty?
|
22
|
+
@queues = RabbitJobs.config.routing_keys
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def queues
|
27
|
+
@queues || ['default']
|
28
|
+
end
|
29
|
+
|
30
|
+
# Subscribes to channel and working on jobs
|
31
|
+
def work(time = 10)
|
32
|
+
startup
|
33
|
+
|
34
|
+
processed_count = 0
|
35
|
+
amqp_with_exchange do |connection, exchange|
|
36
|
+
exchange.channel.prefetch(1)
|
37
|
+
|
38
|
+
check_shutdown = Proc.new {
|
39
|
+
if @shutdown
|
40
|
+
log "Processed jobs: #{processed_count}"
|
41
|
+
log "Stopping worker..."
|
42
|
+
connection.close { EM.stop { exit! } }
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
queues.each do |routing_key|
|
47
|
+
queue = make_queue(exchange, routing_key)
|
48
|
+
|
49
|
+
log "Worker ##{Process.pid} <= #{exchange.name}##{routing_key}"
|
50
|
+
|
51
|
+
queue.subscribe(ack: true) do |metadata, payload|
|
52
|
+
@job = Job.new(payload)
|
53
|
+
@job.perform
|
54
|
+
metadata.ack
|
55
|
+
processed_count += 1
|
56
|
+
check_shutdown.call
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
EM.add_timer(time) do
|
61
|
+
self.shutdown
|
62
|
+
end
|
63
|
+
|
64
|
+
EM.add_periodic_timer(1) do
|
65
|
+
check_shutdown.call
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def shutdown
|
71
|
+
@shutdown = true
|
72
|
+
end
|
73
|
+
|
74
|
+
def startup
|
75
|
+
# prune_dead_workers
|
76
|
+
|
77
|
+
# Fix buffering so we can `rake rj:work > resque.log` and
|
78
|
+
# get output from the child in there.
|
79
|
+
$stdout.sync = true
|
80
|
+
|
81
|
+
@shutdown = false
|
82
|
+
|
83
|
+
Signal.trap('TERM') { shutdown }
|
84
|
+
Signal.trap('INT') { shutdown! }
|
85
|
+
end
|
86
|
+
|
87
|
+
def shutdown!
|
88
|
+
shutdown
|
89
|
+
kill_child
|
90
|
+
end
|
91
|
+
|
92
|
+
def kill_child
|
93
|
+
if @job && @job.child
|
94
|
+
# log! "Killing child at #{@child}"
|
95
|
+
if Kernel.system("ps -o pid,state -p #{@job.child}")
|
96
|
+
Process.kill("KILL", @job.child) rescue nil
|
97
|
+
else
|
98
|
+
# log! "Child #{@child} not found, restarting."
|
99
|
+
# shutdown
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/rabbit_jobs.rb
CHANGED
@@ -1,5 +1,36 @@
|
|
1
|
-
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'rabbit_jobs/version'
|
4
|
+
|
5
|
+
require 'rabbit_jobs/helpers'
|
6
|
+
require 'rabbit_jobs/amqp_helpers'
|
7
|
+
require 'rabbit_jobs/configuration'
|
8
|
+
require 'rabbit_jobs/logger'
|
9
|
+
|
10
|
+
require 'rabbit_jobs/job'
|
11
|
+
require 'rabbit_jobs/publisher'
|
12
|
+
require 'rabbit_jobs/worker'
|
2
13
|
|
3
14
|
module RabbitJobs
|
4
|
-
|
5
|
-
|
15
|
+
extend self
|
16
|
+
|
17
|
+
def enqueue(klass, *params)
|
18
|
+
RabbitJobs::Publisher.enqueue(klass, *params)
|
19
|
+
end
|
20
|
+
|
21
|
+
def enqueue_to(routing_key, klass, *params)
|
22
|
+
RabbitJobs::Publisher.enqueue_to(routing_key, klass, *params)
|
23
|
+
end
|
24
|
+
|
25
|
+
class TestJob < RabbitJobs::Job
|
26
|
+
def self.perform(*params)
|
27
|
+
# puts "processing in job: " + params.inspect
|
28
|
+
# sleep 0.1
|
29
|
+
|
30
|
+
# if rand(3) == 0
|
31
|
+
# puts "ERROR TEXT"
|
32
|
+
# raise "ERROR TEXT"
|
33
|
+
# end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/rabbit_jobs.gemspec
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'rabbit_jobs/version'
|
3
4
|
|
4
5
|
Gem::Specification.new do |gem|
|
5
6
|
gem.authors = ["Pavel Lazureykis"]
|
@@ -7,6 +8,7 @@ Gem::Specification.new do |gem|
|
|
7
8
|
gem.description = %q{Background jobs on RabbitMQ}
|
8
9
|
gem.summary = %q{Background jobs on RabbitMQ}
|
9
10
|
gem.homepage = ""
|
11
|
+
gem.date = Time.now.strftime('%Y-%m-%d')
|
10
12
|
|
11
13
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
14
|
gem.files = `git ls-files`.split("\n")
|
@@ -15,7 +17,5 @@ Gem::Specification.new do |gem|
|
|
15
17
|
gem.require_paths = ["lib"]
|
16
18
|
gem.version = RabbitJobs::VERSION
|
17
19
|
|
18
|
-
gem.add_development_dependency "rspec", "~> 2.8"
|
19
|
-
|
20
20
|
gem.add_dependency "amqp", "~> 0.9"
|
21
21
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
rabbit_jobs:
|
2
|
+
host: example.com
|
3
|
+
exchange: 'my_exchange'
|
4
|
+
exchange_params:
|
5
|
+
durable: true
|
6
|
+
auto_delete: false
|
7
|
+
queues:
|
8
|
+
durable_queue:
|
9
|
+
durable: true
|
10
|
+
auto_delete: false
|
11
|
+
ack: true
|
12
|
+
arguments:
|
13
|
+
x-ha-policy: all
|
14
|
+
fast_queue:
|
15
|
+
durable: false
|
16
|
+
auto_delete: true
|
17
|
+
ack: false
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
describe RabbitJobs::Publisher do
|
6
|
+
it 'should publish message to queue' do
|
7
|
+
|
8
|
+
RabbitJobs.configure do |c|
|
9
|
+
# c.host "somehost.lan"
|
10
|
+
c.exchange 'test', auto_delete: true
|
11
|
+
c.queue 'rspec_queue', auto_delete: true
|
12
|
+
end
|
13
|
+
|
14
|
+
RabbitJobs.enqueue(Integer, 'some', 'other', 'params')
|
15
|
+
RabbitJobs::Publisher.purge_queue('rspec_queue').should == 1
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start {
|
4
|
+
add_filter "spec" # ignore gems
|
5
|
+
}
|
6
|
+
|
7
|
+
require 'rr'
|
8
|
+
|
9
|
+
require 'rabbit_jobs'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.mock_with :rr
|
13
|
+
# or if that doesn't work due to a version incompatibility
|
14
|
+
# config.mock_with RR::Adapters::Rspec
|
15
|
+
|
16
|
+
config.before(:each) do
|
17
|
+
# clear config options
|
18
|
+
RabbitJobs.class_variable_set '@@configuration', nil
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe RabbitJobs::Configuration do
|
5
|
+
it 'builds configuration from configure block' do
|
6
|
+
RabbitJobs.configure do |c|
|
7
|
+
c.host "somehost.lan"
|
8
|
+
|
9
|
+
c.exchange 'my_exchange', durable: true, auto_delete: false
|
10
|
+
|
11
|
+
c.queue 'durable_queue', durable: true, auto_delete: false, ack: true, arguments: {'x-ha-policy' => 'all'}
|
12
|
+
c.queue 'fast_queue', durable: false, auto_delete: true, ack: false
|
13
|
+
end
|
14
|
+
|
15
|
+
RabbitJobs.config.to_hash.should == {
|
16
|
+
host: "somehost.lan",
|
17
|
+
exchange: "my_exchange",
|
18
|
+
exchange_params: {
|
19
|
+
durable: true,
|
20
|
+
auto_delete: false
|
21
|
+
},
|
22
|
+
queues: {
|
23
|
+
"durable_queue" => {
|
24
|
+
durable: true,
|
25
|
+
auto_delete: false,
|
26
|
+
ack: true,
|
27
|
+
arguments: {"x-ha-policy"=>"all"}
|
28
|
+
},
|
29
|
+
"fast_queue" => {
|
30
|
+
durable: false,
|
31
|
+
auto_delete: true,
|
32
|
+
ack: false
|
33
|
+
},
|
34
|
+
}
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'builds configuration from yaml' do
|
39
|
+
RabbitJobs.config.load_file(File.expand_path('../../fixtures/config.yml', __FILE__))
|
40
|
+
|
41
|
+
RabbitJobs.config.to_hash.should == {
|
42
|
+
host: "example.com",
|
43
|
+
exchange: "my_exchange",
|
44
|
+
exchange_params: {
|
45
|
+
durable: true,
|
46
|
+
auto_delete: false
|
47
|
+
},
|
48
|
+
queues: {
|
49
|
+
"durable_queue" => {
|
50
|
+
durable: true,
|
51
|
+
auto_delete: false,
|
52
|
+
ack: true,
|
53
|
+
arguments: {"x-ha-policy"=>"all"}
|
54
|
+
},
|
55
|
+
"fast_queue" => {
|
56
|
+
durable: false,
|
57
|
+
auto_delete: true,
|
58
|
+
ack: false
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'use default config' do
|
65
|
+
RabbitJobs.config.to_hash.should == {
|
66
|
+
host: "localhost",
|
67
|
+
exchange: "rabbit_jobs",
|
68
|
+
exchange_params: {
|
69
|
+
auto_delete: false,
|
70
|
+
durable: true
|
71
|
+
},
|
72
|
+
queues: {
|
73
|
+
"default" => {
|
74
|
+
auto_delete: false,
|
75
|
+
ack: true,
|
76
|
+
durable: true
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns settings on some methods' do
|
83
|
+
RabbitJobs.config.host.should == 'localhost'
|
84
|
+
RabbitJobs.config[:host].should == 'localhost'
|
85
|
+
RabbitJobs.config.routing_keys.should == ['default']
|
86
|
+
RabbitJobs.config.exchange.should == 'rabbit_jobs'
|
87
|
+
RabbitJobs.config.queue_name('default').should == 'rabbit_jobs#default'
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe RabbitJobs::Logger do
|
5
|
+
it '#log should write messages to stdout' do
|
6
|
+
mock($stdout).puts "hello"
|
7
|
+
RabbitJobs::Logger.log("hello")
|
8
|
+
end
|
9
|
+
|
10
|
+
it '#log! should not write messages to stdout in normal mode' do
|
11
|
+
RabbitJobs::Logger.verbose = false
|
12
|
+
|
13
|
+
dont_allow(RabbitJobs::Logger).log("hello")
|
14
|
+
RabbitJobs::Logger.log!("hello")
|
15
|
+
end
|
16
|
+
|
17
|
+
it '#log! should write messages to stdout in verbose mode' do
|
18
|
+
RabbitJobs::Logger.verbose = true
|
19
|
+
|
20
|
+
mock(RabbitJobs::Logger).log("hello")
|
21
|
+
RabbitJobs::Logger.log!("hello")
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
describe RabbitJobs do
|
4
|
+
it 'should pass enqueue methods to publisher' do
|
5
|
+
mock(RabbitJobs::Publisher).enqueue(Integer, 1, 2, "string")
|
6
|
+
RabbitJobs.enqueue(Integer, 1, 2, "string")
|
7
|
+
|
8
|
+
mock(RabbitJobs::Publisher).enqueue_to('default_queue', Integer, 1, 2, "string")
|
9
|
+
RabbitJobs.enqueue_to('default_queue', Integer, 1, 2, "string")
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe RabbitJobs::Worker do
|
5
|
+
describe 'methods' do
|
6
|
+
before :each do
|
7
|
+
@worker = RabbitJobs::Worker.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it '#initialize with default options' do
|
11
|
+
@worker.queues.should == ['default']
|
12
|
+
end
|
13
|
+
|
14
|
+
it '#startup' do
|
15
|
+
@worker.instance_variable_get('@shutdown').should_not == true
|
16
|
+
|
17
|
+
mock(Signal).trap('TERM')
|
18
|
+
mock(Signal).trap('INT')
|
19
|
+
|
20
|
+
@worker.startup
|
21
|
+
|
22
|
+
@worker.instance_variable_get('@shutdown').should_not == true
|
23
|
+
end
|
24
|
+
|
25
|
+
it '#shutdown should set @shutdown to true' do
|
26
|
+
@worker.instance_variable_get('@shutdown').should_not == true
|
27
|
+
@worker.shutdown
|
28
|
+
@worker.instance_variable_get('@shutdown').should == true
|
29
|
+
end
|
30
|
+
|
31
|
+
it '#shutdown! should kill child process' do
|
32
|
+
mock(@worker.kill_child)
|
33
|
+
mock(@worker.shutdown)
|
34
|
+
|
35
|
+
@worker.shutdown!
|
36
|
+
end
|
37
|
+
|
38
|
+
it '#kill_child' do
|
39
|
+
job = RabbitJobs::Job.new(['RabbitJobs'].to_json)
|
40
|
+
job.instance_variable_set '@child', 123123
|
41
|
+
@worker.instance_variable_set('@job', job)
|
42
|
+
|
43
|
+
mock(Kernel).system("ps -o pid,state -p #{123123}") { true }
|
44
|
+
mock(Process).kill("KILL", 123123)
|
45
|
+
|
46
|
+
@worker.kill_child
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rabbit_jobs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,22 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-01-
|
12
|
+
date: 2012-01-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: rspec
|
16
|
-
requirement: &70100539559980 !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ~>
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '2.8'
|
22
|
-
type: :development
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: *70100539559980
|
25
14
|
- !ruby/object:Gem::Dependency
|
26
15
|
name: amqp
|
27
|
-
requirement: &
|
16
|
+
requirement: &70147046160300 !ruby/object:Gem::Requirement
|
28
17
|
none: false
|
29
18
|
requirements:
|
30
19
|
- - ~>
|
@@ -32,7 +21,7 @@ dependencies:
|
|
32
21
|
version: '0.9'
|
33
22
|
type: :runtime
|
34
23
|
prerelease: false
|
35
|
-
version_requirements: *
|
24
|
+
version_requirements: *70147046160300
|
36
25
|
description: Background jobs on RabbitMQ
|
37
26
|
email:
|
38
27
|
- lazureykis@gmail.com
|
@@ -41,13 +30,31 @@ extensions: []
|
|
41
30
|
extra_rdoc_files: []
|
42
31
|
files:
|
43
32
|
- .gitignore
|
33
|
+
- .rspec
|
44
34
|
- Gemfile
|
45
35
|
- LICENSE
|
46
36
|
- README.md
|
47
37
|
- Rakefile
|
38
|
+
- examples/configuration.rb
|
48
39
|
- lib/rabbit_jobs.rb
|
40
|
+
- lib/rabbit_jobs/amqp_helpers.rb
|
41
|
+
- lib/rabbit_jobs/configuration.rb
|
42
|
+
- lib/rabbit_jobs/helpers.rb
|
43
|
+
- lib/rabbit_jobs/job.rb
|
44
|
+
- lib/rabbit_jobs/logger.rb
|
45
|
+
- lib/rabbit_jobs/publisher.rb
|
46
|
+
- lib/rabbit_jobs/tasks.rb
|
49
47
|
- lib/rabbit_jobs/version.rb
|
48
|
+
- lib/rabbit_jobs/worker.rb
|
49
|
+
- lib/tasks/rabbit_jobs.rake
|
50
50
|
- rabbit_jobs.gemspec
|
51
|
+
- spec/fixtures/config.yml
|
52
|
+
- spec/integration/publisher_spec.rb
|
53
|
+
- spec/spec_helper.rb
|
54
|
+
- spec/unit/configuration_spec.rb
|
55
|
+
- spec/unit/logger_spec.rb
|
56
|
+
- spec/unit/rabbit_jobs_spec.rb
|
57
|
+
- spec/unit/worker_spec.rb
|
51
58
|
homepage: ''
|
52
59
|
licenses: []
|
53
60
|
post_install_message:
|
@@ -72,4 +79,11 @@ rubygems_version: 1.8.15
|
|
72
79
|
signing_key:
|
73
80
|
specification_version: 3
|
74
81
|
summary: Background jobs on RabbitMQ
|
75
|
-
test_files:
|
82
|
+
test_files:
|
83
|
+
- spec/fixtures/config.yml
|
84
|
+
- spec/integration/publisher_spec.rb
|
85
|
+
- spec/spec_helper.rb
|
86
|
+
- spec/unit/configuration_spec.rb
|
87
|
+
- spec/unit/logger_spec.rb
|
88
|
+
- spec/unit/rabbit_jobs_spec.rb
|
89
|
+
- spec/unit/worker_spec.rb
|