sneakers 0.1.1.pre → 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/LICENSE.txt +2 -2
- data/Rakefile +1 -0
- data/examples/max_retry_handler.rb +78 -0
- data/examples/newrelic_metrics_worker.rb +40 -0
- data/lib/sneakers/cli.rb +3 -3
- data/lib/sneakers/concerns/logging.rb +1 -1
- data/lib/sneakers/configuration.rb +47 -0
- data/lib/sneakers/handlers/maxretry.rb +183 -0
- data/lib/sneakers/handlers/oneshot.rb +11 -10
- data/lib/sneakers/publisher.rb +11 -10
- data/lib/sneakers/queue.rb +16 -7
- data/lib/sneakers/runner.rb +4 -4
- data/lib/sneakers/version.rb +1 -1
- data/lib/sneakers/worker.rb +38 -19
- data/lib/sneakers/workergroup.rb +2 -2
- data/lib/sneakers.rb +23 -48
- data/sneakers.gemspec +22 -22
- data/spec/sneakers/concerns/{logging.rb → logging_spec.rb} +1 -1
- data/spec/sneakers/concerns/{metrics.rb → metrics_spec.rb} +0 -0
- data/spec/sneakers/configuration_spec.rb +42 -0
- data/spec/sneakers/publisher_spec.rb +46 -22
- data/spec/sneakers/queue_spec.rb +32 -4
- data/spec/sneakers/sneakers_spec.rb +7 -6
- data/spec/sneakers/worker_handlers_spec.rb +350 -0
- data/spec/sneakers/worker_spec.rb +85 -48
- data/spec/spec_helper.rb +3 -1
- metadata +46 -64
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9d39e1dc0f9c583d63d7ba5d6cf8078efe94f93f
|
4
|
+
data.tar.gz: 7bf1d48e4a492d454db3afc7474b9053adc05531
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d7c50e77e8a9de0e4ebed4d6120e78c5b5438d4d430ed776a686a69e6a288f000a4a1f40cf9a0bb5ab861bc7e5cb8fc93a5d8241301f15888b5ac3682447ae58
|
7
|
+
data.tar.gz: 80942f71ebc85d9365ada65413ec16f0f3444631e568f930de6279d6924301f74745336449e72a7da1963b01e0945fa0d155654164b7f3af44e22c9b6851def0
|
data/.gitignore
CHANGED
data/LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2013 Dotan Nahum
|
1
|
+
Copyright (c) 2013-2014 Dotan Nahum
|
2
2
|
|
3
3
|
MIT License
|
4
4
|
|
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -0,0 +1,78 @@
|
|
1
|
+
$: << File.expand_path('../lib', File.dirname(__FILE__))
|
2
|
+
require 'sneakers'
|
3
|
+
require 'sneakers/runner'
|
4
|
+
require 'sneakers/handlers/maxretry'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
Sneakers.configure(:handler => Sneakers::Handlers::Maxretry,
|
8
|
+
:workers => 1,
|
9
|
+
:threads => 1,
|
10
|
+
:prefetch => 1,
|
11
|
+
:exchange => 'sneakers',
|
12
|
+
:exchange_type => 'topic',
|
13
|
+
:routing_key => ['#', 'something'],
|
14
|
+
:durable => true,
|
15
|
+
)
|
16
|
+
Sneakers.logger.level = Logger::DEBUG
|
17
|
+
|
18
|
+
WORKER_OPTIONS = {
|
19
|
+
:ack => true,
|
20
|
+
:threads => 1,
|
21
|
+
:prefetch => 1,
|
22
|
+
:timeout_job_after => 60,
|
23
|
+
:heartbeat => 5,
|
24
|
+
:retry_timeout => 5000
|
25
|
+
}
|
26
|
+
|
27
|
+
# Example of how to write a retry worker. If your rabbit system is empty, then
|
28
|
+
# you must run this twice. Once to setup the exchanges, queues and bindings a
|
29
|
+
# second time to have the sent message end up on the downloads queue.
|
30
|
+
#
|
31
|
+
# Run this via:
|
32
|
+
# bundle exec ruby examples/max_retry_handler.rb
|
33
|
+
#
|
34
|
+
class MaxRetryWorker
|
35
|
+
include Sneakers::Worker
|
36
|
+
from_queue 'downloads',
|
37
|
+
WORKER_OPTIONS.merge({
|
38
|
+
:arguments => {
|
39
|
+
:'x-dead-letter-exchange' => 'downloads-retry'
|
40
|
+
},
|
41
|
+
})
|
42
|
+
|
43
|
+
def work(msg)
|
44
|
+
logger.info("MaxRetryWorker rejecting msg: #{msg.inspect}")
|
45
|
+
|
46
|
+
# We always want to reject to see if we do the proper timeout
|
47
|
+
reject!
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Example of a worker on the same exchange that does not fail, so it should only
|
52
|
+
# see the message once.
|
53
|
+
class SucceedingWorker
|
54
|
+
include Sneakers::Worker
|
55
|
+
from_queue 'uploads',
|
56
|
+
WORKER_OPTIONS.merge({
|
57
|
+
:arguments => {
|
58
|
+
:'x-dead-letter-exchange' => 'uploads-retry'
|
59
|
+
},
|
60
|
+
})
|
61
|
+
|
62
|
+
def work(msg)
|
63
|
+
logger.info("SucceedingWorker succeeding on msg: #{msg.inspect}")
|
64
|
+
ack!
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
messages = 1
|
69
|
+
puts "feeding messages in"
|
70
|
+
messages.times {
|
71
|
+
Sneakers.publish(" -- message -- ",
|
72
|
+
:to_queue => 'anywhere',
|
73
|
+
:persistence => true)
|
74
|
+
}
|
75
|
+
puts "done"
|
76
|
+
|
77
|
+
r = Sneakers::Runner.new([MaxRetryWorker, SucceedingWorker])
|
78
|
+
r.run
|
@@ -0,0 +1,40 @@
|
|
1
|
+
$: << File.expand_path('../lib', File.dirname(__FILE__))
|
2
|
+
require 'sneakers'
|
3
|
+
require 'sneakers/runner'
|
4
|
+
require 'sneakers/metrics/newrelic_metrics'
|
5
|
+
require 'open-uri'
|
6
|
+
require 'nokogiri'
|
7
|
+
require 'newrelic_rpm'
|
8
|
+
|
9
|
+
# With this configuration will send two types of data to newrelic server:
|
10
|
+
# 1. Transaction data which you would see under 'Applications'
|
11
|
+
# 2. Metrics where you will be able to see by configuring a dashboardi, available for enterprise accounts
|
12
|
+
#
|
13
|
+
# You should have newrelic.yml in the 'config' folder with the proper account settings
|
14
|
+
|
15
|
+
Sneakers::Metrics::NewrelicMetrics.eagent ::NewRelic
|
16
|
+
Sneakers.configure metrics: Sneakers::Metrics::NewrelicMetrics.new
|
17
|
+
|
18
|
+
class MetricsWorker
|
19
|
+
include Sneakers::Worker
|
20
|
+
include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
|
21
|
+
|
22
|
+
from_queue 'downloads'
|
23
|
+
|
24
|
+
def work(msg)
|
25
|
+
doc = Nokogiri::HTML(open(msg))
|
26
|
+
logger.info "FOUND <#{doc.css('title').text}>"
|
27
|
+
ack!
|
28
|
+
end
|
29
|
+
|
30
|
+
add_transaction_tracer :work, name: 'MetricsWorker', params: 'args[0]'
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
r = Sneakers::Runner.new([ MetricsWorker ])
|
36
|
+
r.run
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
|
data/lib/sneakers/cli.rb
CHANGED
@@ -66,11 +66,11 @@ module Sneakers
|
|
66
66
|
|
67
67
|
r = Sneakers::Runner.new(workers)
|
68
68
|
|
69
|
-
pid = Sneakers::
|
69
|
+
pid = Sneakers::CONFIG[:pid_path]
|
70
70
|
|
71
71
|
say SNEAKERS
|
72
72
|
say "Workers ....: #{em workers.join(', ')}"
|
73
|
-
say "Log ........: #{em (Sneakers::
|
73
|
+
say "Log ........: #{em (Sneakers::CONFIG[:log] == STDOUT ? 'Console' : Sneakers::CONFIG[:log]) }"
|
74
74
|
say "PID ........: #{em pid}"
|
75
75
|
say ""
|
76
76
|
say (" "*31)+"Process control"
|
@@ -86,7 +86,7 @@ module Sneakers
|
|
86
86
|
|
87
87
|
if options[:debug]
|
88
88
|
say "==== configuration ==="
|
89
|
-
say Sneakers::
|
89
|
+
say Sneakers::CONFIG.inspect
|
90
90
|
say "======================"
|
91
91
|
end
|
92
92
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Sneakers
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
extend Forwardable
|
5
|
+
def_delegators :@hash, :to_hash, :[], :[]=, :merge!, :==, :fetch, :delete
|
6
|
+
|
7
|
+
DEFAULTS = {
|
8
|
+
# runner
|
9
|
+
:runner_config_file => nil,
|
10
|
+
:metrics => nil,
|
11
|
+
:daemonize => false,
|
12
|
+
:start_worker_delay => 0.2,
|
13
|
+
:workers => 4,
|
14
|
+
:log => STDOUT,
|
15
|
+
:pid_path => 'sneakers.pid',
|
16
|
+
|
17
|
+
# workers
|
18
|
+
:timeout_job_after => 5,
|
19
|
+
:prefetch => 10,
|
20
|
+
:threads => 10,
|
21
|
+
:durable => true,
|
22
|
+
:ack => true,
|
23
|
+
:heartbeat => 2,
|
24
|
+
:exchange => 'sneakers',
|
25
|
+
:exchange_type => :direct,
|
26
|
+
:hooks => {}
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
clear
|
32
|
+
end
|
33
|
+
|
34
|
+
def clear
|
35
|
+
@hash = DEFAULTS.dup
|
36
|
+
@hash[:amqp] = ENV.fetch('RABBITMQ_URL', 'amqp://guest:guest@localhost:5672')
|
37
|
+
@hash[:vhost] = AMQ::Settings.parse_amqp_url(@hash[:amqp]).fetch(:vhost, '/')
|
38
|
+
end
|
39
|
+
|
40
|
+
def merge(hash)
|
41
|
+
instance = self.class.new
|
42
|
+
instance.merge! to_hash
|
43
|
+
instance.merge! hash
|
44
|
+
instance
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Sneakers
|
5
|
+
module Handlers
|
6
|
+
#
|
7
|
+
# Maxretry uses dead letter policies on Rabbitmq to requeue and retry
|
8
|
+
# messages after failure (rejections, errors and timeouts). When the maximum
|
9
|
+
# number of retries is reached it will put the message on an error queue.
|
10
|
+
# This handler will only retry at the queue level. To accomplish that, the
|
11
|
+
# setup is a bit complex.
|
12
|
+
#
|
13
|
+
# Input:
|
14
|
+
# worker_exchange (eXchange)
|
15
|
+
# worker_queue (Queue)
|
16
|
+
# We create:
|
17
|
+
# worker_queue-retry - (X) where we setup the worker queue to dead-letter.
|
18
|
+
# worker_queue-retry - (Q) queue bound to ^ exchange, dead-letters to
|
19
|
+
# worker_queue-retry-requeue.
|
20
|
+
# worker_queue-error - (X) where to send max-retry failures
|
21
|
+
# worker_queue-error - (Q) bound to worker_queue-error.
|
22
|
+
# worker_queue-retry-requeue - (X) exchange to bind worker_queue to for
|
23
|
+
# requeuing directly to the worker_queue.
|
24
|
+
#
|
25
|
+
# This requires that you setup arguments to the worker queue to line up the
|
26
|
+
# dead letter queue. See the example for more information.
|
27
|
+
#
|
28
|
+
# Many of these can be override with options:
|
29
|
+
# - retry_exchange - sets retry exchange & queue
|
30
|
+
# - retry_error_exchange - sets error exchange and queue
|
31
|
+
# - retry_requeue_exchange - sets the exchange created to re-queue things
|
32
|
+
# back to the worker queue.
|
33
|
+
#
|
34
|
+
class Maxretry
|
35
|
+
|
36
|
+
def initialize(channel, queue, opts)
|
37
|
+
@worker_queue_name = queue.name
|
38
|
+
Sneakers.logger.debug do
|
39
|
+
"#{log_prefix} creating handler, opts=#{opts}"
|
40
|
+
end
|
41
|
+
|
42
|
+
@channel = channel
|
43
|
+
@opts = opts
|
44
|
+
|
45
|
+
# Construct names, defaulting where suitable
|
46
|
+
retry_name = @opts[:retry_exchange] || "#{@worker_queue_name}-retry"
|
47
|
+
error_name = @opts[:retry_error_exchange] || "#{@worker_queue_name}-error"
|
48
|
+
requeue_name = @opts[:retry_requeue_exchange] || "#{@worker_queue_name}-retry-requeue"
|
49
|
+
|
50
|
+
# Create the exchanges
|
51
|
+
@retry_exchange, @error_exchange, @requeue_exchange = [retry_name, error_name, requeue_name].map do |name|
|
52
|
+
Sneakers.logger.debug { "#{log_prefix} creating exchange=#{name}" }
|
53
|
+
@channel.exchange(name,
|
54
|
+
:type => 'topic',
|
55
|
+
:durable => opts[:durable])
|
56
|
+
end
|
57
|
+
|
58
|
+
# Create the queues and bindings
|
59
|
+
Sneakers.logger.debug do
|
60
|
+
"#{log_prefix} creating queue=#{retry_name} x-dead-letter-exchange=#{requeue_name}"
|
61
|
+
end
|
62
|
+
@retry_queue = @channel.queue(retry_name,
|
63
|
+
:durable => opts[:durable],
|
64
|
+
:arguments => {
|
65
|
+
:'x-dead-letter-exchange' => requeue_name,
|
66
|
+
:'x-message-ttl' => @opts[:retry_timeout] || 60000
|
67
|
+
})
|
68
|
+
@retry_queue.bind(@retry_exchange, :routing_key => '#')
|
69
|
+
|
70
|
+
Sneakers.logger.debug do
|
71
|
+
"#{log_prefix} creating queue=#{error_name}"
|
72
|
+
end
|
73
|
+
@error_queue = @channel.queue(error_name,
|
74
|
+
:durable => opts[:durable])
|
75
|
+
@error_queue.bind(@error_exchange, :routing_key => '#')
|
76
|
+
|
77
|
+
# Finally, bind the worker queue to our requeue exchange
|
78
|
+
queue.bind(@requeue_exchange, :routing_key => '#')
|
79
|
+
|
80
|
+
@max_retries = @opts[:retry_max_times] || 5
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
def acknowledge(hdr, props, msg)
|
85
|
+
@channel.acknowledge(hdr.delivery_tag, false)
|
86
|
+
end
|
87
|
+
|
88
|
+
def reject(hdr, props, msg, requeue = false)
|
89
|
+
if requeue
|
90
|
+
# This was explicitly rejected specifying it be requeued so we do not
|
91
|
+
# want it to pass through our retry logic.
|
92
|
+
@channel.reject(hdr.delivery_tag, requeue)
|
93
|
+
else
|
94
|
+
handle_retry(hdr, props, msg, :reject)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def error(hdr, props, msg, err)
|
100
|
+
handle_retry(hdr, props, msg, err)
|
101
|
+
end
|
102
|
+
|
103
|
+
def timeout(hdr, props, msg)
|
104
|
+
handle_retry(hdr, props, msg, :timeout)
|
105
|
+
end
|
106
|
+
|
107
|
+
def noop(hdr, props, msg)
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
# Helper logic for retry handling. This will reject the message if there
|
112
|
+
# are remaining retries left on it, otherwise it will publish it to the
|
113
|
+
# error exchange along with the reason.
|
114
|
+
# @param hdr [Bunny::DeliveryInfo]
|
115
|
+
# @param props [Bunny::MessageProperties]
|
116
|
+
# @param msg [String] The message
|
117
|
+
# @param reason [String, Symbol, Exception] Reason for the retry, included
|
118
|
+
# in the JSON we put on the error exchange.
|
119
|
+
def handle_retry(hdr, props, msg, reason)
|
120
|
+
# +1 for the current attempt
|
121
|
+
num_attempts = failure_count(props[:headers]) + 1
|
122
|
+
if num_attempts <= @max_retries
|
123
|
+
# We call reject which will route the message to the
|
124
|
+
# x-dead-letter-exchange (ie. retry exchange) on the queue
|
125
|
+
Sneakers.logger.info do
|
126
|
+
"#{log_prefix} msg=retrying, count=#{num_attempts}, headers=#{props[:headers]}"
|
127
|
+
end
|
128
|
+
@channel.reject(hdr.delivery_tag, false)
|
129
|
+
# TODO: metrics
|
130
|
+
else
|
131
|
+
# Retried more than the max times
|
132
|
+
# Publish the original message with the routing_key to the error exchange
|
133
|
+
Sneakers.logger.info do
|
134
|
+
"#{log_prefix} msg=failing, retry_count=#{num_attempts}, reason=#{reason}"
|
135
|
+
end
|
136
|
+
data = {
|
137
|
+
error: reason,
|
138
|
+
num_attempts: num_attempts,
|
139
|
+
failed_at: Time.now.iso8601,
|
140
|
+
payload: Base64.encode64(msg.to_s)
|
141
|
+
}.tap do |hash|
|
142
|
+
if reason.is_a?(Exception)
|
143
|
+
hash[:error_class] = reason.class
|
144
|
+
if reason.backtrace
|
145
|
+
hash[:backtrace] = reason.backtrace.take(10).join(', ')
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end.to_json
|
149
|
+
@error_exchange.publish(data, :routing_key => hdr.routing_key)
|
150
|
+
@channel.acknowledge(hdr.delivery_tag, false)
|
151
|
+
# TODO: metrics
|
152
|
+
end
|
153
|
+
end
|
154
|
+
private :handle_retry
|
155
|
+
|
156
|
+
# Uses the x-death header to determine the number of failures this job has
|
157
|
+
# seen in the past. This does not count the current failure. So for
|
158
|
+
# instance, the first time the job fails, this will return 0, the second
|
159
|
+
# time, 1, etc.
|
160
|
+
# @param headers [Hash] Hash of headers that Rabbit delivers as part of
|
161
|
+
# the message
|
162
|
+
# @return [Integer] Count of number of failures.
|
163
|
+
def failure_count(headers)
|
164
|
+
if headers.nil? || headers['x-death'].nil?
|
165
|
+
0
|
166
|
+
else
|
167
|
+
headers['x-death'].select do |x_death|
|
168
|
+
x_death['queue'] == @worker_queue_name
|
169
|
+
end.count
|
170
|
+
end
|
171
|
+
end
|
172
|
+
private :failure_count
|
173
|
+
|
174
|
+
# Prefix all of our log messages so they are easier to find. We don't have
|
175
|
+
# the worker, so the next best thing is the queue name.
|
176
|
+
def log_prefix
|
177
|
+
"Maxretry handler [queue=#{@worker_queue_name}]"
|
178
|
+
end
|
179
|
+
private :log_prefix
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -1,27 +1,28 @@
|
|
1
1
|
module Sneakers
|
2
2
|
module Handlers
|
3
3
|
class Oneshot
|
4
|
-
def initialize(channel)
|
4
|
+
def initialize(channel, queue, opts)
|
5
5
|
@channel = channel
|
6
|
+
@opts = opts
|
6
7
|
end
|
7
8
|
|
8
|
-
def acknowledge(
|
9
|
-
@channel.acknowledge(
|
9
|
+
def acknowledge(hdr, props, msg)
|
10
|
+
@channel.acknowledge(hdr.delivery_tag, false)
|
10
11
|
end
|
11
12
|
|
12
|
-
def reject(
|
13
|
-
@channel.reject(
|
13
|
+
def reject(hdr, props, msg, requeue=false)
|
14
|
+
@channel.reject(hdr.delivery_tag, requeue)
|
14
15
|
end
|
15
16
|
|
16
|
-
def error(
|
17
|
-
reject(
|
17
|
+
def error(hdr, props, msg, err)
|
18
|
+
reject(hdr, props, msg)
|
18
19
|
end
|
19
20
|
|
20
|
-
def timeout(
|
21
|
-
reject(
|
21
|
+
def timeout(hdr, props, msg)
|
22
|
+
reject(hdr, props, msg)
|
22
23
|
end
|
23
24
|
|
24
|
-
def noop(
|
25
|
+
def noop(hdr, props, msg)
|
25
26
|
|
26
27
|
end
|
27
28
|
end
|
data/lib/sneakers/publisher.rb
CHANGED
@@ -1,28 +1,29 @@
|
|
1
1
|
module Sneakers
|
2
2
|
class Publisher
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(opts={})
|
3
|
+
def initialize(opts = {})
|
6
4
|
@mutex = Mutex.new
|
7
|
-
@opts = Sneakers::
|
5
|
+
@opts = Sneakers::CONFIG.merge(opts)
|
8
6
|
end
|
9
7
|
|
10
|
-
def publish(msg,
|
8
|
+
def publish(msg, options = {})
|
11
9
|
@mutex.synchronize do
|
12
10
|
ensure_connection! unless connected?
|
13
11
|
end
|
14
|
-
|
15
|
-
|
12
|
+
to_queue = options.delete(:to_queue)
|
13
|
+
options[:routing_key] ||= to_queue
|
14
|
+
Sneakers.logger.info {"publishing <#{msg}> to [#{options[:routing_key]}]"}
|
15
|
+
@exchange.publish(msg, options)
|
16
16
|
end
|
17
17
|
|
18
|
+
private
|
18
19
|
|
19
|
-
|
20
|
+
attr_reader :exchange
|
20
21
|
|
21
22
|
def ensure_connection!
|
22
|
-
@bunny = Bunny.new(@opts[:amqp], :heartbeat
|
23
|
+
@bunny = Bunny.new(@opts[:amqp], heartbeat: @opts[:heartbeat], vhost: @opts[:vhost], :logger => Sneakers::logger)
|
23
24
|
@bunny.start
|
24
25
|
@channel = @bunny.create_channel
|
25
|
-
@exchange = @channel.exchange(@opts[:exchange], :
|
26
|
+
@exchange = @channel.exchange(@opts[:exchange], type: @opts[:exchange_type], durable: @opts[:durable])
|
26
27
|
end
|
27
28
|
|
28
29
|
def connected?
|
data/lib/sneakers/queue.rb
CHANGED
@@ -5,7 +5,7 @@ class Sneakers::Queue
|
|
5
5
|
def initialize(name, opts)
|
6
6
|
@name = name
|
7
7
|
@opts = opts
|
8
|
-
@handler_klass = Sneakers::
|
8
|
+
@handler_klass = Sneakers::CONFIG[:handler]
|
9
9
|
end
|
10
10
|
|
11
11
|
#
|
@@ -16,7 +16,7 @@ class Sneakers::Queue
|
|
16
16
|
# :ack
|
17
17
|
#
|
18
18
|
def subscribe(worker)
|
19
|
-
@bunny = Bunny.new(@opts[:amqp], :vhost => @opts[:vhost], :heartbeat => @opts[:heartbeat])
|
19
|
+
@bunny = Bunny.new(@opts[:amqp], :vhost => @opts[:vhost], :heartbeat => @opts[:heartbeat], :logger => Sneakers::logger)
|
20
20
|
@bunny.start
|
21
21
|
|
22
22
|
@channel = @bunny.create_channel
|
@@ -26,19 +26,28 @@ class Sneakers::Queue
|
|
26
26
|
:type => @opts[:exchange_type],
|
27
27
|
:durable => @opts[:durable])
|
28
28
|
|
29
|
-
handler = @handler_klass.new(@channel)
|
30
|
-
|
31
29
|
routing_key = @opts[:routing_key] || @name
|
32
30
|
routing_keys = [*routing_key]
|
33
31
|
|
34
|
-
|
32
|
+
# TODO: get the arguments from the handler? Retry handler wants this so you
|
33
|
+
# don't have to line up the queue's dead letter argument with the exchange
|
34
|
+
# you'll create for retry.
|
35
|
+
queue_durable = @opts[:queue_durable].nil? ? @opts[:durable] : @opts[:queue_durable]
|
36
|
+
queue = @channel.queue(@name, :durable => queue_durable, :arguments => @opts[:arguments])
|
35
37
|
|
36
38
|
routing_keys.each do |key|
|
37
39
|
queue.bind(@exchange, :routing_key => key)
|
38
40
|
end
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
+
# NOTE: we are using the worker's options. This is necessary so the handler
|
43
|
+
# has the same configuration as the worker. Also pass along the exchange and
|
44
|
+
# queue in case the handler requires access to them (for things like binding
|
45
|
+
# retry queues, etc).
|
46
|
+
handler_klass = worker.opts[:handler] || Sneakers::CONFIG[:handler]
|
47
|
+
handler = handler_klass.new(@channel, queue, worker.opts)
|
48
|
+
|
49
|
+
@consumer = queue.subscribe(:block => false, :ack => @opts[:ack]) do | delivery_info, metadata, msg |
|
50
|
+
worker.do_work(delivery_info, metadata, msg, handler)
|
42
51
|
end
|
43
52
|
nil
|
44
53
|
end
|
data/lib/sneakers/runner.rb
CHANGED
@@ -41,7 +41,7 @@ module Sneakers
|
|
41
41
|
|
42
42
|
def reload_config!
|
43
43
|
Sneakers.logger.warn("Loading runner configuration...")
|
44
|
-
config_file = Sneakers::
|
44
|
+
config_file = Sneakers::CONFIG[:runner_config_file]
|
45
45
|
|
46
46
|
if config_file
|
47
47
|
begin
|
@@ -55,7 +55,7 @@ module Sneakers
|
|
55
55
|
config = make_serverengine_config
|
56
56
|
|
57
57
|
[:before_fork, :after_fork].each do | hook |
|
58
|
-
Sneakers::
|
58
|
+
Sneakers::CONFIG[:hooks][hook] = config.delete(hook) if config[hook]
|
59
59
|
end
|
60
60
|
|
61
61
|
|
@@ -65,11 +65,11 @@ module Sneakers
|
|
65
65
|
|
66
66
|
private
|
67
67
|
def make_serverengine_config
|
68
|
-
Sneakers::
|
68
|
+
Sneakers::CONFIG.merge(@conf).merge({
|
69
69
|
:worker_type => 'process',
|
70
70
|
:worker_classes => @worker_classes
|
71
71
|
})
|
72
72
|
end
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
end
|
data/lib/sneakers/version.rb
CHANGED