right_amqp 0.3.3 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/right_amqp/amqp/client.rb +16 -7
- data/lib/right_amqp/ha_client/broker_client.rb +154 -122
- data/lib/right_amqp/ha_client/ha_broker_client.rb +29 -51
- data/right_amqp.gemspec +2 -2
- data/spec/amqp/client_extensions_spec.rb +2 -13
- data/spec/ha_client/broker_client_spec.rb +268 -206
- data/spec/ha_client/ha_broker_client_spec.rb +55 -73
- data/spec/spec_helper.rb +2 -2
- metadata +3 -3
@@ -247,7 +247,13 @@ module AMQP
|
|
247
247
|
def reconnect force = false
|
248
248
|
if @reconnecting and not force
|
249
249
|
# Wait after first reconnect attempt and in between each subsequent attempt
|
250
|
-
EM.add_timer(@settings[:reconnect_interval] || 5)
|
250
|
+
EM.add_timer(@settings[:reconnect_interval] || 5) do
|
251
|
+
begin
|
252
|
+
reconnect(true)
|
253
|
+
rescue Exception => e
|
254
|
+
logger.exception("[amqp] Failed to reconnect", e, :trace)
|
255
|
+
end
|
256
|
+
end
|
251
257
|
return
|
252
258
|
end
|
253
259
|
|
@@ -265,7 +271,13 @@ module AMQP
|
|
265
271
|
again = again.call if again.is_a?(Proc)
|
266
272
|
if again.is_a?(Numeric)
|
267
273
|
# Wait before making initial reconnect attempt
|
268
|
-
EM.add_timer(again)
|
274
|
+
EM.add_timer(again) do
|
275
|
+
begin
|
276
|
+
reconnect(true)
|
277
|
+
rescue Exception => e
|
278
|
+
logger.exception("[amqp] Failed to reconnect", e, :trace)
|
279
|
+
end
|
280
|
+
end
|
269
281
|
return
|
270
282
|
elsif ![nil, true].include?(again)
|
271
283
|
raise ::AMQP::Error, "Could not interpret :reconnect_delay => #{again.inspect}; expected nil, true, or Numeric"
|
@@ -275,9 +287,6 @@ module AMQP
|
|
275
287
|
log 'reconnecting'
|
276
288
|
logger.info("[amqp] Attempting to reconnect to #{@settings[:identity]}")
|
277
289
|
EM.reconnect(@settings[:host], @settings[:port], self)
|
278
|
-
rescue Exception => e
|
279
|
-
logger.exception("[amqp] Failed to reconnect", e, :trace)
|
280
|
-
failed
|
281
290
|
end
|
282
291
|
|
283
292
|
def self.connect opts = {}
|
@@ -291,14 +300,14 @@ module AMQP
|
|
291
300
|
|
292
301
|
def failed
|
293
302
|
@connection_status.call(:failed) if @connection_status
|
294
|
-
@
|
303
|
+
@failed = true
|
295
304
|
close_connection
|
296
305
|
end
|
297
306
|
|
298
307
|
private
|
299
308
|
|
300
309
|
def disconnected
|
301
|
-
unless @
|
310
|
+
unless @failed
|
302
311
|
@connection_status.call(:disconnected) if @connection_status
|
303
312
|
reconnect
|
304
313
|
end
|
@@ -65,10 +65,10 @@ module RightAMQP
|
|
65
65
|
attr_reader :last_failed
|
66
66
|
|
67
67
|
# (RightSupport::Stats::Activity) AMQP lost connection statistics
|
68
|
-
attr_reader :
|
68
|
+
attr_reader :disconnect_stats
|
69
69
|
|
70
70
|
# (RightSupport::Stats::Activity) AMQP connection failure statistics
|
71
|
-
attr_reader :
|
71
|
+
attr_reader :failure_stats
|
72
72
|
|
73
73
|
# (Integer) Number of attempts to connect after failure
|
74
74
|
attr_reader :retries
|
@@ -83,7 +83,10 @@ module RightAMQP
|
|
83
83
|
# :index(String):: Unique index for broker within set of brokers for use in forming alias
|
84
84
|
# serializer(Serializer):: Serializer used for unmarshaling received messages to packets
|
85
85
|
# (responds to :load); if nil, has same effect as setting subscribe option :no_unserialize
|
86
|
-
#
|
86
|
+
# exception_stats(RightSupport::Stats::Exceptions):: Exception statistics container to be updated
|
87
|
+
# whenever there is an unexpected exception
|
88
|
+
# non_delivery_stats(RightSupport::Stats::Activity):: Non-delivery statistics container to be
|
89
|
+
# updated whenever a message cannot be sent or received
|
87
90
|
# options(Hash):: Configuration options
|
88
91
|
# :user(String):: User name
|
89
92
|
# :pass(String):: Password
|
@@ -96,17 +99,28 @@ module RightAMQP
|
|
96
99
|
# :prefetch(Integer):: Maximum number of messages the AMQP broker is to prefetch for the agent
|
97
100
|
# before it receives an ack. Value 1 ensures that only last unacknowledged gets redelivered
|
98
101
|
# if the agent crashes. Value 0 means unlimited prefetch.
|
102
|
+
# :fiber_pool(NB::FiberPool):: Pool of initialized fibers to be used for asynchronous message
|
103
|
+
# processing (can be overridden with subscribe option)
|
99
104
|
# :exception_on_receive_callback(Proc):: Callback activated on a receive exception with parameters
|
100
105
|
# message(Object):: Message received
|
101
106
|
# exception(Exception):: Exception raised
|
102
107
|
# :update_status_callback(Proc):: Callback activated on a connection status change with parameters
|
103
108
|
# broker(BrokerClient):: Broker client
|
104
109
|
# connected_before(Boolean):: Whether was connected prior to this status change
|
110
|
+
# :return_message_callback(Proc):: Callback activated when a message is returned with parameters
|
111
|
+
# to(String):: Queue to which message was published
|
112
|
+
# reason(String):: Reason for return
|
113
|
+
# "NO_ROUTE" - queue does not exist
|
114
|
+
# "NO_CONSUMERS" - queue exists but it has no consumers, or if :immediate was specified,
|
115
|
+
# all consumers are not immediately ready to consume
|
116
|
+
# "ACCESS_REFUSED" - queue not usable because broker is in the process of stopping service
|
117
|
+
# message(String):: Returned serialized message
|
118
|
+
#
|
105
119
|
# existing(BrokerClient|nil):: Existing broker client for this address, or nil if none
|
106
120
|
#
|
107
121
|
# === Raise
|
108
122
|
# ArgumentError:: If serializer does not respond to :dump and :load
|
109
|
-
def initialize(identity, address, serializer,
|
123
|
+
def initialize(identity, address, serializer, exception_stats, non_delivery_stats, options, existing = nil)
|
110
124
|
@options = options
|
111
125
|
@identity = identity
|
112
126
|
@host = address[:host]
|
@@ -117,21 +131,22 @@ module RightAMQP
|
|
117
131
|
unless serializer.nil? || [:dump, :load].all? { |m| serializer.respond_to?(m) }
|
118
132
|
raise ArgumentError, "serializer must be a class/object that responds to :dump and :load"
|
119
133
|
end
|
120
|
-
@serializer
|
121
|
-
@
|
122
|
-
@
|
123
|
-
@
|
124
|
-
@
|
125
|
-
@
|
126
|
-
@
|
134
|
+
@serializer = serializer
|
135
|
+
@queues = []
|
136
|
+
@last_failed = false
|
137
|
+
@exception_stats = exception_stats
|
138
|
+
@non_delivery_stats = non_delivery_stats
|
139
|
+
@disconnect_stats = RightSupport::Stats::Activity.new(measure_rate = false)
|
140
|
+
@failure_stats = RightSupport::Stats::Activity.new(measure_rate = false)
|
141
|
+
@retries = 0
|
127
142
|
|
128
143
|
connect(address, @options[:reconnect_interval])
|
129
144
|
|
130
145
|
if existing
|
131
|
-
@
|
132
|
-
@
|
133
|
-
@last_failed
|
134
|
-
@retries
|
146
|
+
@disconnect_stats = existing.disconnect_stats
|
147
|
+
@failure_stats = existing.failure_stats
|
148
|
+
@last_failed = existing.last_failed
|
149
|
+
@retries = existing.retries
|
135
150
|
update_failure if @status == :failed
|
136
151
|
end
|
137
152
|
end
|
@@ -174,22 +189,23 @@ module RightAMQP
|
|
174
189
|
# Subscribe an AMQP queue to an AMQP exchange
|
175
190
|
# Do not wait for confirmation from broker that subscription is complete
|
176
191
|
# When a message is received, acknowledge, unserialize, and log it as specified
|
177
|
-
# If the message is unserialized and it is not of the right type, it is dropped after logging
|
192
|
+
# If the message is unserialized and it is not of the right type, it is dropped after logging an error
|
178
193
|
#
|
179
194
|
# === Parameters
|
180
195
|
# queue(Hash):: AMQP queue being subscribed with keys :name and :options,
|
181
196
|
# which are the standard AMQP ones plus
|
182
197
|
# :no_declare(Boolean):: Whether to skip declaring this queue on the broker
|
183
|
-
# to cause its creation; for use when
|
198
|
+
# to cause its creation; for use when caller does not have permission to create or
|
184
199
|
# knows the queue already exists and wants to avoid declare overhead
|
185
200
|
# exchange(Hash|nil):: AMQP exchange to subscribe to with keys :type, :name, and :options,
|
186
201
|
# nil means use empty exchange by directly subscribing to queue; the :options are the
|
187
202
|
# standard AMQP ones plus
|
188
203
|
# :no_declare(Boolean):: Whether to skip declaring this exchange on the broker
|
189
|
-
# to cause its creation; for use when
|
204
|
+
# to cause its creation; for use when caller does not have create permission or
|
190
205
|
# knows the exchange already exists and wants to avoid declare overhead
|
191
206
|
# options(Hash):: Subscribe options:
|
192
|
-
# :ack(Boolean)::
|
207
|
+
# :ack(Boolean):: Whether caller takes responsibility for explicitly acknowledging each
|
208
|
+
# message received, defaults to implicit acknowledgement in AMQP as part of message receipt
|
193
209
|
# :no_unserialize(Boolean):: Do not unserialize message, this is an escape for special
|
194
210
|
# situations like enrollment, also implicitly disables receive filtering and logging;
|
195
211
|
# this option is implicitly invoked if initialize without a serializer
|
@@ -200,16 +216,22 @@ module RightAMQP
|
|
200
216
|
# :no_log(Boolean):: Disable receive logging unless debug level
|
201
217
|
# :exchange2(Hash):: Additional exchange to which same queue is to be bound
|
202
218
|
# :brokers(Array):: Identity of brokers for which to subscribe, defaults to all usable if nil or empty
|
219
|
+
# :fiber_pool(NB::FiberPool):: Pool of initialized fibers to be used for asynchronous message
|
220
|
+
# processing (non-nil value will override constructor option setting)
|
203
221
|
#
|
204
222
|
# === Block
|
205
|
-
#
|
223
|
+
# Required block with following parameters to be called each time exchange matches a message to the queue
|
206
224
|
# identity(String):: Serialized identity of broker delivering the message
|
207
225
|
# message(Packet|String):: Message received, which is unserialized unless :no_unserialize was specified
|
208
226
|
# header(AMQP::Protocol::Header):: Message header (optional block parameter)
|
209
227
|
#
|
228
|
+
# === Raise
|
229
|
+
# ArgumentError:: If a block is not supplied
|
230
|
+
#
|
210
231
|
# === Return
|
211
232
|
# (Boolean):: true if subscribe successfully or if already subscribed, otherwise false
|
212
|
-
def subscribe(queue, exchange = nil, options = {}, &
|
233
|
+
def subscribe(queue, exchange = nil, options = {}, &block)
|
234
|
+
raise ArgumentError, "Must call this method with a block" unless block
|
213
235
|
return false unless usable?
|
214
236
|
return true unless @queues.select { |q| q.name == queue[:name] }.empty?
|
215
237
|
|
@@ -235,52 +257,24 @@ module RightAMQP
|
|
235
257
|
end
|
236
258
|
q = binding
|
237
259
|
end
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
execute_callback(blk, @identity, message, header)
|
245
|
-
elsif message == "nil"
|
246
|
-
# This happens as part of connecting an instance agent to a broker prior to version 13
|
247
|
-
logger.debug("RECV #{@alias} nil message ignored")
|
248
|
-
elsif
|
249
|
-
packet = receive(queue[:name], message, options)
|
250
|
-
execute_callback(blk, @identity, packet, header) if packet
|
251
|
-
end
|
252
|
-
true
|
253
|
-
rescue Exception => e
|
254
|
-
logger.exception("Failed executing block for message from queue #{queue.inspect}#{to_exchange} " +
|
255
|
-
"on broker #{@alias}", e, :trace)
|
256
|
-
@exceptions.track("receive", e)
|
257
|
-
false
|
258
|
-
end
|
259
|
-
end
|
260
|
-
else
|
261
|
-
q.subscribe do |header, message|
|
262
|
-
begin
|
263
|
-
if options[:no_unserialize] || @serializer.nil?
|
264
|
-
execute_callback(blk, @identity, message, header)
|
265
|
-
elsif message == "nil"
|
266
|
-
# This happens as part of connecting an instance agent to a broker
|
267
|
-
logger.debug("RECV #{@alias} nil message ignored")
|
268
|
-
elsif
|
269
|
-
packet = receive(queue[:name], message, options)
|
270
|
-
execute_callback(blk, @identity, packet, header) if packet
|
271
|
-
end
|
272
|
-
true
|
273
|
-
rescue Exception => e
|
274
|
-
logger.exception("Failed executing block for message from queue #{queue.inspect}#{to_exchange} " +
|
275
|
-
"on broker #{@alias}", e, :trace)
|
276
|
-
@exceptions.track("receive", e)
|
277
|
-
false
|
260
|
+
q.subscribe(options[:ack] ? {:ack => true} : {}) do |header, message|
|
261
|
+
begin
|
262
|
+
if pool = (options[:fiber_pool] || @options[:fiber_pool])
|
263
|
+
pool.spawn { receive(queue[:name], header, message, options, &block) }
|
264
|
+
else
|
265
|
+
receive(queue[:name], header, message, options, &block)
|
278
266
|
end
|
267
|
+
rescue Exception => e
|
268
|
+
header.ack if options[:ack]
|
269
|
+
logger.exception("Failed setting up to receive message from queue #{queue.inspect} " +
|
270
|
+
"on broker #{@alias}", e, :trace)
|
271
|
+
@exception_stats.track("receive", e)
|
272
|
+
@non_delivery_stats.update("receive failure")
|
279
273
|
end
|
280
274
|
end
|
281
275
|
rescue Exception => e
|
282
276
|
logger.exception("Failed subscribing queue #{queue.inspect}#{to_exchange} on broker #{@alias}", e, :trace)
|
283
|
-
@
|
277
|
+
@exception_stats.track("subscribe", e)
|
284
278
|
false
|
285
279
|
end
|
286
280
|
end
|
@@ -296,17 +290,17 @@ module RightAMQP
|
|
296
290
|
#
|
297
291
|
# === Return
|
298
292
|
# true:: Always return true
|
299
|
-
def unsubscribe(queue_names, &
|
293
|
+
def unsubscribe(queue_names, &block)
|
300
294
|
if usable?
|
301
295
|
@queues.each do |q|
|
302
296
|
if queue_names.include?(q.name)
|
303
297
|
begin
|
304
298
|
logger.info("[stop] Unsubscribing queue #{q.name} on broker #{@alias}")
|
305
|
-
q.unsubscribe {
|
299
|
+
q.unsubscribe { block.call if block }
|
306
300
|
rescue Exception => e
|
307
301
|
logger.exception("Failed unsubscribing queue #{q.name} on broker #{@alias}", e, :trace)
|
308
|
-
@
|
309
|
-
|
302
|
+
@exception_stats.track("unsubscribe", e)
|
303
|
+
block.call if block
|
310
304
|
end
|
311
305
|
end
|
312
306
|
end
|
@@ -332,7 +326,7 @@ module RightAMQP
|
|
332
326
|
true
|
333
327
|
rescue Exception => e
|
334
328
|
logger.exception("Failed declaring #{type.to_s} #{name} on broker #{@alias}", e, :trace)
|
335
|
-
@
|
329
|
+
@exception_stats.track("declare", e)
|
336
330
|
false
|
337
331
|
end
|
338
332
|
end
|
@@ -343,7 +337,7 @@ module RightAMQP
|
|
343
337
|
# exchange(Hash):: AMQP exchange to subscribe to with keys :type, :name, and :options,
|
344
338
|
# which are the standard AMQP ones plus
|
345
339
|
# :no_declare(Boolean):: Whether to skip declaring this exchange or queue on the broker
|
346
|
-
# to cause its creation; for use when
|
340
|
+
# to cause its creation; for use when caller does not have create permission or
|
347
341
|
# knows the object already exists and wants to avoid declare overhead
|
348
342
|
# :declare(Boolean):: Whether to delete this exchange or queue from the AMQP cache
|
349
343
|
# to force it to be declared on the broker and thus be created if it does not exist
|
@@ -382,44 +376,12 @@ module RightAMQP
|
|
382
376
|
true
|
383
377
|
rescue Exception => e
|
384
378
|
logger.exception("Failed publishing to exchange #{exchange.inspect} on broker #{@alias}", e, :trace)
|
385
|
-
@
|
379
|
+
@exception_stats.track("publish", e)
|
380
|
+
@non_delivery_stats.update("publish failure")
|
386
381
|
false
|
387
382
|
end
|
388
383
|
end
|
389
384
|
|
390
|
-
# Provide callback to be activated when broker returns a message that could not be delivered
|
391
|
-
# A message published with :mandatory => true is returned if the exchange does not have any associated queues
|
392
|
-
# or if all the associated queues do not have any consumers
|
393
|
-
# A message published with :immediate => true is returned for the same reasons as :mandatory plus if all
|
394
|
-
# of the queues associated with the exchange are not immediately ready to consume the message
|
395
|
-
#
|
396
|
-
# === Block
|
397
|
-
# Optional block with following parameters to be called when a message is returned
|
398
|
-
# to(String):: Queue to which message was published
|
399
|
-
# reason(String):: Reason for return
|
400
|
-
# "NO_ROUTE" - queue does not exist
|
401
|
-
# "NO_CONSUMERS" - queue exists but it has no consumers, or if :immediate was specified,
|
402
|
-
# all consumers are not immediately ready to consume
|
403
|
-
# "ACCESS_REFUSED" - queue not usable because broker is in the process of stopping service
|
404
|
-
# message(String):: Returned serialized message
|
405
|
-
#
|
406
|
-
# === Return
|
407
|
-
# true:: Always return true
|
408
|
-
def return_message
|
409
|
-
@channel.return_message do |info, message|
|
410
|
-
begin
|
411
|
-
to = if info.exchange && !info.exchange.empty? then info.exchange else info.routing_key end
|
412
|
-
reason = info.reply_text
|
413
|
-
logger.debug("RETURN #{@alias} because #{reason} for #{to}")
|
414
|
-
yield(to, reason, message) if block_given?
|
415
|
-
rescue Exception => e
|
416
|
-
logger.exception("Failed return #{info.inspect} of message from broker #{@alias}", e, :trace)
|
417
|
-
@exceptions.track("return", e)
|
418
|
-
end
|
419
|
-
end
|
420
|
-
true
|
421
|
-
end
|
422
|
-
|
423
385
|
# Delete queue
|
424
386
|
#
|
425
387
|
# === Parameters
|
@@ -446,7 +408,7 @@ module RightAMQP
|
|
446
408
|
end
|
447
409
|
rescue Exception => e
|
448
410
|
logger.exception("Failed deleting queue #{name.inspect} on broker #{@alias}", e, :trace)
|
449
|
-
@
|
411
|
+
@exception_stats.track("delete", e)
|
450
412
|
end
|
451
413
|
end
|
452
414
|
deleted
|
@@ -477,7 +439,7 @@ module RightAMQP
|
|
477
439
|
#
|
478
440
|
# === Return
|
479
441
|
# true:: Always return true
|
480
|
-
def close(propagate = true, normal = true, log = true, &
|
442
|
+
def close(propagate = true, normal = true, log = true, &block)
|
481
443
|
final_status = normal ? :closed : :failed
|
482
444
|
if ![:closed, :failed].include?(@status)
|
483
445
|
begin
|
@@ -489,7 +451,7 @@ module RightAMQP
|
|
489
451
|
end
|
490
452
|
rescue Exception => e
|
491
453
|
logger.exception("Failed to close broker #{@alias}", e, :trace)
|
492
|
-
@
|
454
|
+
@exception_stats.track("close", e)
|
493
455
|
@status = final_status
|
494
456
|
yield if block_given?
|
495
457
|
end
|
@@ -516,8 +478,8 @@ module RightAMQP
|
|
516
478
|
:alias => @alias,
|
517
479
|
:status => @status,
|
518
480
|
:retries => @retries,
|
519
|
-
:disconnects => @
|
520
|
-
:failures => @
|
481
|
+
:disconnects => @disconnect_stats.total,
|
482
|
+
:failures => @failure_stats.total,
|
521
483
|
}
|
522
484
|
end
|
523
485
|
|
@@ -532,15 +494,16 @@ module RightAMQP
|
|
532
494
|
# "disconnects"(Integer|nil):: Number of times lost connection, or nil if none
|
533
495
|
# "failure last"(Hash|nil):: Last connect failure information with key "elapsed", or nil if none
|
534
496
|
# "failures"(Integer|nil):: Number of failed attempts to connect to broker, or nil if none
|
497
|
+
# "retries"(Integer|nil):: Number of connect retries, or nil if none
|
535
498
|
def stats
|
536
499
|
{
|
537
500
|
"alias" => @alias,
|
538
501
|
"identity" => @identity,
|
539
502
|
"status" => @status.to_s,
|
540
|
-
"disconnect last" => @
|
541
|
-
"disconnects" => RightSupport::Stats.nil_if_zero(@
|
542
|
-
"failure last" => @
|
543
|
-
"failures" => RightSupport::Stats.nil_if_zero(@
|
503
|
+
"disconnect last" => @disconnect_stats.last,
|
504
|
+
"disconnects" => RightSupport::Stats.nil_if_zero(@disconnect_stats.total),
|
505
|
+
"failure last" => @failure_stats.last,
|
506
|
+
"failures" => RightSupport::Stats.nil_if_zero(@failure_stats.total),
|
544
507
|
"retries" => RightSupport::Stats.nil_if_zero(@retries)
|
545
508
|
}
|
546
509
|
end
|
@@ -570,7 +533,7 @@ module RightAMQP
|
|
570
533
|
elsif status == :failed
|
571
534
|
update_failure
|
572
535
|
elsif status == :disconnected && before != :disconnected
|
573
|
-
@
|
536
|
+
@disconnect_stats.update
|
574
537
|
end
|
575
538
|
|
576
539
|
unless status == before || @options[:update_status_callback].nil?
|
@@ -582,7 +545,7 @@ module RightAMQP
|
|
582
545
|
protected
|
583
546
|
|
584
547
|
# Connect to broker and register for status updates
|
585
|
-
# Also set prefetch value if specified
|
548
|
+
# Also set prefetch value if specified and setup for message returns
|
586
549
|
#
|
587
550
|
# === Parameters
|
588
551
|
# address(Hash):: Broker address
|
@@ -611,21 +574,67 @@ module RightAMQP
|
|
611
574
|
@channel = MQ.new(@connection)
|
612
575
|
@channel.__send__(:connection).connection_status { |status| update_status(status) }
|
613
576
|
@channel.prefetch(@options[:prefetch]) if @options[:prefetch]
|
577
|
+
@channel.return_message { |header, message| handle_return(header, message) }
|
614
578
|
rescue Exception => e
|
615
579
|
@status = :failed
|
616
|
-
@
|
580
|
+
@failure_stats.update
|
617
581
|
logger.exception("Failed connecting to broker #{@alias}", e, :trace)
|
618
|
-
@
|
582
|
+
@exception_stats.track("connect", e)
|
619
583
|
@connection.close if @connection
|
620
584
|
end
|
621
585
|
end
|
622
586
|
|
623
|
-
# Receive message by unserializing it,
|
587
|
+
# Receive message by optionally unserializing it, passing it to the callback, and optionally
|
588
|
+
# acknowledging it
|
624
589
|
#
|
625
590
|
# === Parameters
|
626
591
|
# queue(String):: Name of queue
|
592
|
+
# header(AMQP::Protocol::Header):: Message header
|
627
593
|
# message(String):: Serialized packet
|
628
|
-
# options(Hash):: Subscribe options
|
594
|
+
# options(Hash):: Subscribe options
|
595
|
+
# :ack(Boolean):: Whether caller takes responsibility for explicitly acknowledging each
|
596
|
+
# message received, defaults to implicit acknowledgement in AMQP as part of message receipt
|
597
|
+
# :no_unserialize(Boolean):: Do not unserialize message, this is an escape for special
|
598
|
+
# situations like enrollment, also implicitly disables receive filtering and logging;
|
599
|
+
# this option is implicitly invoked if initialize without a serializer
|
600
|
+
#
|
601
|
+
# === Block
|
602
|
+
# Block with following parameters to be called with received message
|
603
|
+
# identity(String):: Serialized identity of broker delivering the message
|
604
|
+
# message(Packet|String):: Message received, which is unserialized unless :no_unserialize was specified
|
605
|
+
# header(AMQP::Protocol::Header):: Message header (optional block parameter)
|
606
|
+
#
|
607
|
+
# === Return
|
608
|
+
# true:: Always return true
|
609
|
+
def receive(queue, header, message, options, &block)
|
610
|
+
begin
|
611
|
+
if options[:no_unserialize] || @serializer.nil?
|
612
|
+
execute_callback(block, @identity, message, header)
|
613
|
+
elsif message == "nil"
|
614
|
+
# This happens as part of connecting an instance agent to a broker prior to version 13
|
615
|
+
header.ack if options[:ack]
|
616
|
+
logger.debug("RECV #{@alias} nil message ignored")
|
617
|
+
elsif packet = unserialize(queue, message, options)
|
618
|
+
execute_callback(block, @identity, packet, header)
|
619
|
+
elsif options[:ack]
|
620
|
+
# Need to ack empty packet since no callback is being made
|
621
|
+
header.ack
|
622
|
+
end
|
623
|
+
true
|
624
|
+
rescue Exception => e
|
625
|
+
header.ack if options[:ack]
|
626
|
+
logger.exception("Failed receiving message from queue #{queue.inspect} on broker #{@alias}", e, :trace)
|
627
|
+
@exception_stats.track("receive", e)
|
628
|
+
@non_delivery_stats.update("receive failure")
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
# Unserialize message, check that it is an acceptable type, and log it
|
633
|
+
#
|
634
|
+
# === Parameters
|
635
|
+
# queue(String):: Name of queue
|
636
|
+
# message(String):: Serialized packet
|
637
|
+
# options(Hash):: Subscribe options
|
629
638
|
# (packet class)(Array(Symbol)):: Filters to be applied in to_s when logging packet to :info,
|
630
639
|
# only packet classes specified are accepted, others are not processed but are logged with error
|
631
640
|
# :category(String):: Packet category description to be used in error messages
|
@@ -634,7 +643,7 @@ module RightAMQP
|
|
634
643
|
#
|
635
644
|
# === Return
|
636
645
|
# (Packet|nil):: Unserialized packet or nil if not of right type or if there is an exception
|
637
|
-
def
|
646
|
+
def unserialize(queue, message, options = {})
|
638
647
|
begin
|
639
648
|
received_at = Time.now.to_f
|
640
649
|
packet = @serializer.load(message)
|
@@ -648,15 +657,16 @@ module RightAMQP
|
|
648
657
|
packet
|
649
658
|
else
|
650
659
|
category = options[:category] + " " if options[:category]
|
651
|
-
logger.
|
660
|
+
logger.error("Received invalid #{category}packet type from queue #{queue} on broker #{@alias}: #{packet.class}\n" + caller.join("\n"))
|
652
661
|
nil
|
653
662
|
end
|
654
663
|
rescue Exception => e
|
655
664
|
# TODO Taking advantage of Serializer knowledge here even though out of scope
|
656
665
|
trace = e.class.name =~ /SerializationError/ ? :caller : :trace
|
657
|
-
logger.exception("Failed
|
658
|
-
@
|
666
|
+
logger.exception("Failed unserializing message from queue #{queue.inspect} on broker #{@alias}", e, trace)
|
667
|
+
@exception_stats.track("receive", e)
|
659
668
|
@options[:exception_on_receive_callback].call(message, e) if @options[:exception_on_receive_callback]
|
669
|
+
@non_delivery_stats.update("receive failure")
|
660
670
|
nil
|
661
671
|
end
|
662
672
|
end
|
@@ -682,7 +692,29 @@ module RightAMQP
|
|
682
692
|
else
|
683
693
|
@last_failed = true
|
684
694
|
@retries = 0
|
685
|
-
@
|
695
|
+
@failure_stats.update
|
696
|
+
end
|
697
|
+
true
|
698
|
+
end
|
699
|
+
|
700
|
+
# Handle message returned by broker because it could not deliver it
|
701
|
+
#
|
702
|
+
# === Parameters
|
703
|
+
# header(AMQP::Protocol::Header):: Message header
|
704
|
+
# message(String):: Serialized packet
|
705
|
+
#
|
706
|
+
# === Return
|
707
|
+
# true:: Always return true
|
708
|
+
def handle_return(header, message)
|
709
|
+
begin
|
710
|
+
to = if header.exchange && !header.exchange.empty? then header.exchange else header.routing_key end
|
711
|
+
reason = header.reply_text
|
712
|
+
callback = @options[:return_message_callback]
|
713
|
+
logger.__send__(callback ? :debug : :info, "RETURN #{@alias} for #{to} because #{reason}")
|
714
|
+
callback.call(@identity, to, reason, message) if callback
|
715
|
+
rescue Exception => e
|
716
|
+
logger.exception("Failed return #{header.inspect} of message from broker #{@alias}", e, :trace)
|
717
|
+
@exception_stats.track("return", e)
|
686
718
|
end
|
687
719
|
true
|
688
720
|
end
|