amq-client 0.7.0.alpha35 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|