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
@@ -136,6 +136,7 @@ module RightAMQP
|
|
136
136
|
def initialize(serializer, options = {})
|
137
137
|
@options = options.dup
|
138
138
|
@options[:update_status_callback] = lambda { |b, c| update_status(b, c) }
|
139
|
+
@options[:return_message_callback] = lambda { |i, t, r, m| handle_return(i, t, r, m) }
|
139
140
|
@options[:reconnect_interval] ||= RECONNECT_INTERVAL
|
140
141
|
@connection_status = {}
|
141
142
|
unless serializer.nil? || [:dump, :load].all? { |m| serializer.respond_to?(m) }
|
@@ -149,7 +150,6 @@ module RightAMQP
|
|
149
150
|
@closed = false
|
150
151
|
@brokers_hash = {}
|
151
152
|
@brokers.each { |b| @brokers_hash[b.identity] = b }
|
152
|
-
return_message { |i, r, m, t, c| handle_return(i, r, m, t, c) }
|
153
153
|
end
|
154
154
|
|
155
155
|
# Parse agent user data to extract broker host and port configuration
|
@@ -360,7 +360,7 @@ module RightAMQP
|
|
360
360
|
# === Return
|
361
361
|
# (Array):: Serialized identity of unusable brokers
|
362
362
|
def unusable
|
363
|
-
|
363
|
+
all - usable
|
364
364
|
end
|
365
365
|
|
366
366
|
# Get serialized identity of all brokers
|
@@ -430,7 +430,7 @@ module RightAMQP
|
|
430
430
|
end unless existing
|
431
431
|
|
432
432
|
address = {:host => host, :port => port, :index => index}
|
433
|
-
broker = BrokerClient.new(identity, address, @serializer, @
|
433
|
+
broker = BrokerClient.new(identity, address, @serializer, @exception_stats, @non_delivery_stats, @options, existing)
|
434
434
|
p = priority(old_identity)
|
435
435
|
if priority && priority < p
|
436
436
|
@brokers.insert(priority, broker)
|
@@ -466,7 +466,8 @@ module RightAMQP
|
|
466
466
|
# to cause its creation; for use when client does not have create permission or
|
467
467
|
# knows the exchange already exists and wants to avoid declare overhead
|
468
468
|
# options(Hash):: Subscribe options:
|
469
|
-
# :ack(Boolean)::
|
469
|
+
# :ack(Boolean):: Whether client takes responsibility for explicitly acknowledging each
|
470
|
+
# message received, defaults to implicit acknowledgement in AMQP as part of message receipt
|
470
471
|
# :no_unserialize(Boolean):: Do not unserialize message, this is an escape for special
|
471
472
|
# situations like enrollment, also implicitly disables receive filtering and logging;
|
472
473
|
# this option is implicitly invoked if initialize without a serializer
|
@@ -553,6 +554,10 @@ module RightAMQP
|
|
553
554
|
# to force it to be declared on the broker and thus be created if it does not exist
|
554
555
|
# packet(Packet):: Message to serialize and publish
|
555
556
|
# options(Hash):: Publish options -- standard AMQP ones plus
|
557
|
+
# :mandatory(Boolean):: Return message if the exchange does not have any associated queues
|
558
|
+
# or if all the associated queues do not have any consumers
|
559
|
+
# :immediate(Boolean):: Return message for the same reasons as :mandatory plus if all
|
560
|
+
# of the queues associated with the exchange are not immediately ready to consume the message
|
556
561
|
# :fanout(Boolean):: true means publish to all connected brokers
|
557
562
|
# :brokers(Array):: Identity of brokers selected for use, defaults to all home brokers
|
558
563
|
# if nil or empty
|
@@ -593,38 +598,6 @@ module RightAMQP
|
|
593
598
|
identities
|
594
599
|
end
|
595
600
|
|
596
|
-
# Register callback to be activated when a broker returns a message that could not be delivered
|
597
|
-
# A message published with :mandatory => true is returned if the exchange does not have any associated queues
|
598
|
-
# or if all the associated queues do not have any consumers
|
599
|
-
# A message published with :immediate => true is returned for the same reasons as :mandatory plus if all
|
600
|
-
# of the queues associated with the exchange are not immediately ready to consume the message
|
601
|
-
# Remove any previously registered callback
|
602
|
-
#
|
603
|
-
# === Block
|
604
|
-
# Required block to be called when a message is returned with parameters
|
605
|
-
# identity(String):: Broker serialized identity
|
606
|
-
# reason(String):: Reason for return
|
607
|
-
# "NO_ROUTE" - queue does not exist
|
608
|
-
# "NO_CONSUMERS" - queue exists but it has no consumers, or if :immediate was specified,
|
609
|
-
# all consumers are not immediately ready to consume
|
610
|
-
# "ACCESS_REFUSED" - queue not usable because broker is in the process of stopping service
|
611
|
-
# message(String):: Returned serialized message
|
612
|
-
# to(String):: Queue to which message was published
|
613
|
-
# context(Context|nil):: Message publishing context, or nil if not available
|
614
|
-
#
|
615
|
-
# === Return
|
616
|
-
# true:: Always return true
|
617
|
-
def return_message(&blk)
|
618
|
-
each_usable do |b|
|
619
|
-
b.return_message do |to, reason, message|
|
620
|
-
context = @published.fetch(message)
|
621
|
-
context.record_failure(b.identity) if context
|
622
|
-
blk.call(b.identity, reason, message, to, context)
|
623
|
-
end
|
624
|
-
end
|
625
|
-
true
|
626
|
-
end
|
627
|
-
|
628
601
|
# Provide callback to be activated when a message cannot be delivered
|
629
602
|
#
|
630
603
|
# === Block
|
@@ -748,7 +721,7 @@ module RightAMQP
|
|
748
721
|
rescue Exception => e
|
749
722
|
handler.completed_one
|
750
723
|
logger.exception("Failed to close broker #{b.alias}", e, :trace)
|
751
|
-
@
|
724
|
+
@exception_stats.track("close", e)
|
752
725
|
end
|
753
726
|
end
|
754
727
|
end
|
@@ -838,14 +811,17 @@ module RightAMQP
|
|
838
811
|
# "total"(Integer):: Total exceptions for this category
|
839
812
|
# "recent"(Array):: Most recent as a hash of "count", "type", "message", "when", and "where"
|
840
813
|
# "heartbeat"(Integer|nil):: Number of seconds between AMQP heartbeats, or nil if heartbeat disabled
|
814
|
+
# "non_deliveries"(Hash|nil):: Non-delivery activity stats with keys "total", "percent", "last", and "rate"
|
815
|
+
# with percentage breakdown per non-delivery reason, or nil if none
|
841
816
|
# "returns"(Hash|nil):: Message return activity stats with keys "total", "percent", "last", and "rate"
|
842
817
|
# with percentage breakdown per return reason, or nil if none
|
843
818
|
def stats(reset = false)
|
844
819
|
stats = {
|
845
|
-
"brokers"
|
846
|
-
"exceptions"
|
847
|
-
"heartbeat"
|
848
|
-
"
|
820
|
+
"brokers" => @brokers.map { |b| b.stats },
|
821
|
+
"exceptions" => @exception_stats.stats,
|
822
|
+
"heartbeat" => @options[:heartbeat],
|
823
|
+
"non-deliveries" => @non_delivery_stats.all,
|
824
|
+
"returns" => @return_stats.all
|
849
825
|
}
|
850
826
|
reset_stats if reset
|
851
827
|
stats
|
@@ -858,8 +834,9 @@ module RightAMQP
|
|
858
834
|
# === Return
|
859
835
|
# true:: Always return true
|
860
836
|
def reset_stats
|
861
|
-
@
|
862
|
-
@
|
837
|
+
@return_stats = RightSupport::Stats::Activity.new
|
838
|
+
@non_delivery_stats = RightSupport::Stats::Activity.new
|
839
|
+
@exception_stats = RightSupport::Stats::Exceptions.new(self, @options[:exception_callback])
|
863
840
|
true
|
864
841
|
end
|
865
842
|
|
@@ -872,7 +849,7 @@ module RightAMQP
|
|
872
849
|
def connect_all
|
873
850
|
self.class.addresses(@options[:host], @options[:port]).map do |a|
|
874
851
|
identity = self.class.identity(a[:host], a[:port])
|
875
|
-
BrokerClient.new(identity, a, @serializer, @
|
852
|
+
BrokerClient.new(identity, a, @serializer, @exception_stats, @non_delivery_stats, @options, nil)
|
876
853
|
end
|
877
854
|
end
|
878
855
|
|
@@ -1029,22 +1006,22 @@ module RightAMQP
|
|
1029
1006
|
#
|
1030
1007
|
# === Parameters
|
1031
1008
|
# identity(String):: Identity of broker that could not deliver message
|
1009
|
+
# to(String):: Queue to which message was published
|
1032
1010
|
# reason(String):: Reason for return
|
1033
1011
|
# "NO_ROUTE" - queue does not exist
|
1034
1012
|
# "NO_CONSUMERS" - queue exists but it has no consumers, or if :immediate was specified,
|
1035
1013
|
# all consumers are not immediately ready to consume
|
1036
1014
|
# "ACCESS_REFUSED" - queue not usable because broker is in the process of stopping service
|
1037
1015
|
# message(String):: Returned message in serialized packet format
|
1038
|
-
# to(String):: Queue to which message was published
|
1039
|
-
# context(Context):: Message publishing context
|
1040
1016
|
#
|
1041
1017
|
# === Return
|
1042
1018
|
# true:: Always return true
|
1043
|
-
def handle_return(identity, reason, message
|
1019
|
+
def handle_return(identity, to, reason, message)
|
1044
1020
|
@brokers_hash[identity].update_status(:stopping) if reason == "ACCESS_REFUSED"
|
1045
1021
|
|
1046
|
-
if context
|
1047
|
-
@
|
1022
|
+
if context = @published.fetch(message)
|
1023
|
+
@return_stats.update("#{alias_(identity)} (#{reason.to_s.downcase})")
|
1024
|
+
context.record_failure(identity)
|
1048
1025
|
name = context.name
|
1049
1026
|
options = context.options || {}
|
1050
1027
|
token = context.token
|
@@ -1064,6 +1041,7 @@ module RightAMQP
|
|
1064
1041
|
t = token ? " <#{token}>" : ""
|
1065
1042
|
logger.info("NO ROUTE #{aliases(context.brokers).join(", ")} [#{name}]#{t} to #{to}")
|
1066
1043
|
@non_delivery.call(reason, context.type, token, context.from, to) if @non_delivery
|
1044
|
+
@non_delivery_stats.update("no route")
|
1067
1045
|
end
|
1068
1046
|
end
|
1069
1047
|
|
@@ -1077,14 +1055,14 @@ module RightAMQP
|
|
1077
1055
|
:persistent => persistent, :mandatory => mandatory))
|
1078
1056
|
end
|
1079
1057
|
else
|
1080
|
-
@
|
1058
|
+
@return_stats.update("#{alias_(identity)} (#{reason.to_s.downcase} - missing context)")
|
1081
1059
|
logger.info("Dropping message returned from broker #{identity} for reason #{reason} " +
|
1082
1060
|
"because no message context available for re-routing it to #{to}")
|
1083
1061
|
end
|
1084
1062
|
true
|
1085
1063
|
rescue Exception => e
|
1086
1064
|
logger.exception("Failed to handle #{reason} return from #{identity} for message being routed to #{to}", e, :trace)
|
1087
|
-
@
|
1065
|
+
@exception_stats.track("return", e)
|
1088
1066
|
end
|
1089
1067
|
|
1090
1068
|
# Helper for deferring block execution until specified number of actions have completed
|
data/right_amqp.gemspec
CHANGED
@@ -24,8 +24,8 @@ require 'rubygems'
|
|
24
24
|
|
25
25
|
Gem::Specification.new do |spec|
|
26
26
|
spec.name = 'right_amqp'
|
27
|
-
spec.version = '0.
|
28
|
-
spec.date = '2012-
|
27
|
+
spec.version = '0.5.2'
|
28
|
+
spec.date = '2012-09-27'
|
29
29
|
spec.authors = ['Lee Kirchhoff']
|
30
30
|
spec.email = 'lee@rightscale.com'
|
31
31
|
spec.homepage = 'https://github.com/rightscale/right_amqp'
|
@@ -33,7 +33,7 @@ describe AMQP::Client do
|
|
33
33
|
class SUT
|
34
34
|
include AMQP::Client
|
35
35
|
|
36
|
-
attr_accessor :reconnecting, :settings, :channels
|
36
|
+
attr_accessor :reconnecting, :settings, :channels
|
37
37
|
end
|
38
38
|
|
39
39
|
before(:each) do
|
@@ -88,7 +88,7 @@ describe AMQP::Client do
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
-
context 'with a :reconnect_interval of 5 seconds'
|
91
|
+
context 'with a :reconnect_interval of 5 seconds' do
|
92
92
|
it 'should schedule reconnect attempts on a 5s interval' do
|
93
93
|
@sut.reconnecting = true
|
94
94
|
@sut.settings[:reconnect_delay] = 15
|
@@ -101,17 +101,6 @@ describe AMQP::Client do
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
-
context 'with a reconnect failure' do
|
105
|
-
it 'should fail the connection' do
|
106
|
-
@logger.should_receive(:error).with(/Failed to reconnect/).once
|
107
|
-
flexmock(EM).should_receive(:reconnect).and_raise(Exception).once
|
108
|
-
flexmock(@sut).should_receive(:close_connection).once
|
109
|
-
|
110
|
-
@sut.reconnect()
|
111
|
-
@sut.has_failed.should be_true
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
104
|
end
|
116
105
|
|
117
106
|
context "heartbeat" do
|