amqp 0.8.0.rc13 → 0.8.0.rc14
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -1
- data/.travis.yml +8 -2
- data/.yardopts +1 -0
- data/CHANGELOG +9 -0
- data/Gemfile +17 -11
- data/README.md +26 -16
- data/amqp.gemspec +2 -2
- data/bin/ci/before_build.sh +21 -0
- data/docs/08Migration.textile +199 -5
- data/docs/AMQP091ModelExplained.textile +322 -0
- data/docs/Bindings.textile +24 -4
- data/docs/Clustering.textile +1 -1
- data/docs/ConnectingToTheBroker.textile +98 -82
- data/docs/ConnectionEncryptionWithTLS.textile +65 -5
- data/docs/DocumentationGuidesIndex.textile +93 -13
- data/docs/Durability.textile +1 -1
- data/docs/ErrorHandling.textile +458 -94
- data/docs/Exchanges.textile +901 -87
- data/docs/GettingStarted.textile +278 -143
- data/docs/PatternsAndUseCases.textile +420 -0
- data/docs/Queues.textile +730 -178
- data/docs/RabbitMQVersions.textile +18 -3
- data/docs/RunningTests.textile +1 -1
- data/docs/TestingWithEventedSpec.textile +121 -0
- data/docs/Troubleshooting.textile +15 -1
- data/docs/VendorSpecificExtensions.textile +1 -1
- data/docs/diagrams/001_hello_world_example_routing.png +0 -0
- data/docs/diagrams/002_blabbr_example_routing.png +0 -0
- data/docs/diagrams/003_weathr_example_routing.png +0 -0
- data/docs/diagrams/004_fanout_exchange.png +0 -0
- data/docs/diagrams/005_direct_exchange.png +0 -0
- data/docs/diagrams/redhat/direct_exchange.png +0 -0
- data/docs/diagrams/redhat/fanout_exchange.png +0 -0
- data/docs/diagrams/redhat/topic_exchange.png +0 -0
- data/examples/error_handling/automatic_recovery_of_channel_and_queues.rb +50 -0
- data/examples/error_handling/automatically_recovering_hello_world_consumer.rb +51 -0
- data/examples/error_handling/automatically_recovering_hello_world_consumer_that_uses_a_server_named_queue.rb +51 -0
- data/examples/error_handling/basic_connection_failover.rb +22 -0
- data/examples/error_handling/channel_level_exception.rb +9 -2
- data/examples/error_handling/connection_level_exception.rb +8 -1
- data/examples/error_handling/connection_level_exception_with_objects.rb +49 -0
- data/examples/error_handling/connection_loss_handler.rb +1 -5
- data/examples/error_handling/hello_world_producer.rb +43 -0
- data/examples/error_handling/insufficient_permissions.rb +54 -0
- data/examples/error_handling/manual_connection_and_channel_recovery.rb +71 -0
- data/examples/error_handling/queue_exclusivity_violation.rb +41 -0
- data/examples/error_handling/queue_name_violation.rb +31 -0
- data/examples/exchanges/autodeletion_of_exchanges.rb +1 -4
- data/examples/guides/queues/01a_declaring_a_server_named_queue_using_queue_constructor.rb +7 -8
- data/examples/guides/queues/01b_declaring_a_queue_using_queue_constructor.rb +7 -8
- data/examples/guides/queues/02a_declaring_a_durable_shared_queue.rb +5 -8
- data/examples/guides/queues/02b_declaring_a_durable_shared_queue.rb +5 -8
- data/examples/guides/queues/03a_declaring_a_temporary_exclusive_queue.rb +7 -8
- data/examples/guides/queues/04_bind_a_queue_using_exchange_instance.rb +9 -10
- data/examples/guides/queues/05_bind_a_queue_using_exchange_name.rb +8 -10
- data/examples/guides/queues/06_subscribe_to_receive_messages.rb +10 -12
- data/examples/guides/queues/07_fetch_a_message_from_the_queue.rb +14 -14
- data/examples/guides/queues/08_unsubscribing_a_consumer.rb +13 -16
- data/examples/guides/queues/09_unbinding_from_exchange.rb +16 -22
- data/examples/guides/queues/10_purge_a_queue.rb +13 -18
- data/examples/guides/queues/11_deleting_a_queue.rb +14 -19
- data/examples/guides/queues/12_objects_that_consume_messages.rb +69 -0
- data/examples/guides/queues/13_objects_that_consume_messages_take_two.rb +89 -0
- data/examples/hello_world.rb +1 -3
- data/examples/hello_world_with_an_empty_string.rb +5 -6
- data/examples/inspecting_server_information.rb +45 -0
- data/examples/issues/issue_93.rb +23 -0
- data/examples/issues/issue_94.rb +23 -0
- data/examples/patterns/command/consumer.rb +45 -0
- data/examples/patterns/command/producer.rb +26 -0
- data/examples/patterns/request_reply/client.rb +29 -0
- data/examples/patterns/request_reply/server.rb +26 -0
- data/examples/publishing/publishing_a_one_off_message.rb +6 -4
- data/examples/publishing/returned_messages.rb +2 -10
- data/examples/queues/accessing_message_metadata.rb +15 -13
- data/examples/queues/queue_status.rb +12 -15
- data/examples/routing/fanout_routing.rb +33 -0
- data/examples/routing/headers_routing.rb +17 -15
- data/examples/routing/round_robin_with_direct_exchange.rb +39 -0
- data/examples/routing/round_robin_with_the_default_exchange.rb +38 -0
- data/examples/routing/unroutable_mandatory_message_is_returned.rb +33 -0
- data/examples/routing/weather_updates.rb +15 -20
- data/examples/tls/using_tls.rb +41 -0
- data/lib/amqp/bit_set.rb +80 -0
- data/lib/amqp/broker.rb +72 -0
- data/lib/amqp/channel.rb +93 -13
- data/lib/amqp/client.rb +11 -22
- data/lib/amqp/compatibility/ruby187_patchlevel_check.rb +2 -0
- data/lib/amqp/connection.rb +2 -3
- data/lib/amqp/consumer.rb +208 -0
- data/lib/amqp/deprecated/fork.rb +2 -0
- data/lib/amqp/deprecated/mq.rb +2 -0
- data/lib/amqp/exchange.rb +6 -4
- data/lib/amqp/extensions/rabbitmq.rb +3 -1
- data/lib/amqp/header.rb +76 -14
- data/lib/amqp/int_allocator.rb +96 -0
- data/lib/amqp/logger.rb +2 -0
- data/lib/amqp/queue.rb +242 -86
- data/lib/amqp/rpc.rb +2 -0
- data/lib/amqp/session.rb +169 -9
- data/lib/amqp/utilities/event_loop_helper.rb +2 -0
- data/lib/amqp/utilities/server_type.rb +2 -0
- data/lib/amqp/version.rb +2 -2
- data/lib/mq.rb +4 -2
- data/lib/mq/logger.rb +3 -1
- data/lib/mq/rpc.rb +3 -1
- data/spec/integration/authentication_spec.rb +17 -10
- data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +1 -1
- data/spec/integration/automatic_recovery_predicate_spec.rb +68 -0
- data/spec/integration/basic_get_spec.rb +2 -1
- data/spec/integration/{extensions/basic_return_spec.rb → basic_return_spec.rb} +2 -1
- data/spec/integration/channel_level_exception_handling_spec.rb +53 -0
- data/spec/integration/connection_level_exception_handling_spec.rb +49 -0
- data/spec/integration/declare_and_immediately_bind_a_server_named_queue_spec.rb +38 -17
- data/spec/integration/declare_one_hundred_server_named_queues_spec.rb +44 -0
- data/spec/integration/direct_exchange_routing_spec.rb +125 -0
- data/spec/integration/exchange_declaration_spec.rb +75 -46
- data/spec/integration/extensions/rabbitmq/publisher_confirmations_spec.rb +180 -0
- data/spec/integration/{workload_distribution_spec.rb → fanout_exchange_routing_spec.rb} +10 -9
- data/spec/integration/headers_exchange_routing_spec.rb +269 -0
- data/spec/integration/hello_world_spec.rb +77 -0
- data/spec/integration/immediate_messages_spec.rb +59 -0
- data/spec/integration/mandatory_messages_spec.rb +52 -0
- data/spec/integration/message_metadata_access_spec.rb +106 -0
- data/spec/integration/multiple_consumers_per_queue_spec.rb +319 -0
- data/spec/integration/ordering_of_published_messages_spec.rb +96 -0
- data/spec/integration/queue_declaration_spec.rb +8 -8
- data/spec/integration/queue_status_spec.rb +66 -0
- data/spec/integration/recovery/per_channel_automatic_recovery_on_graceful_broker_shutdown_spec.rb +76 -0
- data/spec/integration/recovery/per_channel_automatic_recovery_spec.rb +72 -0
- data/spec/integration/redelivery_of_unacknowledged_messages_spec.rb +96 -0
- data/spec/integration/regressions/concurrent_publishing_on_the_same_channel_spec.rb +91 -0
- data/spec/integration/regressions/empty_message_body_spec.rb +56 -0
- data/spec/integration/regressions/issue66_spec.rb +2 -1
- data/spec/integration/reply_queue_communication_spec.rb +2 -1
- data/spec/integration/store_and_forward_spec.rb +4 -3
- data/spec/integration/topic_subscription_spec.rb +2 -1
- data/spec/integration/tx_commit_spec.rb +124 -0
- data/spec/integration/tx_rollback_spec.rb +167 -0
- data/spec/spec_helper.rb +44 -71
- data/spec/unit/amqp/bit_set_spec.rb +127 -0
- data/spec/unit/amqp/channel_id_allocation_spec.rb +40 -0
- data/spec/unit/amqp/connection_spec.rb +4 -2
- data/spec/unit/amqp/int_allocator_spec.rb +116 -0
- metadata +92 -26
- data/CONTRIBUTORS +0 -29
- data/docs/Routing.textile +0 -30
- data/examples/real-world/task-queue/README.textile +0 -3
- data/examples/real-world/task-queue/consumer.rb +0 -27
- data/examples/real-world/task-queue/producer.rb +0 -22
- data/spec/unit/amqp/basic_spec.rb +0 -39
- data/tasks.rb +0 -4
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "100 AMQP messages" do
|
6
|
+
|
7
|
+
#
|
8
|
+
# Environment
|
9
|
+
#
|
10
|
+
|
11
|
+
include EventedSpec::AMQPSpec
|
12
|
+
default_options AMQP_OPTS
|
13
|
+
default_timeout 5
|
14
|
+
|
15
|
+
|
16
|
+
before :all do
|
17
|
+
@list = Range.new(0, 100, true).to_a
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
context "published and received on the same channel" do
|
22
|
+
amqp_before do
|
23
|
+
@channel = AMQP::Channel.new
|
24
|
+
@channel.on_error do |ch, close|
|
25
|
+
raise "Channel-level error!: #{close.inspect}"
|
26
|
+
end
|
27
|
+
|
28
|
+
@queue = @channel.queue("amqpgem.tests.integration.queue1", :auto_delete => true)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Examples
|
33
|
+
#
|
34
|
+
|
35
|
+
it "are received on the same channel in the order of publishing" do
|
36
|
+
received = []
|
37
|
+
|
38
|
+
@queue.subscribe do |metadata, payload|
|
39
|
+
received << payload.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
EventMachine.add_timer(0.3) do
|
43
|
+
@list.each { |i| @channel.default_exchange.publish(i.to_s, :routing_key => @queue.name) }
|
44
|
+
end
|
45
|
+
|
46
|
+
done(3.5) {
|
47
|
+
received.size.should == 100
|
48
|
+
received.first.should == 0
|
49
|
+
received.last.should == 99
|
50
|
+
|
51
|
+
received.should == @list
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
context "published on two different channels" do
|
58
|
+
amqp_before do
|
59
|
+
@channel1 = AMQP::Channel.new
|
60
|
+
@channel2 = AMQP::Channel.new
|
61
|
+
|
62
|
+
@channel1.on_error do |ch, close|
|
63
|
+
raise "Channel-level error!: #{close.inspect}"
|
64
|
+
end
|
65
|
+
@channel2.on_error do |ch, close|
|
66
|
+
raise "Channel-level error!: #{close.inspect}"
|
67
|
+
end
|
68
|
+
|
69
|
+
@queue = @channel1.queue("amqpgem.tests.integration.queue1", :auto_delete => true)
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Examples
|
74
|
+
#
|
75
|
+
|
76
|
+
it "are received on the same channel in the order of publishing" do
|
77
|
+
received = []
|
78
|
+
|
79
|
+
@queue.subscribe do |metadata, payload|
|
80
|
+
received << payload.to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
EventMachine.add_timer(0.3) do
|
84
|
+
@list.each { |i| @channel2.default_exchange.publish(i.to_s, :routing_key => @queue.name) }
|
85
|
+
end
|
86
|
+
|
87
|
+
done(3.5) {
|
88
|
+
received.size.should == 100
|
89
|
+
received.first.should == 0
|
90
|
+
received.last.should == 99
|
91
|
+
|
92
|
+
received.should == @list
|
93
|
+
}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end # describe
|
@@ -18,7 +18,7 @@ describe AMQP do
|
|
18
18
|
|
19
19
|
after(:all) do
|
20
20
|
AMQP.cleanup_state
|
21
|
-
done
|
21
|
+
done
|
22
22
|
end
|
23
23
|
|
24
24
|
#
|
@@ -32,13 +32,13 @@ describe AMQP do
|
|
32
32
|
it "declares a new queue with that name" do
|
33
33
|
queue = @channel.queue(name)
|
34
34
|
queue.name.should == name
|
35
|
-
done
|
35
|
+
done
|
36
36
|
end
|
37
37
|
|
38
38
|
it "caches that queue" do
|
39
39
|
queue = @channel.queue(name)
|
40
40
|
@channel.queue(name).object_id.should == queue.object_id
|
41
|
-
done
|
41
|
+
done
|
42
42
|
end
|
43
43
|
end # context
|
44
44
|
|
@@ -48,7 +48,7 @@ describe AMQP do
|
|
48
48
|
@channel.queue("") do |queue, *args|
|
49
49
|
queue.name.should_not be_empty
|
50
50
|
queue.delete
|
51
|
-
done
|
51
|
+
done
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -93,7 +93,7 @@ describe AMQP do
|
|
93
93
|
@channel.queue(name, different_options)
|
94
94
|
}.to raise_error(AMQP::IncompatibleOptionsError)
|
95
95
|
@queue.delete
|
96
|
-
done
|
96
|
+
done
|
97
97
|
end
|
98
98
|
end
|
99
99
|
end
|
@@ -108,7 +108,7 @@ describe AMQP do
|
|
108
108
|
|
109
109
|
queue.should == original_queue
|
110
110
|
|
111
|
-
done
|
111
|
+
done
|
112
112
|
end # it
|
113
113
|
end
|
114
114
|
|
@@ -120,7 +120,7 @@ describe AMQP do
|
|
120
120
|
exchange = @channel.queue("queue declared at #{Time.now.to_i}", :passive => true)
|
121
121
|
}.to raise_error
|
122
122
|
|
123
|
-
done
|
123
|
+
done
|
124
124
|
end # it
|
125
125
|
end # context
|
126
126
|
end # context
|
@@ -136,7 +136,7 @@ describe AMQP do
|
|
136
136
|
@channel.queue("previously.declared.durable.queue", :durable => false)
|
137
137
|
}.to raise_error(AMQP::IncompatibleOptionsError)
|
138
138
|
|
139
|
-
done
|
139
|
+
done
|
140
140
|
end # it
|
141
141
|
end # context
|
142
142
|
end # describe
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe AMQP::Queue do
|
6
|
+
|
7
|
+
#
|
8
|
+
# Environment
|
9
|
+
#
|
10
|
+
|
11
|
+
include EventedSpec::AMQPSpec
|
12
|
+
|
13
|
+
default_timeout 5
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
#
|
18
|
+
# Examples
|
19
|
+
#
|
20
|
+
|
21
|
+
describe "#status" do
|
22
|
+
it "yields # of messages & consumers to the callback" do
|
23
|
+
events = []
|
24
|
+
channel = AMQP::Channel.new
|
25
|
+
|
26
|
+
queue1 = channel.queue("", :auto_delete => true)
|
27
|
+
queue2 = channel.queue("amqpgem.tests.a.named.queue", :auto_delete => true)
|
28
|
+
|
29
|
+
EventMachine.add_timer(1.0) do
|
30
|
+
queue1.status do |m, c|
|
31
|
+
events << :queue1_declare_ok
|
32
|
+
end
|
33
|
+
queue2.status do |m, c|
|
34
|
+
events << :queue2_declare_ok
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
done(2.0) {
|
39
|
+
events.should include(:queue1_declare_ok)
|
40
|
+
events.should include(:queue2_declare_ok)
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
it "yields # of messages & consumers to the callback in pseudo-synchronous code" do
|
46
|
+
events = []
|
47
|
+
channel = AMQP::Channel.new
|
48
|
+
|
49
|
+
queue1 = channel.queue("", :auto_delete => true)
|
50
|
+
queue2 = channel.queue("amqpgem.tests.a.named.queue", :auto_delete => true)
|
51
|
+
|
52
|
+
queue1.status do |m, c|
|
53
|
+
events << :queue1_declare_ok
|
54
|
+
end
|
55
|
+
queue2.status do |m, c|
|
56
|
+
events << :queue2_declare_ok
|
57
|
+
end
|
58
|
+
|
59
|
+
done(2.0) {
|
60
|
+
queue1.name.should =~ /^amq\..+/
|
61
|
+
events.should include(:queue1_declare_ok)
|
62
|
+
events.should include(:queue2_declare_ok)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/spec/integration/recovery/per_channel_automatic_recovery_on_graceful_broker_shutdown_spec.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
unless ENV["CI"]
|
5
|
+
describe "Per-channel automatic channel recovery" do
|
6
|
+
|
7
|
+
#
|
8
|
+
# Environment
|
9
|
+
#
|
10
|
+
|
11
|
+
include RabbitMQ::Control
|
12
|
+
|
13
|
+
include EventedSpec::AMQPSpec
|
14
|
+
default_timeout 20.0
|
15
|
+
|
16
|
+
amqp_before do
|
17
|
+
@channel = AMQP::Channel.new(AMQP.connection, :auto_recovery => true)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
after :all do
|
22
|
+
start_rabbitmq unless rabbitmq_pid
|
23
|
+
end
|
24
|
+
|
25
|
+
# ...
|
26
|
+
|
27
|
+
|
28
|
+
#
|
29
|
+
# Examples
|
30
|
+
#
|
31
|
+
|
32
|
+
it "can be used when broker is shut down gracefully" do
|
33
|
+
AMQP.connection.on_error do |conn, connection_close|
|
34
|
+
puts "[connection.close] Reply code = #{connection_close.reply_code}, reply text = #{connection_close.reply_text}"
|
35
|
+
if connection_close.reply_code == 320
|
36
|
+
puts "[connection.close] Setting up a periodic reconnection timer..."
|
37
|
+
conn.periodically_reconnect(1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
pid = rabbitmq_pid
|
42
|
+
puts "rabbitmq pid = #{pid.inspect}"
|
43
|
+
|
44
|
+
stop_rabbitmq
|
45
|
+
rabbitmq_pid.should be_nil
|
46
|
+
|
47
|
+
# 2 seconds later, check that we are reconnecting
|
48
|
+
EventMachine.add_timer(2.0) do
|
49
|
+
AMQP.connection.should_not be_connected
|
50
|
+
AMQP.connection.should be_reconnecting
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# 4 seconds later, start RabbitMQ
|
55
|
+
EventMachine.add_timer(4.0) do
|
56
|
+
start_rabbitmq
|
57
|
+
rabbitmq_pid.should_not be_nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# 10 seconds later, use the (now recovered) connection. Note that depending
|
61
|
+
# on # of plugins used it may take 5-10 seconds to start up RabbitMQ and initialize it,
|
62
|
+
# then open a new AMQP connection. That's why we wait. MK.
|
63
|
+
EventMachine.add_timer(10.0) do
|
64
|
+
AMQP.connection.should be_connected
|
65
|
+
AMQP.connection.should_not be_reconnecting
|
66
|
+
|
67
|
+
@channel.queue("amqpgem.tests.a.queue", :auto_delete => true).subscribe do |metadata, payload|
|
68
|
+
puts "Got a message: #{payload.inspect}"
|
69
|
+
done
|
70
|
+
end
|
71
|
+
|
72
|
+
EventMachine.add_timer(1.5) { @channel.default_exchange.publish("", :routing_key => "amqpgem.tests.a.queue") }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
unless ENV["CI"]
|
5
|
+
describe "Per-channel automatic channel recovery" do
|
6
|
+
|
7
|
+
#
|
8
|
+
# Environment
|
9
|
+
#
|
10
|
+
|
11
|
+
include RabbitMQ::Control
|
12
|
+
|
13
|
+
include EventedSpec::AMQPSpec
|
14
|
+
default_timeout 20.0
|
15
|
+
|
16
|
+
amqp_before do
|
17
|
+
@channel = AMQP::Channel.new(AMQP.connection, :auto_recovery => true)
|
18
|
+
end
|
19
|
+
|
20
|
+
after :all do
|
21
|
+
start_rabbitmq unless rabbitmq_pid
|
22
|
+
end
|
23
|
+
|
24
|
+
# ...
|
25
|
+
|
26
|
+
|
27
|
+
#
|
28
|
+
# Examples
|
29
|
+
#
|
30
|
+
|
31
|
+
it "kicks in when broker goes down" do
|
32
|
+
AMQP.connection.on_tcp_connection_loss do |conn, settings|
|
33
|
+
puts "[network failure] Trying to reconnect..."
|
34
|
+
conn.reconnect(false, 1)
|
35
|
+
end
|
36
|
+
|
37
|
+
pid = rabbitmq_pid
|
38
|
+
puts "rabbitmq pid = #{pid.inspect}"
|
39
|
+
|
40
|
+
kill_rabbitmq
|
41
|
+
rabbitmq_pid.should be_nil
|
42
|
+
|
43
|
+
# 2 seconds later, check that we are reconnecting
|
44
|
+
EventMachine.add_timer(2.0) do
|
45
|
+
AMQP.connection.should_not be_connected
|
46
|
+
AMQP.connection.should be_reconnecting
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# 4 seconds later, start RabbitMQ
|
51
|
+
EventMachine.add_timer(4.0) do
|
52
|
+
start_rabbitmq
|
53
|
+
rabbitmq_pid.should_not be_nil
|
54
|
+
end
|
55
|
+
|
56
|
+
# 12 seconds later, use the (now recovered) connection. Note that depending
|
57
|
+
# on # of plugins used it may take 5-10 seconds to start up RabbitMQ and initialize it,
|
58
|
+
# then open a new AMQP connection. That's why we wait. MK.
|
59
|
+
EventMachine.add_timer(12.0) do
|
60
|
+
AMQP.connection.should be_connected
|
61
|
+
AMQP.connection.should_not be_reconnecting
|
62
|
+
|
63
|
+
@channel.queue("amqpgem.tests.a.queue", :auto_delete => true).subscribe do |metadata, payload|
|
64
|
+
puts "Got a message"
|
65
|
+
done
|
66
|
+
end
|
67
|
+
|
68
|
+
EventMachine.add_timer(1.5) { @channel.default_exchange.publish("Hi", :routing_key => "amqpgem.tests.a.queue") }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Unacknowledged messages" do
|
6
|
+
|
7
|
+
#
|
8
|
+
# Environment
|
9
|
+
#
|
10
|
+
|
11
|
+
include EventedSpec::AMQPSpec
|
12
|
+
|
13
|
+
default_timeout 5
|
14
|
+
|
15
|
+
amqp_before do
|
16
|
+
@connection1 = AMQP.connect
|
17
|
+
@connection2 = AMQP.connect
|
18
|
+
@connection3 = AMQP.connect
|
19
|
+
|
20
|
+
@channel1 = AMQP::Channel.new(@connection1)
|
21
|
+
@channel2 = AMQP::Channel.new(@connection2)
|
22
|
+
@channel3 = AMQP::Channel.new(@connection3)
|
23
|
+
|
24
|
+
[@channel1, @channel2, @channel3].each { |ch| ch.on_error { fail } }
|
25
|
+
|
26
|
+
@channel1.prefetch(3)
|
27
|
+
@channel2.prefetch(1)
|
28
|
+
end
|
29
|
+
|
30
|
+
after(:all) do
|
31
|
+
AMQP.cleanup_state
|
32
|
+
done
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
#
|
38
|
+
# Examples
|
39
|
+
#
|
40
|
+
|
41
|
+
# this is a spec example based on one of the Working With Queues doc guides.
|
42
|
+
# It is somewhat hairy since it imitates 3 apps in a single process
|
43
|
+
# but demonstrates redeliveries pretty well. MK.
|
44
|
+
|
45
|
+
it "are redelivered to alternate consumers when the 'primary' one disconnects" do
|
46
|
+
number_of_messages_app2_received = 0
|
47
|
+
expected_number_of_deliveries = 21
|
48
|
+
redelivery_values = Array.new
|
49
|
+
|
50
|
+
exchange = @channel3.direct("amq.direct")
|
51
|
+
|
52
|
+
queue1 = @channel1.queue("amqpgem.examples.acknowledgements.explicit", :auto_delete => false)
|
53
|
+
# purge the queue so that we don't get any redeliveries from previous runs
|
54
|
+
queue1.purge
|
55
|
+
queue1.bind(exchange).subscribe(:ack => true) do |metadata, payload|
|
56
|
+
# acknowledge some messages, they will be removed from the queue
|
57
|
+
if metadata.headers["i"] < 10
|
58
|
+
@channel1.acknowledge(metadata.delivery_tag, false)
|
59
|
+
else
|
60
|
+
# some messages are not ack-ed and will remain in the queue for redelivery
|
61
|
+
# when app #1 connection is closed (either properly or due to a crash)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
queue2 = @channel2.queue!("amqpgem.examples.acknowledgements.explicit", :auto_delete => false)
|
66
|
+
queue2.subscribe(:ack => true) do |metadata, payload|
|
67
|
+
redelivery_values << metadata.redelivered?
|
68
|
+
|
69
|
+
# app 2 always acks messages
|
70
|
+
metadata.ack
|
71
|
+
|
72
|
+
number_of_messages_app2_received += 1
|
73
|
+
end
|
74
|
+
|
75
|
+
EventMachine.add_timer(2.0) {
|
76
|
+
# app1 quits/crashes
|
77
|
+
@connection1.close
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
# 0.5 seconds later, publish a bunch of messages
|
82
|
+
EventMachine.add_timer(0.5) {
|
83
|
+
30.times do |i|
|
84
|
+
exchange.publish("Message ##{i}", :headers => { :i => i })
|
85
|
+
i += 1
|
86
|
+
end
|
87
|
+
}
|
88
|
+
|
89
|
+
|
90
|
+
done(4.8) {
|
91
|
+
number_of_messages_app2_received.should be >= expected_number_of_deliveries
|
92
|
+
# 3 last messages are redeliveries
|
93
|
+
redelivery_values.last(7).should == [false, false, false, false, true, true, true]
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|