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.
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