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.
Files changed (35) hide show
  1. data/.travis.yml +3 -3
  2. data/Gemfile +9 -6
  3. data/README.md +18 -12
  4. data/amqp.gemspec +2 -2
  5. data/bin/docup +3 -0
  6. data/docs/08Migration.textile +67 -5
  7. data/docs/AMQP091ModelExplained.textile +138 -101
  8. data/docs/Bindings.textile +109 -8
  9. data/docs/ConnectingToTheBroker.textile +8 -0
  10. data/docs/ConnectionEncryptionWithTLS.textile +5 -0
  11. data/docs/DocumentationGuidesIndex.textile +21 -5
  12. data/docs/Durability.textile +3 -1
  13. data/docs/ErrorHandling.textile +20 -0
  14. data/docs/Exchanges.textile +7 -1
  15. data/docs/GettingStarted.textile +10 -0
  16. data/docs/PatternsAndUseCases.textile +6 -0
  17. data/docs/Queues.textile +7 -1
  18. data/docs/RabbitMQVersions.textile +6 -1
  19. data/docs/RunningTests.textile +8 -3
  20. data/docs/Troubleshooting.textile +31 -0
  21. data/docs/VendorSpecificExtensions.textile +137 -6
  22. data/examples/extensions/rabbitmq/per_queue_message_ttl.rb +24 -25
  23. data/examples/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +11 -20
  24. data/examples/extensions/rabbitmq/using_alternate_exchanges.rb +28 -0
  25. data/examples/hello_world.rb +1 -1
  26. data/lib/amqp.rb +1 -0
  27. data/lib/amqp/compatibility/ruby187_patchlevel_check.rb +2 -0
  28. data/lib/amqp/integration/rails.rb +17 -0
  29. data/lib/amqp/version.rb +1 -1
  30. data/spec/integration/authentication_spec.rb +2 -2
  31. data/spec/integration/fanout_exchange_routing_spec.rb +43 -199
  32. data/spec/integration/multiple_consumers_per_queue_spec.rb +7 -7
  33. data/spec/integration/regressions/concurrent_publishing_on_the_same_channel_spec.rb +1 -1
  34. data/spec/integration/stress/publishing_of_messages_with_incrementing_sizes_spec.rb +50 -0
  35. 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
- h3. Supported extensions
7
+ h2. Supported extensions
8
8
 
9
- AMQP gem supports two "RabbitMQ extensions to AMQP 0.9.1":
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
- h3. Enabling RabbitMQ extensions
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
- h3. Example of using publisher confirmations
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. Documentation
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
- RabbitMQ extensions documentation is part of "amq-client gem documentation":http://rubydoc.info/github/ruby-amqp/amq-client/master/frames
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) do |channel, open_ok|
14
- puts "Channel #{channel.id} is now open"
15
-
16
- channel.on_error do |ch, channel_close|
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
- x = channel.fanout("amq.fanout")
21
- channel.queue("", :auto_delete => true, :arguments => { "x-message-ttl" => 1000 }) do |q|
22
- puts "Declared a new server-named qeueue: #{q.name}"
23
- q.bind(x)
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
- EventMachine.add_timer(0.3) do
27
- 10.times do |i|
28
- puts "Publishing message ##{i}"
29
- x.publish("Message ##{i}")
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
- EventMachine.add_timer(0.7) do
34
- q.pop do |headers, payload|
35
- raise "x-message-ttl didn't seem to work (timeout is up)" if payload.nil?
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
- EventMachine.add_timer(1.5) do
40
- q.pop do |headers, payload|
41
- raise "x-message-ttl didn't seem to work (timeout isn't up)" if payload
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 do
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
- AMQP.start do |connection|
12
- puts "Connected!"
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 do |ch, channel_close|
18
- puts "Oops! a channel-level exception: #{channel_close.reply_text}"
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
- puts "Declared a new server-named qeueue: #{q.name}"
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
- AMQP.stop do
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
@@ -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.direct("")
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
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require "amq/protocol"
3
4
  require "amq/client"
4
5
  require "amq/client/adapters/event_machine"
5
6
 
@@ -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
@@ -6,5 +6,5 @@ module AMQP
6
6
  #
7
7
  # @see AMQ::Protocol::VERSION
8
8
  # @return [String] AMQP gem version
9
- VERSION = '0.8.0.rc14'
9
+ VERSION = '0.8.0.rc15'
10
10
  end
@@ -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 6
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 6
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 "Workload distribution" do
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
- # 6 seconds are for Rubinius, it is surprisingly slow on this workload
115
- done(3.5) {
116
- [@queue1, @queue2, @queue3].each do |q|
117
- @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
118
-
119
- # this one is ordering assertion
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
- context "and messages are published as immediate" do
197
- it "may get a Basic.Return back"
198
- end # context
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
- context "and messages are published WITHOUT routing key" do
203
- it "routes all messages to all bound queues" do
204
- 100.times do
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
- @exchange.publish(dispatched_data)
209
- end
64
+ @queue2.bind(@exchange).subscribe do |payload|
65
+ @received_messages[@queue2.name].push(payload.to_i)
66
+ end # subscribe
210
67
 
211
- # 6 seconds are for Rubinius, it is surprisingly slow on this workload
212
- done(3.5) {
213
- [@queue1, @queue2, @queue3].each do |q|
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
- # this one is ordering assertion
217
- @received_messages[q.name].should == @sent_values
218
- end
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
- context "and messages are published WITH routing key that matches name of one of the queues" do
225
- it "routes all messages to all bound queues" do
226
- 100.times do
227
- dispatched_data = rand(5_000_000)
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
- # 6 seconds are for Rubinius, it is surprisingly slow on this workload
234
- done(3.5) {
235
- [@queue1, @queue2, @queue3].each do |q|
236
- @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
237
-
238
- # this one is ordering assertion
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