rabbit_jobs 0.0.1 → 0.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.
- 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
|