amq-client 0.7.0.alpha35 → 0.8.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.
- data/.rspec +0 -1
- data/.travis.yml +9 -3
- data/Gemfile +22 -12
- data/amq-client.gemspec +1 -1
- data/examples/coolio_adapter/example_helper.rb +2 -0
- data/examples/eventmachine_adapter/basic_consume_with_acknowledgements.rb +3 -3
- data/examples/eventmachine_adapter/{connection_failure_callback.rb → error_handling/connection_failure_callback.rb} +4 -8
- data/examples/eventmachine_adapter/{connection_failure_exception.rb → error_handling/connection_failure_exception.rb} +5 -9
- data/examples/eventmachine_adapter/{connection_loss_handler.rb → error_handling/connection_loss_handler_that_fails_over.rb} +12 -12
- data/examples/eventmachine_adapter/error_handling/connection_loss_handler_with_automatic_recovery.rb +85 -0
- data/examples/eventmachine_adapter/error_handling/connection_loss_handler_with_manual_recovery.rb +85 -0
- data/examples/eventmachine_adapter/error_handling/handling_a_channel_level_exception.rb +2 -5
- data/examples/eventmachine_adapter/example_helper.rb +2 -0
- data/examples/eventmachine_adapter/server_capabilities.rb +12 -0
- data/examples/eventmachine_adapter/tls/tls_without_peer_verification.rb +2 -2
- data/lib/amq/client/async/adapter.rb +170 -31
- data/lib/amq/client/async/adapters/coolio.rb +18 -1
- data/lib/amq/client/async/adapters/event_machine.rb +48 -32
- data/lib/amq/client/async/adapters/eventmachine.rb +3 -1
- data/lib/amq/client/async/callbacks.rb +9 -7
- data/lib/amq/client/async/channel.rb +113 -20
- data/lib/amq/client/async/consumer.rb +270 -0
- data/lib/amq/client/async/exchange.rb +137 -16
- data/lib/amq/client/async/extensions/rabbitmq/confirm.rb +4 -4
- data/lib/amq/client/async/queue.rb +217 -113
- data/lib/amq/client/callbacks.rb +2 -0
- data/lib/amq/client/consumer_tag_generator.rb +24 -0
- data/lib/amq/client/exceptions.rb +10 -6
- data/lib/amq/client/handlers_registry.rb +2 -0
- data/lib/amq/client/queue.rb +2 -0
- data/lib/amq/client/server_named_entity.rb +1 -8
- data/lib/amq/client/settings.rb +64 -2
- data/lib/amq/client/version.rb +3 -1
- data/spec/benchmarks/adapters.rb +2 -0
- data/spec/client/framing/io_frame_spec.rb +9 -6
- data/spec/integration/coolio/basic_ack_spec.rb +2 -0
- data/spec/integration/coolio/basic_cancel_spec.rb +2 -0
- data/spec/integration/coolio/basic_consume_spec.rb +58 -0
- data/spec/integration/coolio/basic_get_spec.rb +2 -0
- data/spec/integration/coolio/basic_return_spec.rb +2 -0
- data/spec/integration/coolio/channel_close_spec.rb +2 -0
- data/spec/integration/coolio/channel_flow_spec.rb +2 -0
- data/spec/integration/coolio/connection_close_spec.rb +2 -0
- data/spec/integration/coolio/connection_start_spec.rb +2 -0
- data/spec/integration/coolio/exchange_declare_spec.rb +8 -6
- data/spec/integration/coolio/spec_helper.rb +2 -0
- data/spec/integration/coolio/tx_commit_spec.rb +2 -1
- data/spec/integration/coolio/tx_rollback_spec.rb +1 -1
- data/spec/integration/eventmachine/basic_ack_spec.rb +3 -1
- data/spec/integration/eventmachine/basic_cancel_spec.rb +2 -0
- data/spec/integration/eventmachine/basic_consume_spec.rb +90 -6
- data/spec/integration/eventmachine/basic_get_spec.rb +2 -0
- data/spec/integration/eventmachine/basic_return_spec.rb +2 -0
- data/spec/integration/eventmachine/channel_close_spec.rb +2 -0
- data/spec/integration/eventmachine/channel_flow_spec.rb +4 -2
- data/spec/integration/eventmachine/concurrent_basic_publish_spec.rb +79 -0
- data/spec/integration/eventmachine/connection_close_spec.rb +2 -0
- data/spec/integration/eventmachine/connection_start_spec.rb +2 -0
- data/spec/integration/eventmachine/exchange_declare_spec.rb +4 -2
- data/spec/integration/eventmachine/queue_declare_spec.rb +2 -0
- data/spec/integration/eventmachine/regressions/amqp_gem_issue66_spec.rb +2 -0
- data/spec/integration/eventmachine/spec_helper.rb +2 -0
- data/spec/integration/eventmachine/tx_commit_spec.rb +2 -1
- data/spec/integration/eventmachine/tx_rollback_spec.rb +1 -1
- data/spec/regression/bad_frame_slicing_in_adapters_spec.rb +2 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/client/settings_spec.rb +92 -3
- metadata +24 -23
- data/CONTRIBUTORS +0 -3
data/lib/amq/client/callbacks.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AMQ
|
4
|
+
module Client
|
5
|
+
class ConsumerTagGenerator
|
6
|
+
|
7
|
+
#
|
8
|
+
# API
|
9
|
+
#
|
10
|
+
|
11
|
+
# @return [String] Generated consumer tag
|
12
|
+
def generate
|
13
|
+
"#{Kernel.rand}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
|
14
|
+
end # generate
|
15
|
+
|
16
|
+
# @return [String] Generated consumer tag
|
17
|
+
def generate_for(queue)
|
18
|
+
raise ArgumentError, "argument must respond to :name" unless queue.respond_to?(:name)
|
19
|
+
|
20
|
+
"#{queue.name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
|
21
|
+
end # generate_for(queue)
|
22
|
+
end # ConsumerTagGenerator
|
23
|
+
end # Client
|
24
|
+
end # AMQ
|
@@ -51,12 +51,6 @@ module AMQ
|
|
51
51
|
# Client
|
52
52
|
#
|
53
53
|
|
54
|
-
class MissingInterfaceMethodError < NotImplementedError
|
55
|
-
def initialize(method_name)
|
56
|
-
super("Method #{method_name} is supposed to be overriden by adapter")
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
54
|
class MissingHandlerError < StandardError
|
61
55
|
def initialize(frame)
|
62
56
|
super("No callback registered for #{frame.method_class}")
|
@@ -97,5 +91,15 @@ module AMQ
|
|
97
91
|
end # initialize(settings)
|
98
92
|
end # PossibleAuthenticationFailureError
|
99
93
|
|
94
|
+
|
95
|
+
|
96
|
+
class UnknownExchangeTypeError < StandardError
|
97
|
+
BUILTIN_TYPES = [:fanout, :direct, :topic, :headers].freeze
|
98
|
+
|
99
|
+
def initialize(types, given)
|
100
|
+
super("#{given.inspect} exchange type is unknown. Standard types are #{BUILTIN_TYPES.inspect}, custom exchange types must begin with x-, for example: x-recent-history")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
100
104
|
end # Client
|
101
105
|
end # AMQ
|
data/lib/amq/client/queue.rb
CHANGED
@@ -7,17 +7,10 @@ module AMQ
|
|
7
7
|
|
8
8
|
# @return [Boolean] true if this entity is anonymous (server-named)
|
9
9
|
def server_named?
|
10
|
-
@name.nil?
|
10
|
+
@server_named || @name.nil? || @name.empty?
|
11
11
|
end
|
12
12
|
# backwards compabitility. MK.
|
13
13
|
alias anonymous? server_named?
|
14
|
-
|
15
|
-
def dup
|
16
|
-
if server_named?
|
17
|
-
raise RuntimeError.new("You can't clone anonymous queue until it receives back the name in Queue.Declare-Ok response. Move the code with #dup to the callback for the #declare method.") # TODO: that's not true in all cases, imagine the user didn't call #declare yet.
|
18
|
-
end
|
19
|
-
super
|
20
|
-
end
|
21
14
|
end # ServerNamedEntity
|
22
15
|
end # Client
|
23
16
|
end # AMQ
|
data/lib/amq/client/settings.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require "amq/protocol/client" # TODO: "amq/protocol/constants"
|
4
|
+
require "uri"
|
4
5
|
|
5
6
|
module AMQ
|
6
7
|
module Client
|
7
8
|
# @see AMQ::Client::Settings.configure
|
8
9
|
module Settings
|
10
|
+
# @private
|
11
|
+
AMQP_PORTS = {"amqp" => 5672, "amqps" => 5671}.freeze
|
12
|
+
|
13
|
+
# @private
|
14
|
+
AMQPS = "amqps".freeze
|
15
|
+
|
9
16
|
# Default connection settings used by AMQ clients
|
10
17
|
#
|
11
18
|
# @see AMQ::Client::Settings.configure
|
@@ -86,8 +93,63 @@ module AMQ
|
|
86
93
|
end
|
87
94
|
end
|
88
95
|
|
89
|
-
|
90
|
-
|
96
|
+
# Parses AMQP connection URI and returns its components as a hash.
|
97
|
+
#
|
98
|
+
# h2. vhost naming schemes
|
99
|
+
#
|
100
|
+
# It is convenient to be able to specify the AMQP connection
|
101
|
+
# parameters as a URI string, and various "amqp" URI schemes
|
102
|
+
# exist. Unfortunately, there is no standard for these URIs, so
|
103
|
+
# while the schemes share the basic idea, they differ in some
|
104
|
+
# details. This implementation aims to encourage URIs that work
|
105
|
+
# as widely as possible.
|
106
|
+
#
|
107
|
+
# The URI scheme should be "amqp", or "amqps" if SSL is required.
|
108
|
+
#
|
109
|
+
# The host, port, username and password are represented in the
|
110
|
+
# authority component of the URI in the same way as in http URIs.
|
111
|
+
#
|
112
|
+
# The vhost is obtained from the first segment of the path, with the
|
113
|
+
# leading slash removed. The path should contain only a single
|
114
|
+
# segment (i.e, the only slash in it should be the leading one).
|
115
|
+
# If the vhost is to include slashes or other reserved URI
|
116
|
+
# characters, these should be percent-escaped.
|
117
|
+
#
|
118
|
+
# @example How vhost is parsed
|
119
|
+
#
|
120
|
+
# AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com") # => vhost is nil, so default (/) will be used
|
121
|
+
# AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/") # => vhost is an empty string
|
122
|
+
# AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/%2Fvault") # => vhost is /vault
|
123
|
+
# AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/production") # => vhost is production
|
124
|
+
# AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/a.b.c") # => vhost is a.b.c
|
125
|
+
# AMQ::Client::Settings.parse_amqp_url("amqp://dev.rabbitmq.com/foo/bar") # => ArgumentError
|
126
|
+
#
|
127
|
+
#
|
128
|
+
# @param [String] connection_string AMQP connection URI, à la JDBC connection string. For example: amqp://bus.megacorp.internal:5877.
|
129
|
+
# @return [Hash] Connection parameters (:username, :password, :vhost, :host, :port, :ssl)
|
130
|
+
#
|
131
|
+
# @raise [ArgumentError] When connection URI schema is not amqp or amqps, or the path contains multiple segments
|
132
|
+
#
|
133
|
+
# @see http://bit.ly/ks8MXK Connecting to The Broker documentation guide
|
134
|
+
# @api public
|
135
|
+
def self.parse_amqp_url(connection_string)
|
136
|
+
uri = URI.parse(connection_string)
|
137
|
+
raise ArgumentError.new("Connection URI must use amqp or amqps schema (example: amqp://bus.megacorp.internal:5766), learn more at http://bit.ly/ks8MXK") unless %w{amqp amqps}.include?(uri.scheme)
|
138
|
+
|
139
|
+
opts = {}
|
140
|
+
|
141
|
+
opts[:scheme] = uri.scheme
|
142
|
+
opts[:user] = URI.unescape(uri.user) if uri.user
|
143
|
+
opts[:pass] = URI.unescape(uri.password) if uri.password
|
144
|
+
opts[:host] = uri.host if uri.host
|
145
|
+
opts[:port] = uri.port || AMQ::Client::Settings::AMQP_PORTS[uri.scheme]
|
146
|
+
opts[:ssl] = uri.scheme == AMQ::Client::Settings::AMQPS
|
147
|
+
if uri.path =~ %r{^/(.*)}
|
148
|
+
raise ArgumentError.new("#{uri} has multiple-segment path; please percent-encode any slashes in the vhost name (e.g. /production => %2Fproduction). Learn more at http://bit.ly/amqp-gem-and-connection-uris") if $1.index('/')
|
149
|
+
opts[:vhost] = URI.unescape($1)
|
150
|
+
end
|
151
|
+
|
152
|
+
opts
|
91
153
|
end
|
92
154
|
end
|
93
155
|
end
|
data/lib/amq/client/version.rb
CHANGED
data/spec/benchmarks/adapters.rb
CHANGED
@@ -45,13 +45,16 @@ describe AMQ::Client::Framing::IO do
|
|
45
45
|
subject.decode(@io).payload.should eql("\x00\n\x00(\x01/\x00\x00")
|
46
46
|
end
|
47
47
|
|
48
|
-
|
49
|
-
|
48
|
+
context "if the frame length is miscalculated" do
|
49
|
+
it "should raise an error"
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
|
53
|
+
context "if frame doesn't end with FINAL_OCTET" do
|
54
|
+
it "should raise an error" do
|
55
|
+
data = @io.read[0..-2] + "too long" + "\xCE"
|
56
|
+
io = StringIO.new(data)
|
57
|
+
lambda { subject.decode(io) }.should raise_error(AMQ::Client::NoFinalOctetError)
|
58
|
+
end
|
56
59
|
end
|
57
60
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'integration/coolio/spec_helper'
|
3
5
|
|
@@ -87,3 +89,59 @@ describe "AMQ::Client::CoolioClient", "Basic.Consume", :nojruby => true do
|
|
87
89
|
end # it "should not leave messages in the queues with noack=true"
|
88
90
|
end # context "sending 100 messages"
|
89
91
|
end # describe AMQ::Client::CoolioClient, "Basic.Consume"
|
92
|
+
|
93
|
+
|
94
|
+
describe "Multiple", AMQ::Client::Async::Consumer, :nojruby => true do
|
95
|
+
include EventedSpec::SpecHelper
|
96
|
+
default_timeout 4
|
97
|
+
|
98
|
+
context "sharing the same queue with equal prefetch levels" do
|
99
|
+
let(:messages) { (0..99).map {|i| "Message #{i}" } }
|
100
|
+
|
101
|
+
it "have messages distributed to them in the round-robin manner" do
|
102
|
+
@consumer1_mailbox = []
|
103
|
+
@consumer2_mailbox = []
|
104
|
+
@consumer3_mailbox = []
|
105
|
+
|
106
|
+
coolio_amqp_connect do |client|
|
107
|
+
channel = AMQ::Client::Channel.new(client, 1)
|
108
|
+
channel.open do
|
109
|
+
queue = AMQ::Client::Queue.new(client, channel).declare(false, false, false, true)
|
110
|
+
queue.bind("amq.fanout")
|
111
|
+
|
112
|
+
consumer1 = AMQ::Client::Async::Consumer.new(channel, queue, "#{queue.name}-consumer-#{Time.now}")
|
113
|
+
consumer2 = AMQ::Client::Async::Consumer.new(channel, queue)
|
114
|
+
consumer3 = AMQ::Client::Async::Consumer.new(channel, queue, "#{queue.name}-consumer-#{rand}-#{Time.now}", false, true)
|
115
|
+
|
116
|
+
|
117
|
+
consumer1.consume.on_delivery do |method, header, payload|
|
118
|
+
@consumer1_mailbox << payload
|
119
|
+
end
|
120
|
+
|
121
|
+
consumer2.consume(true).on_delivery do |method, header, payload|
|
122
|
+
@consumer2_mailbox << payload
|
123
|
+
end
|
124
|
+
|
125
|
+
consumer3.consume(false) do
|
126
|
+
puts "Consumer 3 is ready"
|
127
|
+
end
|
128
|
+
consumer3.on_delivery do |method, header, payload|
|
129
|
+
@consumer3_mailbox << payload
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
exchange = AMQ::Client::Exchange.new(client, channel, "amq.fanout", :fanout)
|
134
|
+
messages.each do |message|
|
135
|
+
exchange.publish(message)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
done(1.5) {
|
140
|
+
@consumer1_mailbox.size.should == 34
|
141
|
+
@consumer2_mailbox.size.should == 33
|
142
|
+
@consumer3_mailbox.size.should == 33
|
143
|
+
}
|
144
|
+
end
|
145
|
+
end # it
|
146
|
+
end # context
|
147
|
+
end # describe
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'integration/coolio/spec_helper'
|
3
5
|
|
4
6
|
describe "AMQ::Client::CoolioClient", "Exchange.Declare", :nojruby => true do
|
5
7
|
include EventedSpec::SpecHelper
|
6
|
-
default_timeout
|
8
|
+
default_timeout 2
|
7
9
|
let(:exchange_name) { "amq-client.testexchange.#{Time.now.to_i}" }
|
8
10
|
it "should create an exchange and trigger a callback" do
|
9
11
|
coolio_amqp_connect do |client|
|
@@ -12,7 +14,7 @@ describe "AMQ::Client::CoolioClient", "Exchange.Declare", :nojruby => true do
|
|
12
14
|
exchange = AMQ::Client::Exchange.new(client, channel, exchange_name, :fanout)
|
13
15
|
exchange.declare do
|
14
16
|
exchange.delete
|
15
|
-
done(0.
|
17
|
+
done(0.5)
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
@@ -33,7 +35,7 @@ describe "AMQ::Client::CoolioClient", "Exchange.Declare", :nojruby => true do
|
|
33
35
|
@callback_fired = true
|
34
36
|
end
|
35
37
|
delayed(0.1) { exchange.delete }
|
36
|
-
done(0.
|
38
|
+
done(0.5)
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
@@ -55,7 +57,7 @@ describe "AMQ::Client::CoolioClient", "Exchange.Declare", :nojruby => true do
|
|
55
57
|
end
|
56
58
|
end
|
57
59
|
delayed(0.1) { exchange.delete }
|
58
|
-
done(0.
|
60
|
+
done(0.5)
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
@@ -77,7 +79,7 @@ describe "AMQ::Client::CoolioClient", "Exchange.Declare", :nojruby => true do
|
|
77
79
|
@callback_fired = true
|
78
80
|
end
|
79
81
|
delayed(0.1) { exchange.delete }
|
80
|
-
done(0.
|
82
|
+
done(0.5)
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
@@ -99,7 +101,7 @@ describe "AMQ::Client::CoolioClient", "Exchange.Declare", :nojruby => true do
|
|
99
101
|
end
|
100
102
|
end
|
101
103
|
delayed(0.1) { exchange.delete }
|
102
|
-
done(0.
|
104
|
+
done(0.5)
|
103
105
|
end
|
104
106
|
end
|
105
107
|
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
|
2
3
|
require 'spec_helper'
|
3
4
|
require 'integration/coolio/spec_helper'
|
4
5
|
|
5
6
|
describe "AMQ::Client::CoolioClient", "Tx.Commit", :nojruby => true do
|
6
7
|
include EventedSpec::SpecHelper
|
7
|
-
default_timeout
|
8
|
+
default_timeout 4
|
8
9
|
let(:message) { "Hello, world!" }
|
9
10
|
|
10
11
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'integration/eventmachine/spec_helper'
|
3
5
|
|
@@ -28,7 +30,7 @@ describe AMQ::Client::EventMachineClient, "Basic.Ack" do
|
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
|
-
done(
|
33
|
+
done(3.5) {
|
32
34
|
@received_messages =~ messages
|
33
35
|
}
|
34
36
|
end
|