amqp 0.8.0.rc14 → 0.8.0.rc15
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/.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
|