amqp 0.8.0.rc14 → 0.8.0.rc15
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -3
- data/Gemfile +9 -6
- data/README.md +18 -12
- data/amqp.gemspec +2 -2
- data/bin/docup +3 -0
- data/docs/08Migration.textile +67 -5
- data/docs/AMQP091ModelExplained.textile +138 -101
- data/docs/Bindings.textile +109 -8
- data/docs/ConnectingToTheBroker.textile +8 -0
- data/docs/ConnectionEncryptionWithTLS.textile +5 -0
- data/docs/DocumentationGuidesIndex.textile +21 -5
- data/docs/Durability.textile +3 -1
- data/docs/ErrorHandling.textile +20 -0
- data/docs/Exchanges.textile +7 -1
- data/docs/GettingStarted.textile +10 -0
- data/docs/PatternsAndUseCases.textile +6 -0
- data/docs/Queues.textile +7 -1
- data/docs/RabbitMQVersions.textile +6 -1
- data/docs/RunningTests.textile +8 -3
- data/docs/Troubleshooting.textile +31 -0
- data/docs/VendorSpecificExtensions.textile +137 -6
- data/examples/extensions/rabbitmq/per_queue_message_ttl.rb +24 -25
- data/examples/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +11 -20
- data/examples/extensions/rabbitmq/using_alternate_exchanges.rb +28 -0
- data/examples/hello_world.rb +1 -1
- data/lib/amqp.rb +1 -0
- data/lib/amqp/compatibility/ruby187_patchlevel_check.rb +2 -0
- data/lib/amqp/integration/rails.rb +17 -0
- data/lib/amqp/version.rb +1 -1
- data/spec/integration/authentication_spec.rb +2 -2
- data/spec/integration/fanout_exchange_routing_spec.rb +43 -199
- data/spec/integration/multiple_consumers_per_queue_spec.rb +7 -7
- data/spec/integration/regressions/concurrent_publishing_on_the_same_channel_spec.rb +1 -1
- data/spec/integration/stress/publishing_of_messages_with_incrementing_sizes_spec.rb +50 -0
- metadata +13 -9
@@ -68,6 +68,32 @@ version 2.0 or later.
|
|
68
68
|
TBD
|
69
69
|
|
70
70
|
|
71
|
+
h2. Handling channel-level exceptions
|
72
|
+
|
73
|
+
A broad range of problems result in AMQP channel exceptions: an indication by the broker that there was an issue application needs to be aware of.
|
74
|
+
Channel-level exceptions are typically not fatal and can be recovered from. Some examples are:
|
75
|
+
|
76
|
+
* Exchange is re-declared with attributes different from the original declaration. For example, a non-durable exchange is being re-declared as durable.
|
77
|
+
* Queue is re-declared with attributes different from the original declaration. For example, an autodeletable queue is being re-declared as non-autodeletable.
|
78
|
+
* Queue is bound to an exchange that does not exist.
|
79
|
+
|
80
|
+
and so on. When troubleshooting AMQP applications, it is recommended that you detect and handle channel-level exceptions on all channels your
|
81
|
+
application may use. For that, use {AMQP::Channel#on_error} method as demonstrated below:
|
82
|
+
|
83
|
+
<pre>
|
84
|
+
<code>
|
85
|
+
events_channel.on_error do |ch, channel_close|
|
86
|
+
puts "Channel-level exception on the events channel: #{channel_close.reply_text}"
|
87
|
+
end
|
88
|
+
|
89
|
+
commands_channel.on_error do |ch, channel_close|
|
90
|
+
puts "Channel-level exception on the commands channel: #{channel_close.reply_text}"
|
91
|
+
end
|
92
|
+
</code>
|
93
|
+
</pre>
|
94
|
+
|
95
|
+
Defining channel-level exception handlers will reveal many issues that it might take more time to detect using other troubleshooting techniques.
|
96
|
+
|
71
97
|
|
72
98
|
h2. Testing network connection with AMQP broker using Telnet
|
73
99
|
|
@@ -117,6 +143,11 @@ suggests that *erlang-os-mon* package is not installed.
|
|
117
143
|
|
118
144
|
|
119
145
|
|
146
|
+
h2. Authors
|
147
|
+
|
148
|
+
This guide was written by "Michael Klishin":http://twitter.com/michaelklishin and edited by "Chris Duncan":https://twitter.com/celldee.
|
149
|
+
|
150
|
+
|
120
151
|
|
121
152
|
h2. Tell us what you think!
|
122
153
|
|
@@ -4,14 +4,19 @@ h1. Vendor-specific AMQP extensions support in amqp gem
|
|
4
4
|
|
5
5
|
h2. RabbitMQ extensions
|
6
6
|
|
7
|
-
|
7
|
+
h2. Supported extensions
|
8
8
|
|
9
|
-
AMQP gem supports
|
9
|
+
AMQP gem supports many RabbitMQ extensions to AMQP 0.9.1:
|
10
10
|
|
11
11
|
* "Publisher confirmations":http://www.rabbitmq.com/extensions.html#confirms (confirm.* class)
|
12
12
|
* "Negative acknowledgements":http://www.rabbitmq.com/extensions.html#negative-acknowledgements (basic.nack)
|
13
|
+
* "Alternate Exchanges":http://www.rabbitmq.com/extensions.html#alternate-exchange
|
14
|
+
* "Per-queue Message Time-to-Live":http://www.rabbitmq.com/extensions.html#queue-ttl
|
15
|
+
* "Queue Leases":http://www.rabbitmq.com/extensions.html#queue-leases
|
16
|
+
* "Sender-selected Distribution":http://www.rabbitmq.com/extensions.html#sender-selected-distribution
|
17
|
+
* "Validated user_id":http://www.rabbitmq.com/extensions.html#validated-user-id
|
13
18
|
|
14
|
-
|
19
|
+
h2. Enabling RabbitMQ extensions
|
15
20
|
|
16
21
|
If you are using RabbitMQ as AMQP broker and want to use these extensions, simply replace
|
17
22
|
|
@@ -24,14 +29,140 @@ require "amqp"
|
|
24
29
|
require "amqp/extensions/rabbitmq"
|
25
30
|
</pre>
|
26
31
|
|
27
|
-
|
32
|
+
|
33
|
+
h2. Per-queue Message Time-to-Live
|
34
|
+
|
35
|
+
Per-queue Message Time-to-Live (TTL) is a RabbitMQ extension to AMQP 0.9.1 that lets developers control for how long
|
36
|
+
a message published to a queue can live before it is discarded. A message that has been in the queue for longer than the
|
37
|
+
configured TTL is said to be dead. Dead messages will not be delivered to consumers and cannot be fetched using
|
38
|
+
*basic.get* operation ({AMQP::Queue#pop}).
|
39
|
+
|
40
|
+
Message TTL is specified using *x-message-ttl* argument on declaration. With amqp gem, you pass it to {AMQP::Queue#initialize} or
|
41
|
+
{AMQP::Channel#queue}:
|
42
|
+
|
43
|
+
<pre>
|
44
|
+
<code>
|
45
|
+
# 1000 milliseconds
|
46
|
+
channel.queue("", :arguments => { "x-message-ttl" => 1000 })
|
47
|
+
</code>
|
48
|
+
</pre>
|
49
|
+
|
50
|
+
When a published messages is routed to multiple queues, each of the queues gets a _copy of the message_. If then the message dies in
|
51
|
+
one of the queues, it has no effect on copies of the message in other queues.
|
52
|
+
|
53
|
+
h3. Example
|
54
|
+
|
55
|
+
The example below sets message TTL for a new server-named queue to be 1000 milliseconds. It then publishes several messages that
|
56
|
+
are routed to the queue and tries to fetch messages using *basic.get* AMQP method ({AMQP::Queue#pop} after 0.7 and 1.5 seconds:
|
57
|
+
|
58
|
+
<script src="https://gist.github.com/1113127.js"> </script>
|
59
|
+
|
60
|
+
|
61
|
+
h3. Learn More
|
62
|
+
|
63
|
+
See also rabbitmq.com section on "Per-queue Message TTL":http://www.rabbitmq.com/extensions.html#queue-ttl
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
h2. Publisher Confirms (Publisher Acknowledgements)
|
68
|
+
|
69
|
+
In some situations not a single message can be lost. The only reliable way of doing so is using confirmations.
|
70
|
+
"Publisher Confirms AMQP extension":http://www.rabbitmq.com/blog/2011/02/10/introducing-publisher-confirms/ was designed to solve the reliable publishing problem.
|
71
|
+
|
72
|
+
Publisher confirms are similar to message acknowledgements documented in the {file:docs/Queues.textile Working With Queues} guide but involve publisher and AMQP broker
|
73
|
+
instead of consumer and AMQP broker.
|
74
|
+
|
75
|
+
|
76
|
+
h3. Public API
|
77
|
+
|
78
|
+
To use publisher confirmations, first put channel into confirmation mode using {AMQP::Channel#confirm_select}:
|
79
|
+
|
80
|
+
<pre>
|
81
|
+
<code>
|
82
|
+
channel.confirm_select
|
83
|
+
</code>
|
84
|
+
</pre>
|
85
|
+
|
86
|
+
From this moment on, every message published on this channel will cause channel's _publisher index_ (message counter) to be incremented. It is possible to access
|
87
|
+
using {AMQP::Channel#publisher_index} method. To check whether channel is in the confirmation mode, use {AMQP::Channel#uses_publisher_confirmations?} predicate.
|
88
|
+
|
89
|
+
To handle AMQP broker acknowledgements, define a handler using {AMQP::Channel#on_ack}, for example:
|
90
|
+
|
91
|
+
<pre>
|
92
|
+
<code>
|
93
|
+
channel.on_ack do |basic_ack|
|
94
|
+
puts "Received basic_ack: multiple = #{basic_ack.multiple}, delivery_tag = #{basic_ack.delivery_tag}"
|
95
|
+
end
|
96
|
+
</code>
|
97
|
+
</pre>
|
98
|
+
|
99
|
+
Delivery tag will indicate number of confirmed messages. If *multiple* attribute is true, the confirmation is for all messages up to the number
|
100
|
+
delivery tag indicates. In other words, AMQP broker may confirm just one message or a batch of them.
|
101
|
+
|
102
|
+
|
103
|
+
h3. Example
|
28
104
|
|
29
105
|
<script src="https://gist.github.com/923599.js?file=rabbitmq_publisher_confirmations_with_amqp_gem_0.8.0.rb"></script>
|
30
106
|
|
31
107
|
|
32
|
-
h3.
|
108
|
+
h3. Learn More
|
109
|
+
|
110
|
+
See also rabbitmq.com section on "Confirms aka Publisher Acknowledgements":http://www.rabbitmq.com/extensions.html#confirms
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
h2. basic.nack
|
116
|
+
|
117
|
+
The AMQP specification defines the basic.reject method that allows clients to reject individual, delivered messages, instructing the broker
|
118
|
+
to either discard them or requeue them. Unfortunately, basic.reject provides no support for negatively acknowledging messages in bulk.
|
119
|
+
|
120
|
+
To solve this, RabbitMQ supports the basic.nack method that provides all the functionality of basic.reject whilst also
|
121
|
+
allowing for bulk processing of messages.
|
122
|
+
|
123
|
+
h3. Public API
|
124
|
+
|
125
|
+
When RabbitMQ extensions are loaded, {AMQP::Channel#reject} method is overriden via mixin to take one additional argument: multi (defaults to false).
|
126
|
+
When it is given and is true, amqp gem will use basic.nack AMQP method instead of basic.reject, to reject multiple messages at once.
|
127
|
+
Otherwise, basic.reject is used as usual.
|
128
|
+
|
129
|
+
|
130
|
+
h3. Learn More
|
131
|
+
|
132
|
+
See also rabbitmq.com section on "Confirms aka Publisher Acknowledgements":http://www.rabbitmq.com/extensions.html#negative-acknowledgements
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
h2. Alternate Exchanges
|
137
|
+
|
138
|
+
Alternate Exchanges is a RabbitMQ extension to AMQP 0.9.1 that lets developers define "fallback" exchanges where unroutable messages will be sent.
|
139
|
+
|
140
|
+
|
141
|
+
h3. Public API
|
142
|
+
|
143
|
+
To specify exchange A as alternate exchange to exchange B, specify 'alternate-exchange' argument on declaration of B:
|
144
|
+
|
145
|
+
<pre>
|
146
|
+
<code>
|
147
|
+
exchange1 = channel.fanout("ops.fallback", :auto_delete => true)
|
148
|
+
exchange2 = channel.fanout("events.collector", :auto_delete => true, :arguments => { "alternate-exchange" => "ops.fallback" })
|
149
|
+
</code>
|
150
|
+
</pre>
|
151
|
+
|
152
|
+
|
153
|
+
h3. Example
|
154
|
+
|
155
|
+
<script src="https://gist.github.com/1159091.js?file=using_alternate_exchanges.rb"></script>
|
156
|
+
|
157
|
+
|
158
|
+
h3. Learn More
|
159
|
+
|
160
|
+
See also rabbitmq.com section on "Alternate Exchanges":http://www.rabbitmq.com/extensions.html#alternate-exchange
|
161
|
+
|
162
|
+
|
163
|
+
h2. Authors
|
33
164
|
|
34
|
-
|
165
|
+
This guide was written by "Michael Klishin":http://twitter.com/michaelklishin and edited by "Chris Duncan":https://twitter.com/celldee.
|
35
166
|
|
36
167
|
|
37
168
|
|
@@ -10,44 +10,43 @@ require "amqp/extensions/rabbitmq"
|
|
10
10
|
|
11
11
|
AMQP.start do |connection|
|
12
12
|
puts "Connected!"
|
13
|
-
AMQP::Channel.new(connection)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
puts "Oops! a channel-level exception: #{channel_close.reply_text}"
|
18
|
-
end
|
13
|
+
channel = AMQP::Channel.new(connection)
|
14
|
+
channel.on_error do |ch, channel_close|
|
15
|
+
puts "Oops! a channel-level exception: #{channel_close.reply_text}"
|
16
|
+
end
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
x = channel.fanout("amq.fanout")
|
19
|
+
channel.queue("", :auto_delete => true, :arguments => { "x-message-ttl" => 1000 }) do |q|
|
20
|
+
puts "Declared a new server-named queue: #{q.name}"
|
21
|
+
q.bind(x)
|
24
22
|
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
24
|
+
EventMachine.add_timer(0.3) do
|
25
|
+
10.times do |i|
|
26
|
+
puts "Publishing message ##{i}"
|
27
|
+
x.publish("Message ##{i}")
|
31
28
|
end
|
29
|
+
end
|
32
30
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
31
|
+
EventMachine.add_timer(0.7) do
|
32
|
+
q.pop do |headers, payload|
|
33
|
+
puts "Got a message: #{payload}"
|
37
34
|
end
|
35
|
+
end
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
EventMachine.add_timer(1.5) do
|
38
|
+
q.pop do |headers, payload|
|
39
|
+
if payload.nil?
|
40
|
+
puts "No messages in the queue"
|
41
|
+
else
|
42
|
+
raise "x-message-ttl didn't seem to work (timeout isn't up)"
|
42
43
|
end
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
48
|
show_stopper = Proc.new {
|
48
|
-
AMQP.stop
|
49
|
-
EM.stop
|
50
|
-
end
|
49
|
+
AMQP.stop { EventMachine.stop }
|
51
50
|
}
|
52
51
|
|
53
52
|
|
@@ -8,46 +8,37 @@ $:.unshift(File.expand_path("../../../../lib", __FILE__))
|
|
8
8
|
require 'amqp'
|
9
9
|
require "amqp/extensions/rabbitmq"
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
EventMachine.run do
|
12
|
+
connection = AMQP.connect(:host => '127.0.0.1')
|
13
|
+
puts "Connecting to AMQP broker. Running #{AMQP::VERSION} version of the gem..."
|
14
|
+
|
13
15
|
AMQP::Channel.new(connection) do |channel|
|
14
16
|
puts "Channel #{channel.id} is now open"
|
15
17
|
|
16
18
|
channel.confirm_select
|
17
|
-
channel.on_error
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
|
22
|
-
channel.on_ack do |basic_ack|
|
23
|
-
puts "Received basic_ack: multiple = #{basic_ack.multiple}, delivery_tag = #{basic_ack.delivery_tag}"
|
24
|
-
end
|
19
|
+
channel.on_error { |ch, channel_close| puts "Oops! a channel-level exception: #{channel_close.reply_text}" }
|
20
|
+
channel.on_ack { |basic_ack| puts "Received basic_ack: multiple = #{basic_ack.multiple}, delivery_tag = #{basic_ack.delivery_tag}" }
|
25
21
|
|
26
22
|
x = channel.fanout("amq.fanout")
|
27
23
|
channel.queue("", :auto_delete => true) do |q|
|
28
|
-
|
29
|
-
|
30
|
-
q.bind(x, :no_ack => true).subscribe(:ack => true) do |header, payload|
|
24
|
+
q.bind(x).subscribe(:ack => true) do |header, payload|
|
31
25
|
puts "Received #{payload}"
|
32
26
|
end
|
33
27
|
end
|
34
28
|
|
35
29
|
EventMachine.add_timer(0.5) do
|
36
30
|
10.times do |i|
|
37
|
-
puts "Publishing message ##{i}"
|
38
|
-
x.publish("Message ##{i}")
|
31
|
+
puts "Publishing message ##{i + 1}"
|
32
|
+
x.publish("Message ##{i + 1}")
|
39
33
|
end
|
40
34
|
end
|
41
35
|
end
|
42
36
|
|
43
37
|
show_stopper = Proc.new {
|
44
|
-
|
45
|
-
EM.stop
|
46
|
-
end
|
38
|
+
connection.close { EventMachine.stop }
|
47
39
|
}
|
48
40
|
|
49
|
-
|
50
|
-
EM.add_timer(3, show_stopper)
|
41
|
+
EM.add_timer(6, show_stopper)
|
51
42
|
Signal.trap('INT', show_stopper)
|
52
43
|
Signal.trap('TERM', show_stopper)
|
53
44
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "bundler"
|
5
|
+
Bundler.setup
|
6
|
+
|
7
|
+
$:.unshift(File.expand_path("../../../../lib", __FILE__))
|
8
|
+
|
9
|
+
require 'amqp'
|
10
|
+
|
11
|
+
EventMachine.run do
|
12
|
+
connection = AMQP.connect(:host => '127.0.0.1')
|
13
|
+
puts "Connecting to AMQP broker. Running #{AMQP::VERSION} version of the gem..."
|
14
|
+
|
15
|
+
channel = AMQP::Channel.new(connection)
|
16
|
+
queue = channel.queue("amqpgem.examples.hello_world", :auto_delete => true)
|
17
|
+
exchange1 = channel.fanout("my.fanout1", :auto_delete => true)
|
18
|
+
exchange2 = channel.fanout("my.fanout2", :auto_delete => true, :arguments => { "alternate-exchange" => "my.fanout1" })
|
19
|
+
|
20
|
+
queue.bind(exchange1).subscribe do |payload|
|
21
|
+
puts "Received a message: #{payload}. Disconnecting..."
|
22
|
+
|
23
|
+
connection.close { EventMachine.stop }
|
24
|
+
end
|
25
|
+
|
26
|
+
exchange1.publish "This message will be routed because of the binding", :mandatory => true
|
27
|
+
exchange2.publish "This message will be re-routed to alternate-exchange", :mandatory => true
|
28
|
+
end
|
data/examples/hello_world.rb
CHANGED
@@ -14,7 +14,7 @@ EventMachine.run do
|
|
14
14
|
|
15
15
|
channel = AMQP::Channel.new(connection)
|
16
16
|
queue = channel.queue("amqpgem.examples.hello_world", :auto_delete => true)
|
17
|
-
exchange = channel.
|
17
|
+
exchange = channel.default_exchange
|
18
18
|
|
19
19
|
queue.subscribe do |payload|
|
20
20
|
puts "Received a message: #{payload}. Disconnecting..."
|
data/lib/amqp.rb
CHANGED
@@ -14,6 +14,8 @@ this issue.
|
|
14
14
|
|
15
15
|
To reiterate: 1.8.7-p174 and 1.8.7-p334 are supported. The fix has been committed to MRI in December 2009. It's
|
16
16
|
a good idea to upgrade, although downgrading to p174 is an option, too.
|
17
|
+
|
18
|
+
To learn more (including the 0.8.x migration guide) at http://bit.ly/amqp-gem-docs and https://github.com/ruby-amqp.
|
17
19
|
MESSAGE
|
18
20
|
end
|
19
21
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
module Integration
|
5
|
+
class Rails
|
6
|
+
|
7
|
+
def self.start(options = {}, &block)
|
8
|
+
yaml = YAML.load_file(File.join(::Rails.root, "config", "amqp.yml"))
|
9
|
+
settings = yaml.fetch(::Rails.env, Hash.new).symbolize_keys
|
10
|
+
|
11
|
+
EventMachine.next_tick do
|
12
|
+
AMQP.start(settings.merge(options), &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end # Rails
|
16
|
+
end # Integration
|
17
|
+
end # AMQP
|
data/lib/amqp/version.rb
CHANGED
@@ -67,7 +67,7 @@ describe "Authentication attempt" do
|
|
67
67
|
end # context
|
68
68
|
|
69
69
|
context "and provided credentials ARE INCORRECT" do
|
70
|
-
default_timeout
|
70
|
+
default_timeout 10
|
71
71
|
|
72
72
|
after(:all) { done }
|
73
73
|
|
@@ -83,7 +83,7 @@ describe "Authentication attempt" do
|
|
83
83
|
|
84
84
|
|
85
85
|
context "and provided vhost DOES NOT EXIST" do
|
86
|
-
default_timeout
|
86
|
+
default_timeout 10
|
87
87
|
|
88
88
|
after(:all) { done }
|
89
89
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
describe "
|
5
|
+
describe AMQP::Exchange, "of type fanout" do
|
6
6
|
|
7
7
|
#
|
8
8
|
# Environment
|
@@ -32,215 +32,59 @@ describe "Workload distribution" do
|
|
32
32
|
# Examples
|
33
33
|
#
|
34
34
|
|
35
|
-
context "that uses fanout exchange" do
|
36
|
-
amqp_before :each do
|
37
|
-
@exchange = @channel.fanout("amqpgem.integration.multicast.fanout", :auto_delete => true)
|
38
|
-
end
|
39
|
-
|
40
|
-
context "with three bound queues" do
|
41
|
-
amqp_before :each do
|
42
|
-
@queue1 = @channel.queue("amqpgem.integration.multicast.queue1", :auto_delete => true)
|
43
|
-
@queue2 = @channel.queue("amqpgem.integration.multicast.queue2", :auto_delete => true)
|
44
|
-
@queue3 = @channel.queue("amqpgem.integration.multicast.queue3", :auto_delete => true)
|
45
|
-
|
46
|
-
@queues = [@queue1, @queue2, @queue3]
|
47
|
-
|
48
|
-
@sent_values = Array.new
|
49
|
-
|
50
|
-
@queue1.bind(@exchange).subscribe do |payload|
|
51
|
-
@received_messages[@queue1.name].push(payload.to_i)
|
52
|
-
end # subscribe
|
53
|
-
|
54
|
-
@queue2.bind(@exchange).subscribe do |payload|
|
55
|
-
@received_messages[@queue2.name].push(payload.to_i)
|
56
|
-
end # subscribe
|
57
|
-
|
58
|
-
@queue3.bind(@exchange).subscribe do |payload|
|
59
|
-
@received_messages[@queue3.name].push(payload.to_i)
|
60
|
-
end # subscribe
|
61
|
-
end
|
62
|
-
|
63
|
-
amqp_before :each do
|
64
|
-
@received_messages = {
|
65
|
-
@queue1.name => [],
|
66
|
-
@queue2.name => [],
|
67
|
-
@queue3.name => []
|
68
|
-
}
|
69
|
-
|
70
|
-
@expected_number_of_messages = {
|
71
|
-
@queue1.name => 100,
|
72
|
-
@queue2.name => 100,
|
73
|
-
@queue3.name => 100
|
74
|
-
}
|
75
|
-
end
|
76
|
-
|
77
|
-
amqp_after :each do
|
78
|
-
@sent_values.clear
|
79
|
-
end
|
80
|
-
|
81
|
-
|
82
|
-
context "and messages are published as non-mandatory" do
|
83
|
-
it "routes all messages to all bound queues" do
|
84
|
-
100.times do
|
85
|
-
dispatched_data = rand(5_000_000)
|
86
|
-
@sent_values.push(dispatched_data)
|
87
|
-
|
88
|
-
@exchange.publish(dispatched_data, :mandatory => false)
|
89
|
-
end
|
90
|
-
|
91
|
-
# for Rubinius, it is surprisingly slow on this workload
|
92
|
-
done(3.5) {
|
93
|
-
[@queue1, @queue2, @queue3].each do |q|
|
94
|
-
@received_messages[q.name].size.should == @expected_number_of_messages[q.name]
|
95
|
-
|
96
|
-
# this one is ordering assertion
|
97
|
-
@received_messages[q.name].should == @sent_values
|
98
|
-
end
|
99
|
-
}
|
100
|
-
end # it
|
101
|
-
end # context
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
context "and messages are published as mandatory" do
|
106
|
-
it "routes all messages to all bound queues" do
|
107
|
-
100.times do
|
108
|
-
dispatched_data = rand(5_000_000)
|
109
|
-
@sent_values.push(dispatched_data)
|
110
|
-
|
111
|
-
@exchange.publish(dispatched_data, :mandatory => true)
|
112
|
-
end
|
113
35
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
@received_messages[q.name].should == @sent_values
|
121
|
-
end
|
122
|
-
}
|
123
|
-
end # it
|
124
|
-
end # context
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
context "and messages are published as non-persistent" do
|
129
|
-
it "routes all messages to all bound queues" do
|
130
|
-
100.times do
|
131
|
-
dispatched_data = rand(5_000_000)
|
132
|
-
@sent_values.push(dispatched_data)
|
133
|
-
|
134
|
-
@exchange.publish(dispatched_data, :persistent => false)
|
135
|
-
end
|
136
|
-
|
137
|
-
# 6 seconds are for Rubinius, it is surprisingly slow on this workload
|
138
|
-
done(3.5) {
|
139
|
-
[@queue1, @queue2, @queue3].each do |q|
|
140
|
-
@received_messages[q.name].size.should == @expected_number_of_messages[q.name]
|
141
|
-
|
142
|
-
# this one is ordering assertion
|
143
|
-
@received_messages[q.name].should == @sent_values
|
144
|
-
end
|
145
|
-
}
|
146
|
-
end # it
|
147
|
-
end # context
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
context "and messages are published as persistent" do
|
152
|
-
it "routes all messages to all bound queues" do
|
153
|
-
100.times do
|
154
|
-
dispatched_data = rand(5_000_000)
|
155
|
-
@sent_values.push(dispatched_data)
|
156
|
-
|
157
|
-
@exchange.publish(dispatched_data, :persistent => true)
|
158
|
-
end
|
159
|
-
|
160
|
-
# 6 seconds are for Rubinius, it is surprisingly slow on this workload
|
161
|
-
done(3.5) {
|
162
|
-
[@queue1, @queue2, @queue3].each do |q|
|
163
|
-
@received_messages[q.name].size.should == @expected_number_of_messages[q.name]
|
164
|
-
|
165
|
-
# this one is ordering assertion
|
166
|
-
@received_messages[q.name].should == @sent_values
|
167
|
-
end
|
168
|
-
}
|
169
|
-
end # it
|
170
|
-
end # context
|
171
|
-
|
172
|
-
|
173
|
-
context "and messages are published as non-immediate" do
|
174
|
-
it "routes all messages to all bound queues" do
|
175
|
-
100.times do
|
176
|
-
dispatched_data = rand(5_000_000)
|
177
|
-
@sent_values.push(dispatched_data)
|
178
|
-
|
179
|
-
@exchange.publish(dispatched_data, :immediate => false)
|
180
|
-
end
|
181
|
-
|
182
|
-
# 6 seconds are for Rubinius, it is surprisingly slow on this workload
|
183
|
-
done(3.5) {
|
184
|
-
[@queue1, @queue2, @queue3].each do |q|
|
185
|
-
@received_messages[q.name].size.should == @expected_number_of_messages[q.name]
|
186
|
-
|
187
|
-
# this one is ordering assertion
|
188
|
-
@received_messages[q.name].should == @sent_values
|
189
|
-
end
|
190
|
-
}
|
191
|
-
end # it
|
192
|
-
end # context
|
36
|
+
context "with three bound queues" do
|
37
|
+
it "routes all messages to all bound queues" do
|
38
|
+
@exchange = @channel.fanout("amqpgem.integration.multicast.fanout", :auto_delete => true)
|
39
|
+
@queue1 = @channel.queue("amqpgem.integration.multicast.queue1", :auto_delete => true)
|
40
|
+
@queue2 = @channel.queue("amqpgem.integration.multicast.queue2", :auto_delete => true)
|
41
|
+
@queue3 = @channel.queue("amqpgem.integration.multicast.queue3", :auto_delete => true)
|
193
42
|
|
43
|
+
@queues = [@queue1, @queue2, @queue3]
|
194
44
|
|
45
|
+
@received_messages = {
|
46
|
+
@queue1.name => [],
|
47
|
+
@queue2.name => [],
|
48
|
+
@queue3.name => []
|
49
|
+
}
|
195
50
|
|
196
|
-
|
197
|
-
|
198
|
-
|
51
|
+
@expected_number_of_messages = {
|
52
|
+
@queue1.name => 10,
|
53
|
+
@queue2.name => 10,
|
54
|
+
@queue3.name => 10
|
55
|
+
}
|
199
56
|
|
200
57
|
|
58
|
+
@sent_values = Array.new
|
201
59
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
dispatched_data = rand(5_000_000)
|
206
|
-
@sent_values.push(dispatched_data)
|
60
|
+
@queue1.bind(@exchange).subscribe do |payload|
|
61
|
+
@received_messages[@queue1.name].push(payload.to_i)
|
62
|
+
end # subscribe
|
207
63
|
|
208
|
-
|
209
|
-
|
64
|
+
@queue2.bind(@exchange).subscribe do |payload|
|
65
|
+
@received_messages[@queue2.name].push(payload.to_i)
|
66
|
+
end # subscribe
|
210
67
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
@received_messages[q.name].size.should == @expected_number_of_messages[q.name]
|
68
|
+
@queue3.bind(@exchange).subscribe do |payload|
|
69
|
+
@received_messages[@queue3.name].push(payload.to_i)
|
70
|
+
end # subscribe
|
215
71
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
}
|
220
|
-
end # it
|
221
|
-
end # context
|
72
|
+
10.times do
|
73
|
+
dispatched_data = rand(5_000_000)
|
74
|
+
@sent_values.push(dispatched_data)
|
222
75
|
|
76
|
+
@exchange.publish(dispatched_data, :mandatory => false)
|
77
|
+
end
|
223
78
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
@sent_values.push(dispatched_data)
|
229
|
-
|
230
|
-
@exchange.publish(dispatched_data, :routing_key => @queues.sample.name)
|
231
|
-
end
|
79
|
+
# for Rubinius, it is surprisingly slow on this workload
|
80
|
+
done(2.5) {
|
81
|
+
[@queue1, @queue2, @queue3].each do |q|
|
82
|
+
@received_messages[q.name].size.should == @expected_number_of_messages[q.name]
|
232
83
|
|
233
|
-
#
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
@received_messages[q.name].should == @sent_values
|
240
|
-
end
|
241
|
-
}
|
242
|
-
end # it
|
243
|
-
end # context
|
244
|
-
end # context
|
245
|
-
end # context
|
84
|
+
# this one is ordering assertion
|
85
|
+
@received_messages[q.name].should == @sent_values
|
86
|
+
end
|
87
|
+
}
|
88
|
+
end # it
|
89
|
+
end
|
246
90
|
end # describe
|