amqp 0.8.0.rc12 → 0.8.0.rc13
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -1
- data/.yardopts +2 -1
- data/CONTRIBUTORS +29 -22
- data/Gemfile +2 -1
- data/README.md +241 -0
- data/amqp.gemspec +7 -5
- data/bin/set_test_suite_realms_up.sh +6 -6
- data/docs/08Migration.textile +3 -1
- data/docs/Bindings.textile +3 -1
- data/docs/Clustering.textile +4 -0
- data/docs/ConnectingToTheBroker.textile +108 -86
- data/docs/ConnectionEncryptionWithTLS.textile +3 -1
- data/docs/DocumentationGuidesIndex.textile +24 -2
- data/docs/Durability.textile +22 -1
- data/docs/ErrorHandling.textile +21 -1
- data/docs/Exchanges.textile +181 -9
- data/docs/GettingStarted.textile +65 -167
- data/docs/Queues.textile +400 -355
- data/docs/RabbitMQVersions.textile +34 -3
- data/docs/Routing.textile +4 -1
- data/docs/RunningTests.textile +116 -0
- data/docs/Troubleshooting.textile +131 -0
- data/docs/VendorSpecificExtensions.textile +20 -0
- data/examples/channels/qos_aka_prefetch.rb +3 -3
- data/examples/channels/qos_aka_prefetch_without_callback.rb +2 -2
- data/examples/error_handling/channel_level_exception.rb +1 -1
- data/examples/error_handling/channel_level_exception_with_multiple_channels_involved.rb +1 -0
- data/examples/error_handling/connection_level_exception.rb +26 -0
- data/examples/error_handling/global_channel_level_exception_handler.rb +3 -3
- data/examples/exchanges/autodeletion_of_exchanges.rb +37 -0
- data/examples/extensions/rabbitmq/per_queue_message_ttl.rb +3 -3
- data/examples/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +2 -2
- data/examples/guides/getting_started/{03_babblr.rb → 03_blabbr.rb} +0 -0
- data/examples/guides/queues/01a_declaring_a_server_named_queue_using_queue_constructor.rb +18 -0
- data/examples/guides/queues/01b_declaring_a_queue_using_queue_constructor.rb +18 -0
- data/examples/guides/queues/02a_declaring_a_durable_shared_queue.rb +18 -0
- data/examples/guides/queues/02b_declaring_a_durable_shared_queue.rb +18 -0
- data/examples/guides/queues/03a_declaring_a_temporary_exclusive_queue.rb +19 -0
- data/examples/guides/queues/03b_declaring_a_temporary_exclusive_queue.rb +18 -0
- data/examples/guides/queues/{05_binding_a_queue_using_exchange_instance.rb → 04_bind_a_queue_using_exchange_instance.rb} +2 -2
- data/examples/guides/queues/{06_biding_a_queue_using_exchange_name_string.rb → 05_bind_a_queue_using_exchange_name.rb} +2 -2
- data/examples/guides/queues/{07_subscribing_to_receive_messages.rb → 06_subscribe_to_receive_messages.rb} +1 -1
- data/examples/guides/queues/{08_poll_for_messages.rb → 07_fetch_a_message_from_the_queue.rb} +1 -1
- data/examples/guides/queues/{09_unsubscribing_a_consumer.rb → 08_unsubscribing_a_consumer.rb} +0 -0
- data/examples/guides/queues/{10_unbinding_from_exchange.rb → 09_unbinding_from_exchange.rb} +2 -2
- data/examples/guides/queues/{11_purge_a_queue.rb → 10_purge_a_queue.rb} +2 -2
- data/examples/guides/queues/{12_deleting_a_queue.rb → 11_deleting_a_queue.rb} +2 -2
- data/examples/hello_world.rb +1 -1
- data/examples/hello_world_with_an_empty_string.rb +33 -0
- data/examples/issues/amq_client_issue_7.rb +31 -0
- data/examples/issues/amq_protocol_issue_14.rb +46 -0
- data/examples/issues/issue_75.rb +23 -0
- data/examples/issues/issue_79.rb +35 -0
- data/examples/issues/issue_80.rb +40 -0
- data/examples/publishing/{publishing_and_immediately_stopping_event_loop.rb → publishing_a_one_off_message.rb} +9 -12
- data/examples/publishing/publishing_callback.rb +52 -0
- data/examples/publishing/returned_messages.rb +3 -3
- data/examples/queues/accessing_message_metadata.rb +60 -0
- data/examples/queues/queue_status.rb +0 -7
- data/examples/queues/rejecting_messages_without_requeueuing.rb +47 -0
- data/examples/queues/using_explicit_acknowledgements.rb +96 -0
- data/examples/routing/headers_routing.rb +54 -0
- data/lib/amqp/channel.rb +245 -40
- data/lib/amqp/client.rb +23 -11
- data/lib/amqp/exchange.rb +58 -41
- data/lib/amqp/queue.rb +66 -13
- data/lib/amqp/version.rb +1 -1
- data/spec/integration/authentication_spec.rb +5 -5
- data/spec/integration/basic_get_spec.rb +1 -1
- data/spec/integration/channel_close_spec.rb +10 -3
- data/spec/integration/queue_declaration_spec.rb +26 -5
- data/spec/integration/topic_subscription_spec.rb +1 -1
- data/spec/unit/amqp/client_spec.rb +7 -54
- data/tasks.rb +1 -8
- metadata +64 -23
- data/README.textile +0 -229
@@ -8,12 +8,6 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
|
|
8
8
|
|
9
9
|
require 'amqp'
|
10
10
|
|
11
|
-
if RUBY_VERSION == "1.8.7"
|
12
|
-
class Array
|
13
|
-
alias sample choice
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
11
|
puts "=> Publishing and immediately stopping the event loop in the callback"
|
18
12
|
puts
|
19
13
|
|
@@ -23,15 +17,18 @@ puts
|
|
23
17
|
# http://bit.ly/lQP1Al
|
24
18
|
|
25
19
|
EventMachine.run do
|
26
|
-
|
27
|
-
channel
|
28
|
-
channel.on_error
|
20
|
+
connection = AMQP.connect(:host => '127.0.0.1')
|
21
|
+
channel = AMQP::Channel.new(connection)
|
22
|
+
channel.on_error do |ch, channel_close|
|
23
|
+
puts "Channel-level error: #{channel_close.reply_text}, shutting down..."
|
24
|
+
connection.close { EventMachine.stop }
|
25
|
+
end
|
29
26
|
|
30
27
|
queue = channel.queue("some_topic", :auto_delete => true)
|
31
28
|
exchange = channel.topic("foo", :durable => true, :auto_delete => true)
|
32
29
|
|
33
|
-
exchange.publish(
|
34
|
-
puts '
|
35
|
-
EventMachine.stop
|
30
|
+
exchange.publish('hello world', :routing_key => "some_topic", :persistent => true, :nowait => false ) do
|
31
|
+
puts 'About to disconnect'
|
32
|
+
connection.close { EventMachine.stop }
|
36
33
|
end
|
37
34
|
end
|
@@ -0,0 +1,52 @@
|
|
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 "=> Using a callback to #publish. It is run on the _next_ EventMachine loop run."
|
12
|
+
puts
|
13
|
+
|
14
|
+
EventMachine.run do
|
15
|
+
connection = AMQP.connect(:host => '127.0.0.1')
|
16
|
+
channel = AMQP::Channel.new(connection)
|
17
|
+
channel.on_error do |ch, channel_close|
|
18
|
+
puts "Channel-level error: #{channel_close.reply_text}, shutting down..."
|
19
|
+
connection.close { EventMachine.stop }
|
20
|
+
end
|
21
|
+
|
22
|
+
queue = channel.queue("amqpgem.examples.publishing.queue1", :auto_delete => true)
|
23
|
+
exchange = channel.fanout("amqpgem.examples.topic", :durable => true, :auto_delete => true)
|
24
|
+
|
25
|
+
queue.bind(exchange, :routing_key => "some_topic")
|
26
|
+
|
27
|
+
|
28
|
+
# Don't be deceived: this callback is run on the next event loop tick. There is no guarantee that your
|
29
|
+
# data was sent: there is buffering going on on multiple layers (C++ core of EventMachine, libc functions,
|
30
|
+
# kernel uses buffering for many I/O system calls).
|
31
|
+
#
|
32
|
+
# This callback is simply for convenience. In a distributed environment, the only way to know when your data
|
33
|
+
# is sent is when you receive an acknowledgement. TCP works that way. MK.
|
34
|
+
|
35
|
+
100.times do |i|
|
36
|
+
exchange.publish("hello world #{i}", :routing_key => "some_topic", :persistent => true) do
|
37
|
+
puts "Callback #{i} has fired"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
exchange.publish("hello world 101", :routing_key => "some_topic", :persistent => false) do
|
42
|
+
puts "Callback 101 has fired"
|
43
|
+
end
|
44
|
+
|
45
|
+
exchange.publish("hello world 102", :routing_key => "some_topic", :persistent => true) do
|
46
|
+
puts "Callback 102 has fired"
|
47
|
+
end
|
48
|
+
|
49
|
+
EventMachine.add_timer(1) do
|
50
|
+
connection.close { EventMachine.stop }
|
51
|
+
end
|
52
|
+
end
|
@@ -19,17 +19,17 @@ puts
|
|
19
19
|
|
20
20
|
AMQP.start(:host => '127.0.0.1') do |connection|
|
21
21
|
channel = AMQP.channel
|
22
|
-
channel.on_error {
|
22
|
+
channel.on_error { |ch, channel_close| EventMachine.stop; raise "channel error: #{channel_close.reply_text}" }
|
23
23
|
|
24
24
|
exchange = channel.fanout("amq.fanout")
|
25
|
-
exchange.on_return do |basic_return,
|
25
|
+
exchange.on_return do |basic_return, metadata, payload|
|
26
26
|
puts "#{payload} was returned! reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}"
|
27
27
|
end
|
28
28
|
|
29
29
|
EventMachine.add_timer(0.3) {
|
30
30
|
10.times do |i|
|
31
31
|
exchange.publish("Message ##{i}", :immediate => true)
|
32
|
-
end
|
32
|
+
end
|
33
33
|
}
|
34
34
|
|
35
35
|
EventMachine.add_timer(2) {
|
@@ -0,0 +1,60 @@
|
|
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 "Connected 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
|
+
exchange = channel.direct("amq.direct")
|
18
|
+
|
19
|
+
queue.bind(exchange)
|
20
|
+
|
21
|
+
channel.on_error do |ch, channel_close|
|
22
|
+
puts channel_close.reply_text
|
23
|
+
connection.close { EventMachine.stop }
|
24
|
+
end
|
25
|
+
|
26
|
+
queue.subscribe do |metadata, payload|
|
27
|
+
puts "metadata.routing_key : #{metadata.routing_key}"
|
28
|
+
puts "metadata.content_type: #{metadata.content_type}"
|
29
|
+
puts "metadata.priority : #{metadata.priority}"
|
30
|
+
puts "metadata.headers : #{metadata.headers.inspect}"
|
31
|
+
puts "metadata.timestamp : #{metadata.timestamp.inspect}"
|
32
|
+
puts "metadata.type : #{metadata.type}"
|
33
|
+
puts "metadata.delivery_tag: #{metadata.delivery_tag}"
|
34
|
+
puts "metadata.redelivered : #{metadata.redelivered}"
|
35
|
+
|
36
|
+
puts "metadata.app_id : #{metadata.app_id}"
|
37
|
+
puts "metadata.exchange : #{metadata.exchange}"
|
38
|
+
puts
|
39
|
+
puts "Received a message: #{payload}. Disconnecting..."
|
40
|
+
|
41
|
+
connection.close {
|
42
|
+
EventMachine.stop { exit }
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
exchange.publish("Hello, world!",
|
47
|
+
:app_id => "amqpgem.example",
|
48
|
+
:priority => 8,
|
49
|
+
:type => "kinda.checkin",
|
50
|
+
# headers table keys can be anything
|
51
|
+
:headers => {
|
52
|
+
:coordinates => {
|
53
|
+
:latitude => 59.35,
|
54
|
+
:longitude => 18.066667
|
55
|
+
},
|
56
|
+
:participants => 11,
|
57
|
+
:venue => "Stockholm"
|
58
|
+
},
|
59
|
+
:timestamp => Time.now.to_i)
|
60
|
+
end
|
@@ -8,13 +8,6 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
|
|
8
8
|
|
9
9
|
require 'amqp'
|
10
10
|
|
11
|
-
if RUBY_VERSION == "1.8.7"
|
12
|
-
class Array
|
13
|
-
alias sample choice
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
11
|
puts "=> Queue#status example"
|
19
12
|
puts
|
20
13
|
AMQP.start(:host => 'localhost') do |connection|
|
@@ -0,0 +1,47 @@
|
|
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
|
+
if RUBY_VERSION == "1.8.7"
|
12
|
+
class Array
|
13
|
+
alias sample choice
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
puts "=> Queue#status example"
|
19
|
+
puts
|
20
|
+
AMQP.start(:host => 'localhost') do |connection|
|
21
|
+
channel = AMQP::Channel.new(connection)
|
22
|
+
|
23
|
+
exchange = channel.fanout("amqpgem.integration.queue.status.fanout", :auto_delete => true)
|
24
|
+
queue = channel.queue("amqpgem.integration.queue.status.queue", :auto_delete => true)
|
25
|
+
|
26
|
+
queue.bind(exchange).subscribe do |metadata, payload|
|
27
|
+
puts "Rejecting #{payload}"
|
28
|
+
channel.reject(metadata.delivery_tag)
|
29
|
+
end
|
30
|
+
|
31
|
+
100.times do |i|
|
32
|
+
print "."
|
33
|
+
exchange.publish(Time.now.to_i.to_s + "_#{i}", :key => queue.name)
|
34
|
+
end
|
35
|
+
$stdout.flush
|
36
|
+
|
37
|
+
|
38
|
+
show_stopper = Proc.new do
|
39
|
+
$stdout.puts "Stopping..."
|
40
|
+
connection.close {
|
41
|
+
EventMachine.stop { exit }
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
Signal.trap "INT", show_stopper
|
46
|
+
EventMachine.add_timer(2, show_stopper)
|
47
|
+
end
|
@@ -0,0 +1,96 @@
|
|
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 "=> Subscribing for messages using explicit acknowledgements model"
|
12
|
+
puts
|
13
|
+
|
14
|
+
# this example uses Kernel#sleep and thus we must run EventMachine reactor in
|
15
|
+
# a separate thread, or nothing will be sent/received while we sleep() on the current thread.
|
16
|
+
t = Thread.new { EventMachine.run }
|
17
|
+
sleep(0.5)
|
18
|
+
|
19
|
+
# open two connections to imitate two apps
|
20
|
+
connection1 = AMQP.connect
|
21
|
+
connection2 = AMQP.connect
|
22
|
+
connection3 = AMQP.connect
|
23
|
+
|
24
|
+
channel_exception_handler = Proc.new { |ch, channel_close| EventMachine.stop; raise "channel error: #{channel_close.reply_text}" }
|
25
|
+
|
26
|
+
# open two channels
|
27
|
+
channel1 = AMQP::Channel.new(connection1)
|
28
|
+
channel1.on_error(&channel_exception_handler)
|
29
|
+
# first app will be given up to 3 messages at a time. If it doesn't
|
30
|
+
# ack any messages after it was delivered 3, messages will be routed to
|
31
|
+
# the app #2.
|
32
|
+
channel1.prefetch(3)
|
33
|
+
|
34
|
+
channel2 = AMQP::Channel.new(connection2)
|
35
|
+
channel2.on_error(&channel_exception_handler)
|
36
|
+
# app #2 processes messages one-by-one and has to send and ack every time
|
37
|
+
channel2.prefetch(1)
|
38
|
+
|
39
|
+
# app 3 will just publish messages
|
40
|
+
channel3 = AMQP::Channel.new(connection3)
|
41
|
+
channel3.on_error(&channel_exception_handler)
|
42
|
+
|
43
|
+
exchange = channel3.direct("amq.direct")
|
44
|
+
|
45
|
+
queue1 = channel1.queue("amqpgem.examples.acknowledgements.explicit", :auto_delete => false)
|
46
|
+
# purge the queue so that we don't get any redeliveries from previous runs
|
47
|
+
queue1.purge
|
48
|
+
queue1.bind(exchange).subscribe(:ack => true) do |metadata, payload|
|
49
|
+
# do some work
|
50
|
+
sleep(0.2)
|
51
|
+
|
52
|
+
# acknowledge some messages, they will be removed from the queue
|
53
|
+
if rand > 0.5
|
54
|
+
# FYI: there is a shortcut, metadata.ack
|
55
|
+
channel1.acknowledge(metadata.delivery_tag, false)
|
56
|
+
puts "[consumer1] Got message ##{metadata.headers['i']}, ack-ed"
|
57
|
+
else
|
58
|
+
# some messages are not ack-ed and will remain in the queue for redelivery
|
59
|
+
# when app #1 connection is closed (either properly or due to a crash)
|
60
|
+
puts "[consumer1] Got message ##{metadata.headers['i']}, SKIPPPED"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
queue2 = channel2.queue!("amqpgem.examples.acknowledgements.explicit", :auto_delete => false)
|
65
|
+
queue2.subscribe(:ack => true) do |metadata, payload|
|
66
|
+
metadata.ack
|
67
|
+
# app 2 always acks messages
|
68
|
+
puts "[consumer2] Received #{payload}, redelivered = #{metadata.redelivered}, ack-ed"
|
69
|
+
end
|
70
|
+
|
71
|
+
# after some time one of the consumers quits/crashes
|
72
|
+
EventMachine.add_timer(4.0) {
|
73
|
+
connection1.close
|
74
|
+
puts "----- Connection 1 is now closed (we pretend that it has crashed) -----"
|
75
|
+
}
|
76
|
+
|
77
|
+
EventMachine.add_timer(10.0) do
|
78
|
+
# purge the queue so that we don't get any redeliveries on the next run
|
79
|
+
queue2.purge {
|
80
|
+
connection2.close {
|
81
|
+
connection3.close { EventMachine.stop }
|
82
|
+
}
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
i = 0
|
88
|
+
EventMachine.add_periodic_timer(0.8) {
|
89
|
+
3.times do
|
90
|
+
exchange.publish("Message ##{i}", :headers => { :i => i })
|
91
|
+
i += 1
|
92
|
+
end
|
93
|
+
}
|
94
|
+
|
95
|
+
|
96
|
+
t.join
|
@@ -0,0 +1,54 @@
|
|
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 "=> Headers routing example"
|
12
|
+
puts
|
13
|
+
AMQP.start do |connection|
|
14
|
+
channel = AMQP::Channel.new(connection)
|
15
|
+
channel.on_error do |ch, channel_close|
|
16
|
+
puts "A channel-level exception: #{channel_close.inspect}"
|
17
|
+
end
|
18
|
+
|
19
|
+
exchange = channel.headers("amq.match", :durable => true)
|
20
|
+
|
21
|
+
channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'all', :arch => "x64", :os => 'linux' }).subscribe do |metadata, payload|
|
22
|
+
puts "[linux/x64] Got a message: #{payload}"
|
23
|
+
end
|
24
|
+
channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'all', :arch => "x32", :os => 'linux' }).subscribe do |metadata, payload|
|
25
|
+
puts "[linux/x32] Got a message: #{payload}"
|
26
|
+
end
|
27
|
+
channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'any', :os => 'linux', :arch => "__any__" }).subscribe do |metadata, payload|
|
28
|
+
puts "[linux] Got a message: #{payload}"
|
29
|
+
end
|
30
|
+
channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'any', :os => 'macosx', :cores => 8 }).subscribe do |metadata, payload|
|
31
|
+
puts "[macosx|octocore] Got a message: #{payload}"
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
EventMachine.add_timer(0.5) do
|
36
|
+
exchange.publish "For linux/x64", :headers => { :arch => "x64", :os => 'linux' }
|
37
|
+
exchange.publish "For linux/x32", :headers => { :arch => "x32", :os => 'linux' }
|
38
|
+
exchange.publish "For linux", :headers => { :os => 'linux' }
|
39
|
+
exchange.publish "For OS X", :headers => { :os => 'macosx' }
|
40
|
+
exchange.publish "For solaris/x64", :headers => { :os => 'solaris', :arch => 'x64' }
|
41
|
+
exchange.publish "For ocotocore", :headers => { :cores => 8 }
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
show_stopper = Proc.new do
|
46
|
+
$stdout.puts "Stopping..."
|
47
|
+
connection.close {
|
48
|
+
EventMachine.stop { exit }
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
Signal.trap "INT", show_stopper
|
53
|
+
EventMachine.add_timer(2, show_stopper)
|
54
|
+
end
|
data/lib/amqp/channel.rb
CHANGED
@@ -205,6 +205,7 @@ module AMQP
|
|
205
205
|
def initialize(connection = nil, id = self.class.next_channel_id, options = {}, &block)
|
206
206
|
raise 'AMQP can only be used from within EM.run {}' unless EM.reactor_running?
|
207
207
|
|
208
|
+
@options = options
|
208
209
|
@connection = connection || AMQP.connection || AMQP.start
|
209
210
|
|
210
211
|
super(@connection, id)
|
@@ -241,20 +242,10 @@ module AMQP
|
|
241
242
|
end # @connection.on_open
|
242
243
|
end
|
243
244
|
|
244
|
-
# Takes a block that will be deferred till the moment when channel is considered open
|
245
|
-
# (channel.open-ok is received from the broker). If you need to delay an operation
|
246
|
-
# till the moment channel is open, this method is what you are looking for.
|
247
|
-
#
|
248
|
-
# Multiple callbacks are supported. If when this moment is called, channel is already
|
249
|
-
# open, block is executed immediately.
|
250
|
-
#
|
251
|
-
# @api public
|
252
|
-
def once_open(&block)
|
253
|
-
@channel_is_open_deferrable.callback(&block)
|
254
|
-
end # once_open(&block)
|
255
|
-
alias once_opened once_open
|
256
245
|
|
257
246
|
|
247
|
+
# @group Declaring exchanges
|
248
|
+
|
258
249
|
# Defines, intializes and returns a direct Exchange instance.
|
259
250
|
#
|
260
251
|
# Learn more about direct exchanges in {Exchange Exchange class documentation}.
|
@@ -336,6 +327,7 @@ module AMQP
|
|
336
327
|
|
337
328
|
validate_parameters_match!(exchange, extended_opts)
|
338
329
|
|
330
|
+
block.call(exchange) if block
|
339
331
|
exchange
|
340
332
|
else
|
341
333
|
register_exchange(Exchange.new(self, :direct, name, opts, &block))
|
@@ -443,6 +435,7 @@ module AMQP
|
|
443
435
|
|
444
436
|
validate_parameters_match!(exchange, extended_opts)
|
445
437
|
|
438
|
+
block.call(exchange) if block
|
446
439
|
exchange
|
447
440
|
else
|
448
441
|
register_exchange(Exchange.new(self, :fanout, name, opts, &block))
|
@@ -558,6 +551,7 @@ module AMQP
|
|
558
551
|
|
559
552
|
validate_parameters_match!(exchange, extended_opts)
|
560
553
|
|
554
|
+
block.call(exchange) if block
|
561
555
|
exchange
|
562
556
|
else
|
563
557
|
register_exchange(Exchange.new(self, :topic, name, opts, &block))
|
@@ -601,9 +595,53 @@ module AMQP
|
|
601
595
|
# @raise [AMQP::Error] Raised when exchange is declared with :passive => true and the exchange does not exist.
|
602
596
|
#
|
603
597
|
#
|
604
|
-
# @example Using
|
598
|
+
# @example Using headers exchange to route messages based on multiple attributes (OS, architecture, # of cores)
|
599
|
+
#
|
600
|
+
# puts "=> Headers routing example"
|
601
|
+
# puts
|
602
|
+
# AMQP.start do |connection|
|
603
|
+
# channel = AMQP::Channel.new(connection)
|
604
|
+
# channel.on_error do |ch, channel_close|
|
605
|
+
# puts "A channel-level exception: #{channel_close.inspect}"
|
606
|
+
# end
|
607
|
+
#
|
608
|
+
# exchange = channel.headers("amq.match", :durable => true)
|
609
|
+
#
|
610
|
+
# channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'all', :arch => "x64", :os => 'linux' }).subscribe do |metadata, payload|
|
611
|
+
# puts "[linux/x64] Got a message: #{payload}"
|
612
|
+
# end
|
613
|
+
# channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'all', :arch => "x32", :os => 'linux' }).subscribe do |metadata, payload|
|
614
|
+
# puts "[linux/x32] Got a message: #{payload}"
|
615
|
+
# end
|
616
|
+
# channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'any', :os => 'linux', :arch => "__any__" }).subscribe do |metadata, payload|
|
617
|
+
# puts "[linux] Got a message: #{payload}"
|
618
|
+
# end
|
619
|
+
# channel.queue("", :auto_delete => true).bind(exchange, :arguments => { 'x-match' => 'any', :os => 'macosx', :cores => 8 }).subscribe do |metadata, payload|
|
620
|
+
# puts "[macosx|octocore] Got a message: #{payload}"
|
621
|
+
# end
|
622
|
+
#
|
623
|
+
#
|
624
|
+
# EventMachine.add_timer(0.5) do
|
625
|
+
# exchange.publish "For linux/x64", :headers => { :arch => "x64", :os => 'linux' }
|
626
|
+
# exchange.publish "For linux/x32", :headers => { :arch => "x32", :os => 'linux' }
|
627
|
+
# exchange.publish "For linux", :headers => { :os => 'linux' }
|
628
|
+
# exchange.publish "For OS X", :headers => { :os => 'macosx' }
|
629
|
+
# exchange.publish "For solaris/x64", :headers => { :os => 'solaris', :arch => 'x64' }
|
630
|
+
# exchange.publish "For ocotocore", :headers => { :cores => 8 }
|
631
|
+
# end
|
632
|
+
#
|
633
|
+
#
|
634
|
+
# show_stopper = Proc.new do
|
635
|
+
# $stdout.puts "Stopping..."
|
636
|
+
# connection.close {
|
637
|
+
# EventMachine.stop { exit }
|
638
|
+
# }
|
639
|
+
# end
|
640
|
+
#
|
641
|
+
# Signal.trap "INT", show_stopper
|
642
|
+
# EventMachine.add_timer(2, show_stopper)
|
643
|
+
# end
|
605
644
|
#
|
606
|
-
# # TODO
|
607
645
|
#
|
608
646
|
#
|
609
647
|
# @see Exchange
|
@@ -619,12 +657,18 @@ module AMQP
|
|
619
657
|
|
620
658
|
validate_parameters_match!(exchange, extended_opts)
|
621
659
|
|
660
|
+
block.call(exchange) if block
|
622
661
|
exchange
|
623
662
|
else
|
624
663
|
register_exchange(Exchange.new(self, :headers, name, opts, &block))
|
625
664
|
end
|
626
665
|
end
|
627
666
|
|
667
|
+
# @endgroup
|
668
|
+
|
669
|
+
|
670
|
+
# @group Declaring queues
|
671
|
+
|
628
672
|
|
629
673
|
# Declares and returns a Queue instance associated with this channel. See {Queue Queue class documentation} for
|
630
674
|
# more information about queues.
|
@@ -714,19 +758,13 @@ module AMQP
|
|
714
758
|
|
715
759
|
validate_parameters_match!(queue, extended_opts)
|
716
760
|
|
761
|
+
block.call(queue) if block
|
717
762
|
queue
|
718
763
|
else
|
719
764
|
self.queue!(name, opts, &block)
|
720
765
|
end
|
721
766
|
end
|
722
767
|
|
723
|
-
# Returns true if channel is not closed.
|
724
|
-
# @return [Boolean]
|
725
|
-
# @api public
|
726
|
-
def open?
|
727
|
-
self.status == :opened || self.status == :opening
|
728
|
-
end # open?
|
729
|
-
|
730
768
|
# Same as {Channel#queue} but when queue with the same name already exists in this channel
|
731
769
|
# object's cache, this method will replace existing queue with a newly defined one. Consider
|
732
770
|
# using {Channel#queue} instead.
|
@@ -753,6 +791,9 @@ module AMQP
|
|
753
791
|
register_queue(queue)
|
754
792
|
end
|
755
793
|
|
794
|
+
# @endgroup
|
795
|
+
|
796
|
+
|
756
797
|
|
757
798
|
# Instantiates and returns an RPC instance associated with this channel.
|
758
799
|
#
|
@@ -763,8 +804,8 @@ module AMQP
|
|
763
804
|
#
|
764
805
|
# Marshalling and unmarshalling the objects is handled internally. This
|
765
806
|
# marshalling is subject to the same restrictions as defined in the
|
766
|
-
#
|
767
|
-
# library.
|
807
|
+
# [http://ruby-doc.org/core/classes/Marshal.html Marshal module} in the Ruby standard
|
808
|
+
# library.
|
768
809
|
#
|
769
810
|
# When the optional object is not passed, the returned rpc reference is
|
770
811
|
# used to send messages and arguments to the queue. See {AMQP::RPC#method_missing}
|
@@ -787,6 +828,82 @@ module AMQP
|
|
787
828
|
|
788
829
|
|
789
830
|
|
831
|
+
# Returns a hash of all rpc proxy objects.
|
832
|
+
#
|
833
|
+
# Most of the time, this method is not
|
834
|
+
# called by application code.
|
835
|
+
# @api plugin
|
836
|
+
def rpcs
|
837
|
+
@rpcs.values
|
838
|
+
end
|
839
|
+
|
840
|
+
|
841
|
+
|
842
|
+
# @group Channel lifecycle
|
843
|
+
|
844
|
+
# Opens AMQP channel.
|
845
|
+
#
|
846
|
+
# @note Instantiated channels are opened by default. This method should only be used for error recovery after network connection loss.
|
847
|
+
# @api public
|
848
|
+
def open(&block)
|
849
|
+
super(&block)
|
850
|
+
end
|
851
|
+
|
852
|
+
# @return [Boolean] true if channel is not closed.
|
853
|
+
# @api public
|
854
|
+
def open?
|
855
|
+
self.status == :opened || self.status == :opening
|
856
|
+
end # open?
|
857
|
+
|
858
|
+
# Takes a block that will be deferred till the moment when channel is considered open
|
859
|
+
# (channel.open-ok is received from the broker). If you need to delay an operation
|
860
|
+
# till the moment channel is open, this method is what you are looking for.
|
861
|
+
#
|
862
|
+
# Multiple callbacks are supported. If when this moment is called, channel is already
|
863
|
+
# open, block is executed immediately.
|
864
|
+
#
|
865
|
+
# @api public
|
866
|
+
def once_open(&block)
|
867
|
+
@channel_is_open_deferrable.callback(&block)
|
868
|
+
end # once_open(&block)
|
869
|
+
alias once_opened once_open
|
870
|
+
|
871
|
+
# Closes AMQP channel.
|
872
|
+
#
|
873
|
+
# @api public
|
874
|
+
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)
|
876
|
+
end
|
877
|
+
|
878
|
+
# @endgroup
|
879
|
+
|
880
|
+
|
881
|
+
|
882
|
+
|
883
|
+
# @group QoS and flow handling
|
884
|
+
|
885
|
+
# Asks the peer to pause or restart the flow of content data sent to a consumer.
|
886
|
+
# This is a simple flowcontrol mechanism that a peer can use to avoid overflowing its
|
887
|
+
# queues or otherwise finding itself receiving more messages than it can process. Note that
|
888
|
+
# this method is not intended for window control. It does not affect contents returned to
|
889
|
+
# Queue#get callers.
|
890
|
+
#
|
891
|
+
# @param [Boolean] Desired flow state.
|
892
|
+
#
|
893
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.5.2.3.)
|
894
|
+
# @api public
|
895
|
+
def flow(active = false, &block)
|
896
|
+
super(active, &block)
|
897
|
+
end
|
898
|
+
|
899
|
+
# @return [Boolean] True if flow in this channel is active (messages will be delivered to consumers that use this channel).
|
900
|
+
#
|
901
|
+
# @api public
|
902
|
+
def flow_is_active?
|
903
|
+
@flow_is_active
|
904
|
+
end # flow_is_active?
|
905
|
+
|
906
|
+
|
790
907
|
|
791
908
|
# @param [Fixnum] Message count
|
792
909
|
# @param [Boolean] global (false)
|
@@ -803,18 +920,106 @@ module AMQP
|
|
803
920
|
self
|
804
921
|
end
|
805
922
|
|
923
|
+
# @endgroup
|
806
924
|
|
807
925
|
|
808
|
-
|
926
|
+
|
927
|
+
# @group Message acknowledgements
|
928
|
+
|
929
|
+
# Acknowledge one or all messages on the channel.
|
809
930
|
#
|
810
|
-
#
|
811
|
-
#
|
812
|
-
# @
|
813
|
-
|
814
|
-
|
931
|
+
# @api public
|
932
|
+
# @see #reject
|
933
|
+
# @see #recover
|
934
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
|
935
|
+
def acknowledge(delivery_tag, multiple = false)
|
936
|
+
super(delivery_tag, multiple)
|
937
|
+
end # acknowledge(delivery_tag, multiple = false)
|
938
|
+
|
939
|
+
# Reject a message with given delivery tag.
|
940
|
+
#
|
941
|
+
# @api public
|
942
|
+
# @see #acknowledge
|
943
|
+
# @see #recover
|
944
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.14.)
|
945
|
+
def reject(delivery_tag, requeue = true)
|
946
|
+
super(delivery_tag, requeue)
|
947
|
+
end # reject(delivery_tag, requeue = true)
|
948
|
+
|
949
|
+
# Notifies AMQ broker that consumer has recovered and unacknowledged messages need
|
950
|
+
# to be redelivered.
|
951
|
+
#
|
952
|
+
# @return [Channel] self
|
953
|
+
#
|
954
|
+
# @note RabbitMQ as of 2.3.1 does not support basic.recover with requeue = false.
|
955
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.16.)
|
956
|
+
# @see #acknowledge
|
957
|
+
# @api public
|
958
|
+
def recover(requeue = true, &block)
|
959
|
+
super(requeue, &block)
|
960
|
+
end # recover(requeue = false, &block)
|
961
|
+
|
962
|
+
# @endgroup
|
963
|
+
|
964
|
+
|
965
|
+
|
966
|
+
|
967
|
+
# @group Transactions
|
968
|
+
|
969
|
+
# Sets the channel to use standard transactions. One must use this method at least
|
970
|
+
# once on a channel before using #tx_tommit or tx_rollback methods.
|
971
|
+
#
|
972
|
+
# @api public
|
973
|
+
def tx_select(&block)
|
974
|
+
super(&block)
|
975
|
+
end # tx_select(&block)
|
976
|
+
|
977
|
+
# Commits AMQP transaction.
|
978
|
+
#
|
979
|
+
# @api public
|
980
|
+
def tx_commit(&block)
|
981
|
+
super(&block)
|
982
|
+
end # tx_commit(&block)
|
983
|
+
|
984
|
+
# Rolls AMQP transaction back.
|
985
|
+
#
|
986
|
+
# @api public
|
987
|
+
def tx_rollback(&block)
|
988
|
+
super(&block)
|
989
|
+
end # tx_rollback(&block)
|
990
|
+
|
991
|
+
|
992
|
+
# @endgroup
|
993
|
+
|
994
|
+
|
995
|
+
|
996
|
+
|
997
|
+
|
998
|
+
# @group Error handling
|
999
|
+
|
1000
|
+
# Defines a callback that will be executed when channel is closed after
|
1001
|
+
# channel-level exception.
|
1002
|
+
#
|
1003
|
+
# @api public
|
1004
|
+
def on_error(&block)
|
1005
|
+
super(&block)
|
815
1006
|
end
|
816
1007
|
|
817
1008
|
|
1009
|
+
# Defines a global callback to be run on channel-level exception across
|
1010
|
+
# all channels. Consider using Channel#on_error instead. This method is here for sake
|
1011
|
+
# of backwards compatibility with 0.6.x and 0.7.x releases.
|
1012
|
+
# @see AMQP::Channel#on_error
|
1013
|
+
# @deprecated
|
1014
|
+
# @api public
|
1015
|
+
def self.on_error(&block)
|
1016
|
+
self.error(&block)
|
1017
|
+
end # self.on_error(&block)
|
1018
|
+
|
1019
|
+
# @endgroup
|
1020
|
+
|
1021
|
+
|
1022
|
+
|
818
1023
|
|
819
1024
|
#
|
820
1025
|
# Implementation
|
@@ -838,15 +1043,6 @@ module AMQP
|
|
838
1043
|
end
|
839
1044
|
end
|
840
1045
|
|
841
|
-
# Defines a global callback to be run on channel-level exception across
|
842
|
-
# all channels. Consider using Channel#on_error instead. This method is here for sake
|
843
|
-
# of backwards compatibility with 0.6.x and 0.7.x releases.
|
844
|
-
# @see AMQP::Channel#on_error
|
845
|
-
# @deprecated
|
846
|
-
# @api public
|
847
|
-
def self.on_error(&block)
|
848
|
-
self.error(&block)
|
849
|
-
end # self.on_error(&block)
|
850
1046
|
|
851
1047
|
# Overrides AMQ::Client::Channel version to also call global callback
|
852
1048
|
# (if defined) for backwards compatibility.
|
@@ -867,12 +1063,19 @@ module AMQP
|
|
867
1063
|
#
|
868
1064
|
# @private
|
869
1065
|
# @api plugin
|
870
|
-
def reset
|
1066
|
+
def reset(&block)
|
871
1067
|
# See AMQ::Client::Channel
|
872
1068
|
self.reset_state!
|
873
1069
|
|
874
1070
|
# there is no way to reset a deferrable; we have to use a new instance. MK.
|
875
1071
|
@channel_is_open_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
|
1072
|
+
@channel_is_open_deferrable.callback(&block)
|
1073
|
+
|
1074
|
+
@connection.on_connection do
|
1075
|
+
@channel_is_open_deferrable.succeed
|
1076
|
+
|
1077
|
+
self.prefetch(@options[:prefetch], false) if @options[:prefetch]
|
1078
|
+
end
|
876
1079
|
end
|
877
1080
|
|
878
1081
|
# @private
|
@@ -930,8 +1133,9 @@ module AMQP
|
|
930
1133
|
# Backwards compatibility with 0.6.x
|
931
1134
|
#
|
932
1135
|
|
933
|
-
# unique identifier
|
1136
|
+
# unique identifier of the default thread-local channel
|
934
1137
|
# @deprecated
|
1138
|
+
# @private
|
935
1139
|
def self.id
|
936
1140
|
Thread.current[:mq_id] ||= "#{`hostname`.strip}-#{Process.pid}-#{Thread.current.object_id}"
|
937
1141
|
end
|
@@ -956,6 +1160,7 @@ module AMQP
|
|
956
1160
|
|
957
1161
|
# @private
|
958
1162
|
def validate_parameters_match!(entity, parameters)
|
1163
|
+
parameters.delete(:no_declare)
|
959
1164
|
unless entity.opts == parameters || parameters[:passive]
|
960
1165
|
raise AMQP::IncompatibleOptionsError.new(entity.name, entity.opts, parameters)
|
961
1166
|
end
|