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,39 @@
|
|
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
|
+
AMQP.connect do |connection|
|
13
|
+
channel1 = AMQP::Channel.new(connection)
|
14
|
+
channel2 = AMQP::Channel.new(connection)
|
15
|
+
exchange = channel1.direct("amqpgem.examples.exchanges.direct", :auto_delete => true)
|
16
|
+
|
17
|
+
q1 = channel1.queue("amqpgem.examples.queues.shared", :auto_delete => true).bind(exchange, :routing_key => "shared.key")
|
18
|
+
q1.subscribe do |payload|
|
19
|
+
puts "Queue #{q1.name} on channel 1 received #{payload}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# since the queue is shared, binding here is redundant but we will leave it in for completeness.
|
23
|
+
q2 = channel2.queue("amqpgem.examples.queues.shared", :auto_delete => true).bind(exchange, :routing_key => "shared.key")
|
24
|
+
q2.subscribe do |payload|
|
25
|
+
puts "Queue #{q2.name} on channel 2 received #{payload}"
|
26
|
+
end
|
27
|
+
|
28
|
+
# Publish some test data in a bit, after all queues are declared & bound
|
29
|
+
EventMachine.add_timer(1.2) do
|
30
|
+
5.times { |i| exchange.publish("Hello #{i}, direct exchanges world!", :routing_key => "shared.key") }
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
show_stopper = Proc.new { connection.close { EventMachine.stop } }
|
35
|
+
|
36
|
+
Signal.trap "TERM", show_stopper
|
37
|
+
EM.add_timer(3, show_stopper)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,38 @@
|
|
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
|
+
AMQP.connect do |connection|
|
13
|
+
channel1 = AMQP::Channel.new(connection)
|
14
|
+
channel2 = AMQP::Channel.new(connection)
|
15
|
+
exchange = channel1.default_exchange
|
16
|
+
|
17
|
+
q1 = channel1.queue("amqpgem.examples.queues.shared", :auto_delete => true)
|
18
|
+
q1.subscribe do |payload|
|
19
|
+
puts "Queue #{q1.name} on channel 1 received #{payload}"
|
20
|
+
end
|
21
|
+
|
22
|
+
q2 = channel2.queue("amqpgem.examples.queues.shared", :auto_delete => true)
|
23
|
+
q2.subscribe do |payload|
|
24
|
+
puts "Queue #{q2.name} on channel 2 received #{payload}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# Publish some test data in a bit, after all queues are declared & bound
|
28
|
+
EventMachine.add_timer(1.2) do
|
29
|
+
5.times { |i| exchange.publish("Hello #{i}, direct exchanges world!", :routing_key => "amqpgem.examples.queues.shared") }
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
show_stopper = Proc.new { connection.close { EventMachine.stop } }
|
34
|
+
|
35
|
+
Signal.trap "TERM", show_stopper
|
36
|
+
EM.add_timer(3, show_stopper)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
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
|
+
puts "=> Handling a returned unroutable message that was published as mandatory"
|
12
|
+
puts
|
13
|
+
|
14
|
+
AMQP.start(:host => '127.0.0.1') do |connection|
|
15
|
+
channel = AMQP.channel
|
16
|
+
channel.on_error { |ch, channel_close| EventMachine.stop; raise "channel error: #{channel_close.reply_text}" }
|
17
|
+
|
18
|
+
# this exchange has no bindings, so messages published to it cannot be routed.
|
19
|
+
exchange = channel.fanout("amqpgem.examples.fanout", :auto_delete => true)
|
20
|
+
exchange.on_return do |basic_return, metadata, payload|
|
21
|
+
puts "#{payload} was returned! reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}"
|
22
|
+
end
|
23
|
+
|
24
|
+
EventMachine.add_timer(0.3) {
|
25
|
+
10.times do |i|
|
26
|
+
exchange.publish("Message ##{i}", :mandatory => true)
|
27
|
+
end
|
28
|
+
}
|
29
|
+
|
30
|
+
EventMachine.add_timer(2) {
|
31
|
+
connection.close { EventMachine.stop }
|
32
|
+
}
|
33
|
+
end
|
@@ -15,27 +15,28 @@ EventMachine.run do
|
|
15
15
|
|
16
16
|
# Subscribers.
|
17
17
|
channel.queue("", :exclusive => true) do |queue|
|
18
|
-
queue.bind(exchange, :routing_key => "americas.north.#").subscribe do |
|
19
|
-
puts "An update for North America: #{payload}, routing key is #{
|
18
|
+
queue.bind(exchange, :routing_key => "americas.north.#").subscribe do |metadata, payload|
|
19
|
+
puts "An update for North America: #{payload}, routing key is #{metadata.routing_key}"
|
20
20
|
end
|
21
21
|
end
|
22
|
-
channel.queue("americas.south").bind(exchange, :routing_key => "americas.south.#").subscribe do |
|
23
|
-
puts "An update for South America: #{payload}, routing key is #{
|
22
|
+
channel.queue("americas.south").bind(exchange, :routing_key => "americas.south.#").subscribe do |metadata, payload|
|
23
|
+
puts "An update for South America: #{payload}, routing key is #{metadata.routing_key}"
|
24
24
|
end
|
25
|
-
channel.queue("us.california").bind(exchange, :routing_key => "americas.north.us.ca.*").subscribe do |
|
26
|
-
puts "An update for US/California: #{payload}, routing key is #{
|
25
|
+
channel.queue("us.california").bind(exchange, :routing_key => "americas.north.us.ca.*").subscribe do |metadata, payload|
|
26
|
+
puts "An update for US/California: #{payload}, routing key is #{metadata.routing_key}"
|
27
27
|
end
|
28
|
-
channel.queue("us.tx.austin").bind(exchange, :routing_key => "#.tx.austin").subscribe do |
|
29
|
-
puts "An update for Austin, TX: #{payload}, routing key is #{
|
28
|
+
channel.queue("us.tx.austin").bind(exchange, :routing_key => "#.tx.austin").subscribe do |metadata, payload|
|
29
|
+
puts "An update for Austin, TX: #{payload}, routing key is #{metadata.routing_key}"
|
30
30
|
end
|
31
|
-
channel.queue("it.rome").bind(exchange, :routing_key => "europe.italy.rome").subscribe do |
|
32
|
-
puts "An update for Rome, Italy: #{payload}, routing key is #{
|
31
|
+
channel.queue("it.rome").bind(exchange, :routing_key => "europe.italy.rome").subscribe do |metadata, payload|
|
32
|
+
puts "An update for Rome, Italy: #{payload}, routing key is #{metadata.routing_key}"
|
33
33
|
end
|
34
|
-
channel.queue("asia.hk").bind(exchange, :routing_key => "asia.southeast.hk.#").subscribe do |
|
35
|
-
puts "An update for Hong Kong: #{payload}, routing key is #{
|
34
|
+
channel.queue("asia.hk").bind(exchange, :routing_key => "asia.southeast.hk.#").subscribe do |metadata, payload|
|
35
|
+
puts "An update for Hong Kong: #{payload}, routing key is #{metadata.routing_key}"
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
# publish a bunch of messages after 1 second, when all queues are declared and bound
|
39
|
+
EventMachine.add_timer(1) do
|
39
40
|
exchange.publish("San Diego update", :routing_key => "americas.north.us.ca.sandiego").
|
40
41
|
publish("Berkeley update", :routing_key => "americas.north.us.ca.berkeley").
|
41
42
|
publish("San Francisco update", :routing_key => "americas.north.us.ca.sanfrancisco").
|
@@ -49,13 +50,7 @@ EventMachine.run do
|
|
49
50
|
end
|
50
51
|
|
51
52
|
|
52
|
-
show_stopper = Proc.new {
|
53
|
-
connection.close do
|
54
|
-
EM.stop
|
55
|
-
end
|
56
|
-
}
|
57
|
-
|
58
|
-
Signal.trap "INT", show_stopper
|
53
|
+
show_stopper = Proc.new { connection.close { EventMachine.stop } }
|
59
54
|
Signal.trap "TERM", show_stopper
|
60
55
|
|
61
56
|
EM.add_timer(3, show_stopper)
|
@@ -0,0 +1,41 @@
|
|
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
|
+
examples_dir = File.join(File.dirname(File.expand_path(__FILE__)), "..")
|
10
|
+
|
11
|
+
certificate_chain_file_path = File.join(examples_dir, "tls_certificates", "client", "cert.pem")
|
12
|
+
client_private_key_file_path = File.join(examples_dir, "tls_certificates", "client", "key.pem")
|
13
|
+
|
14
|
+
|
15
|
+
require 'amqp'
|
16
|
+
|
17
|
+
# This example assumes you have configured RabbitMQ to listen on port 5671
|
18
|
+
# for TLS connections (using RabbitMQ configuration file), for example:
|
19
|
+
#
|
20
|
+
# [
|
21
|
+
# {rabbit, [
|
22
|
+
# {ssl_listeners, [5671]},
|
23
|
+
# {ssl_options, [{cacertfile, "/usr/local/etc/rabbitmq/tls/testca/cacert.pem"},
|
24
|
+
# {certfile, "/usr/local/etc/rabbitmq/tls/server/cert.pem"},
|
25
|
+
# {keyfile, "/usr/local/etc/rabbitmq/tls/server/key.pem"},
|
26
|
+
# {verify, verify_peer},
|
27
|
+
# {fail_if_no_peer_cert, true}]}
|
28
|
+
# ]}
|
29
|
+
# ].
|
30
|
+
#
|
31
|
+
# See TLS certificates under ./examples/tls_certificates
|
32
|
+
|
33
|
+
AMQP.start(:port => 5671,
|
34
|
+
:ssl => {
|
35
|
+
:cert_chain_file => certificate_chain_file_path,
|
36
|
+
:private_key_file => client_private_key_file_path
|
37
|
+
}) do |connection|
|
38
|
+
puts "Connected, authenticated. TLS seems to work."
|
39
|
+
|
40
|
+
connection.disconnect { puts "Now closing the connection…"; EventMachine.stop }
|
41
|
+
end
|
data/lib/amqp/bit_set.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
# Very minimalistic, pure Ruby implementation of bit set. Inspired by java.util.BitSet,
|
5
|
+
# although significantly smaller in scope.
|
6
|
+
class BitSet
|
7
|
+
|
8
|
+
#
|
9
|
+
# API
|
10
|
+
#
|
11
|
+
|
12
|
+
ADDRESS_BITS_PER_WORD = 6
|
13
|
+
BITS_PER_WORD = (1 << ADDRESS_BITS_PER_WORD)
|
14
|
+
WORD_MASK = 0xffffffffffffffff
|
15
|
+
|
16
|
+
# @param [Integer] Number of bits in the set
|
17
|
+
# @api public
|
18
|
+
def initialize(nbits)
|
19
|
+
@nbits = nbits
|
20
|
+
|
21
|
+
self.init_words(nbits)
|
22
|
+
end # initialize(nbits)
|
23
|
+
|
24
|
+
# Sets (flags) given bit. This method allows bits to be set more than once in a row, no exception will be raised.
|
25
|
+
#
|
26
|
+
# @param [Integer] A bit to set
|
27
|
+
# @api public
|
28
|
+
def set(i)
|
29
|
+
w = self.word_index(i)
|
30
|
+
@words[w] |= (1 << i)
|
31
|
+
end # set(i)
|
32
|
+
|
33
|
+
# Fetches flag value for given bit.
|
34
|
+
#
|
35
|
+
# @param [Integer] A bit to fetch
|
36
|
+
# @return [Boolean] true if given bit is set, false otherwise
|
37
|
+
# @api public
|
38
|
+
def get(i)
|
39
|
+
w = self.word_index(i)
|
40
|
+
|
41
|
+
(@words[w] & (1 << i)) != 0
|
42
|
+
end # get(i)
|
43
|
+
alias [] get
|
44
|
+
|
45
|
+
# Unsets (unflags) given bit. This method allows bits to be unset more than once in a row, no exception will be raised.
|
46
|
+
#
|
47
|
+
# @param [Integer] A bit to unset
|
48
|
+
# @api public
|
49
|
+
def unset(i)
|
50
|
+
w = self.word_index(i)
|
51
|
+
return if w.nil?
|
52
|
+
|
53
|
+
@words[w] &= ~(1 << i)
|
54
|
+
end # unset(i)
|
55
|
+
|
56
|
+
# Clears all bits in the set
|
57
|
+
# @api public
|
58
|
+
def clear
|
59
|
+
self.init_words(@nbits)
|
60
|
+
end # clear
|
61
|
+
|
62
|
+
|
63
|
+
#
|
64
|
+
# Implementation
|
65
|
+
#
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
# @private
|
70
|
+
def init_words(nbits)
|
71
|
+
n = word_index(nbits-1) + 1
|
72
|
+
@words = Array.new(n) { 1 }
|
73
|
+
end # init_words
|
74
|
+
|
75
|
+
# @private
|
76
|
+
def word_index(i)
|
77
|
+
i >> ADDRESS_BITS_PER_WORD
|
78
|
+
end # word_index(i)
|
79
|
+
end # BitSet
|
80
|
+
end # AMQP
|
data/lib/amqp/broker.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
# A utility class that makes inspection of broker capabilities easier.
|
5
|
+
class Broker
|
6
|
+
|
7
|
+
#
|
8
|
+
# API
|
9
|
+
#
|
10
|
+
|
11
|
+
RABBITMQ_PRODUCT = "RabbitMQ".freeze
|
12
|
+
|
13
|
+
# Broker information
|
14
|
+
# @return [Hash]
|
15
|
+
# @see Session#server_properties
|
16
|
+
attr_reader :properties
|
17
|
+
|
18
|
+
# @return [Hash] properties Broker information
|
19
|
+
# @see Session#server_properties
|
20
|
+
def initialize(properties)
|
21
|
+
@properties = properties
|
22
|
+
end # initialize(properties)
|
23
|
+
|
24
|
+
# @group Product information
|
25
|
+
|
26
|
+
# @return [Boolean] true if broker is RabbitMQ
|
27
|
+
def rabbitmq?
|
28
|
+
self.product == RABBITMQ_PRODUCT
|
29
|
+
end # rabbitmq?
|
30
|
+
|
31
|
+
# @return [String] Broker product information
|
32
|
+
def product
|
33
|
+
@product ||= @properties["product"]
|
34
|
+
end # product
|
35
|
+
|
36
|
+
# @return [String] Broker version
|
37
|
+
def version
|
38
|
+
@version ||= @properties["version"]
|
39
|
+
end # version
|
40
|
+
|
41
|
+
# @endgroup
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
# @group Product capabilities
|
46
|
+
|
47
|
+
# @return [Boolean]
|
48
|
+
def supports_publisher_confirmations?
|
49
|
+
@properties["capabilities"]["publisher_confirms"]
|
50
|
+
end # supports_publisher_confirmations?
|
51
|
+
|
52
|
+
# @return [Boolean]
|
53
|
+
def supports_basic_nack?
|
54
|
+
@properties["capabilities"]["basic.nack"]
|
55
|
+
end # supports_basic_nack?
|
56
|
+
|
57
|
+
# @return [Boolean]
|
58
|
+
def supports_consumer_cancel_notifications?
|
59
|
+
@properties["capabilities"]["consumer_cancel_notify"]
|
60
|
+
end # supports_consumer_cancel_notifications?
|
61
|
+
|
62
|
+
# @return [Boolean]
|
63
|
+
def supports_exchange_to_exchange_bindings?
|
64
|
+
@properties["capabilities"]["exchange_exchange_bindings"]
|
65
|
+
end # supports_exchange_to_exchange_bindings?
|
66
|
+
|
67
|
+
|
68
|
+
# @endgroup
|
69
|
+
|
70
|
+
|
71
|
+
end # Broker
|
72
|
+
end # AMQP
|
data/lib/amqp/channel.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require "amqp/int_allocator"
|
3
4
|
require "amqp/exchange"
|
4
5
|
require "amqp/queue"
|
5
6
|
|
@@ -156,9 +157,6 @@ module AMQP
|
|
156
157
|
attr_reader :status
|
157
158
|
|
158
159
|
|
159
|
-
# @note We encourage you to not rely on default AMQP connection and pass connection parameter
|
160
|
-
# explicitly.
|
161
|
-
#
|
162
160
|
# @param [AMQP::Session] connection Connection to open this channel on. If not given, default AMQP
|
163
161
|
# connection (accessible via {AMQP.connection}) will be used.
|
164
162
|
# @param [Integer] id Channel id. Must not be greater than max channel id client and broker
|
@@ -194,6 +192,7 @@ module AMQP
|
|
194
192
|
#
|
195
193
|
#
|
196
194
|
# @option options [Boolean] :prefetch (nil) Specifies number of messages to prefetch. Channel-specific. See {AMQP::Channel#prefetch}.
|
195
|
+
# @option options [Boolean] :auto_recovery (nil) Turns on automatic network failure recovery mode for this channel.
|
197
196
|
#
|
198
197
|
# @yield [channel, open_ok] Yields open channel instance and AMQP method (channel.open-ok) instance. The latter is optional.
|
199
198
|
# @yieldparam [Channel] channel Channel that is successfully open
|
@@ -205,14 +204,18 @@ module AMQP
|
|
205
204
|
def initialize(connection = nil, id = self.class.next_channel_id, options = {}, &block)
|
206
205
|
raise 'AMQP can only be used from within EM.run {}' unless EM.reactor_running?
|
207
206
|
|
208
|
-
@options = options
|
209
207
|
@connection = connection || AMQP.connection || AMQP.start
|
208
|
+
# this means 2nd argument is options
|
209
|
+
if id.kind_of?(Hash)
|
210
|
+
options = options.merge(id)
|
211
|
+
id = self.class.next_channel_id
|
212
|
+
end
|
210
213
|
|
211
|
-
super(@connection, id)
|
214
|
+
super(@connection, id, options)
|
212
215
|
|
213
216
|
@rpcs = Hash.new
|
214
217
|
# we need this deferrable to mimic what AMQP gem 0.7 does to enable
|
215
|
-
# the following (
|
218
|
+
# the following (pseudo-synchronous) style of programming some people use in their
|
216
219
|
# existing codebases:
|
217
220
|
#
|
218
221
|
# connection = AMQP.connect
|
@@ -242,6 +245,32 @@ module AMQP
|
|
242
245
|
end # @connection.on_open
|
243
246
|
end
|
244
247
|
|
248
|
+
# @return [Boolean] true if this channel is in automatic recovery mode
|
249
|
+
# @see #auto_recovering?
|
250
|
+
attr_accessor :auto_recovery
|
251
|
+
|
252
|
+
# @return [Boolean] true if this channel uses automatic recovery mode
|
253
|
+
def auto_recovering?
|
254
|
+
@auto_recovery
|
255
|
+
end # auto_recovering?
|
256
|
+
|
257
|
+
# Called by associated connection object when AMQP connection has been re-established
|
258
|
+
# (for example, after a network failure).
|
259
|
+
#
|
260
|
+
# @api plugin
|
261
|
+
def auto_recover
|
262
|
+
return unless auto_recovering?
|
263
|
+
|
264
|
+
self.open do
|
265
|
+
@channel_is_open_deferrable.succeed
|
266
|
+
|
267
|
+
# exchanges must be recovered first because queue recovery includes recovery of bindings. MK.
|
268
|
+
@exchanges.each { |name, e| e.auto_recover }
|
269
|
+
@queues.each { |name, q| q.auto_recover }
|
270
|
+
end
|
271
|
+
end # auto_recover
|
272
|
+
|
273
|
+
|
245
274
|
|
246
275
|
|
247
276
|
# @group Declaring exchanges
|
@@ -753,6 +782,8 @@ module AMQP
|
|
753
782
|
# @return [Queue]
|
754
783
|
# @api public
|
755
784
|
def queue(name = AMQ::Protocol::EMPTY_STRING, opts = {}, &block)
|
785
|
+
raise ArgumentError.new("queue name must not be nil; if you want broker to generate queue name for you, pass an empty string") if name.nil?
|
786
|
+
|
756
787
|
if name && !name.empty? && (queue = find_queue(name))
|
757
788
|
extended_opts = Queue.add_default_options(name, opts, block)
|
758
789
|
|
@@ -791,6 +822,13 @@ module AMQP
|
|
791
822
|
register_queue(queue)
|
792
823
|
end
|
793
824
|
|
825
|
+
# @return [Array<AMQP::Queue>] Queues cache for this channel
|
826
|
+
# @api plugin
|
827
|
+
# @private
|
828
|
+
def queues
|
829
|
+
@queues
|
830
|
+
end # queues
|
831
|
+
|
794
832
|
# @endgroup
|
795
833
|
|
796
834
|
|
@@ -872,7 +910,10 @@ module AMQP
|
|
872
910
|
#
|
873
911
|
# @api public
|
874
912
|
def close(reply_code = 200, reply_text = DEFAULT_REPLY_TEXT, class_id = 0, method_id = 0, &block)
|
875
|
-
super(reply_code, reply_text, class_id, method_id, &block)
|
913
|
+
r = super(reply_code, reply_text, class_id, method_id, &block)
|
914
|
+
self.class.release_channel_id(@id)
|
915
|
+
|
916
|
+
r
|
876
917
|
end
|
877
918
|
|
878
919
|
# @endgroup
|
@@ -1090,8 +1131,10 @@ module AMQP
|
|
1090
1131
|
#
|
1091
1132
|
# @api plugin
|
1092
1133
|
# @private
|
1093
|
-
def handle_connection_interruption(
|
1094
|
-
super(
|
1134
|
+
def handle_connection_interruption(reason = nil)
|
1135
|
+
super(reason)
|
1136
|
+
|
1137
|
+
self.class.release_channel_id(@id) unless auto_recovering?
|
1095
1138
|
@channel_is_open_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
|
1096
1139
|
end
|
1097
1140
|
|
@@ -1102,18 +1145,55 @@ module AMQP
|
|
1102
1145
|
@channel_id_mutex ||= Mutex.new
|
1103
1146
|
end
|
1104
1147
|
|
1105
|
-
# Returns
|
1148
|
+
# Returns next available channel id. This method is thread safe.
|
1149
|
+
#
|
1106
1150
|
# @return [Fixnum]
|
1107
1151
|
# @api public
|
1152
|
+
# @see Channel.release_channel_id
|
1153
|
+
# @see Channel.reset_channel_id_allocator
|
1108
1154
|
def self.next_channel_id
|
1109
1155
|
channel_id_mutex.synchronize do
|
1110
|
-
|
1111
|
-
@last_channel_id += 1
|
1156
|
+
self.initialize_channel_id_allocator
|
1112
1157
|
|
1113
|
-
@
|
1158
|
+
@int_allocator.allocate
|
1114
1159
|
end
|
1115
1160
|
end
|
1116
1161
|
|
1162
|
+
# Releases previously allocated channel id. This method is thread safe.
|
1163
|
+
#
|
1164
|
+
# @param [Fixnum] Channel id to release
|
1165
|
+
# @api public
|
1166
|
+
# @see Channel.next_channel_id
|
1167
|
+
# @see Channel.reset_channel_id_allocator
|
1168
|
+
def self.release_channel_id(i)
|
1169
|
+
channel_id_mutex.synchronize do
|
1170
|
+
self.initialize_channel_id_allocator
|
1171
|
+
|
1172
|
+
@int_allocator.release(i)
|
1173
|
+
end
|
1174
|
+
end # self.release_channel_id(i)
|
1175
|
+
|
1176
|
+
# Resets channel allocator. This method is thread safe.
|
1177
|
+
# @api public
|
1178
|
+
# @see Channel.next_channel_id
|
1179
|
+
# @see Channel.release_channel_id
|
1180
|
+
def self.reset_channel_id_allocator
|
1181
|
+
channel_id_mutex.synchronize do
|
1182
|
+
initialize_channel_id_allocator
|
1183
|
+
|
1184
|
+
@int_allocator.reset
|
1185
|
+
end
|
1186
|
+
end # self.reset_channel_id_allocator
|
1187
|
+
|
1188
|
+
|
1189
|
+
# @private
|
1190
|
+
def self.initialize_channel_id_allocator
|
1191
|
+
# TODO: ideally, this should be in agreement with agreed max number of channels of the connection,
|
1192
|
+
# but it is possible that value either not yet available. MK.
|
1193
|
+
max_channel = (1 << 16) - 1
|
1194
|
+
@int_allocator ||= IntAllocator.new(1, max_channel)
|
1195
|
+
end # self.initialize_channel_id_allocator
|
1196
|
+
|
1117
1197
|
# @private
|
1118
1198
|
# @api plugin
|
1119
1199
|
def register_rpc(rpc)
|