amqp 0.7.5 → 0.8.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -4
- data/.travis.yml +3 -5
- data/.yardopts +6 -0
- data/CHANGELOG +26 -7
- data/Gemfile +15 -7
- data/README.textile +216 -0
- data/Rakefile +0 -6
- data/amqp.gemspec +14 -4
- data/bin/jenkins.sh +27 -0
- data/bin/set_test_suite_realms_up.sh +16 -2
- data/docs/VendorSpecificExtensions.textile +32 -0
- data/examples/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +53 -0
- data/examples/hello_world.rb +29 -0
- data/examples/real-world/task-queue/README.textile +3 -0
- data/examples/real-world/task-queue/consumer.rb +27 -0
- data/examples/real-world/task-queue/producer.rb +22 -0
- data/examples/various/ack.rb +70 -0
- data/examples/various/automatic_binding_for_default_direct_exchange.rb +53 -0
- data/examples/various/basic_get.rb +65 -0
- data/examples/various/callbacks.rb +45 -0
- data/examples/various/clock.rb +74 -0
- data/examples/various/declare_a_queue_without_assignment.rb +46 -0
- data/examples/various/declare_an_exchange_without_assignment.rb +46 -0
- data/examples/various/hashtable.rb +60 -0
- data/examples/{logger.rb → various/logger.rb} +9 -7
- data/examples/{multiclock.rb → various/multiclock.rb} +15 -17
- data/examples/various/open_channel_without_assignment.rb +34 -0
- data/examples/various/pingpong.rb +53 -0
- data/examples/various/primes-simple.rb +29 -0
- data/examples/various/primes.rb +76 -0
- data/examples/various/pubsub.rb +43 -0
- data/examples/various/queue_status.rb +58 -0
- data/examples/various/stocks.rb +59 -0
- data/examples/various/weather_updates.rb +63 -0
- data/lib/amqp.rb +11 -2
- data/lib/amqp/basic_client.rb +23 -54
- data/lib/amqp/channel.rb +577 -805
- data/lib/amqp/client.rb +37 -275
- data/lib/amqp/connection.rb +165 -93
- data/lib/amqp/deprecated/fork.rb +15 -0
- data/lib/amqp/deprecated/logger.rb +99 -0
- data/lib/amqp/deprecated/mq.rb +20 -0
- data/lib/amqp/deprecated/rpc.rb +168 -0
- data/lib/amqp/exchange.rb +409 -281
- data/lib/amqp/extensions/rabbitmq.rb +1 -0
- data/lib/amqp/header.rb +41 -17
- data/lib/amqp/logger.rb +10 -84
- data/lib/amqp/queue.rb +457 -320
- data/lib/amqp/rpc.rb +11 -107
- data/lib/amqp/version.rb +1 -1
- data/lib/mq.rb +2 -1
- data/lib/mq/logger.rb +2 -0
- data/lib/mq/rpc.rb +2 -0
- data/spec/integration/authentication_spec.rb +36 -40
- data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +3 -5
- data/spec/integration/basic_get_spec.rb +91 -0
- data/spec/integration/channel_close_spec.rb +5 -5
- data/spec/integration/exchange_declaration_spec.rb +6 -53
- data/spec/integration/extensions/basic_return_spec.rb +47 -0
- data/spec/integration/queue_declaration_spec.rb +14 -17
- data/spec/integration/queue_exclusivity_spec.rb +49 -48
- data/spec/integration/reply_queue_communication_spec.rb +6 -4
- data/spec/integration/store_and_forward_spec.rb +9 -36
- data/spec/integration/topic_subscription_spec.rb +1 -1
- data/spec/integration/workload_distribution_spec.rb +1 -0
- data/spec/spec_helper.rb +69 -43
- data/spec/unit/amqp/connection_spec.rb +27 -23
- data/tasks.rb +11 -0
- metadata +124 -95
- data/README.md +0 -156
- data/TODO +0 -30
- data/amqp.pre.gemspec +0 -6
- data/examples/ack.rb +0 -47
- data/examples/automatic_binding_for_default_direct_exchange.rb +0 -65
- data/examples/callbacks.rb +0 -40
- data/examples/clock.rb +0 -65
- data/examples/default_channel.rb +0 -19
- data/examples/hashtable.rb +0 -61
- data/examples/immediately_bind_a_server_named_queue.rb +0 -38
- data/examples/internal.rb +0 -51
- data/examples/issues/issue_75.rb +0 -21
- data/examples/issues/issue_94.rb +0 -23
- data/examples/pingpong.rb +0 -54
- data/examples/pop.rb +0 -45
- data/examples/primes-simple.rb +0 -21
- data/examples/primes.rb +0 -101
- data/examples/simple.rb +0 -81
- data/examples/stocks.rb +0 -67
- data/gemfiles/eventmachine-pre +0 -24
- data/lib/amqp/buffer.rb +0 -272
- data/lib/amqp/collection.rb +0 -60
- data/lib/amqp/frame.rb +0 -68
- data/lib/amqp/protocol.rb +0 -163
- data/lib/amqp/server.rb +0 -101
- data/lib/amqp/spec.rb +0 -832
- data/protocol/amqp-0.8.json +0 -617
- data/protocol/amqp-0.8.xml +0 -3908
- data/protocol/codegen.rb +0 -175
- data/protocol/doc.txt +0 -281
- data/research/api.rb +0 -52
- data/research/primes-forked.rb +0 -65
- data/research/primes-processes.rb +0 -137
- data/research/primes-threaded.rb +0 -51
- data/spec/integration/queue_status_spec.rb +0 -44
- data/spec/unit/amqp/buffer_spec.rb +0 -178
- data/spec/unit/amqp/client_spec.rb +0 -102
- data/spec/unit/amqp/collection_spec.rb +0 -144
- data/spec/unit/amqp/frame_spec.rb +0 -60
- data/spec/unit/amqp/protocol_spec.rb +0 -51
@@ -0,0 +1,15 @@
|
|
1
|
+
require "amqp/ext/em"
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
# @deprecated
|
5
|
+
# @api public
|
6
|
+
def self.fork(workers)
|
7
|
+
EM.fork(workers) do
|
8
|
+
# clean up globals in the fork
|
9
|
+
Thread.current[:mq] = nil
|
10
|
+
AMQP.instance_variable_set('@conn', nil)
|
11
|
+
|
12
|
+
yield
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
class Logger
|
5
|
+
def initialize(*args, &block)
|
6
|
+
opts = args.pop if args.last.is_a? Hash
|
7
|
+
opts ||= {}
|
8
|
+
|
9
|
+
printer(block) if block
|
10
|
+
|
11
|
+
@prop = opts
|
12
|
+
@tags = ([:timestamp] + args).uniq
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :prop
|
16
|
+
alias :base :prop
|
17
|
+
|
18
|
+
def log(severity, *args)
|
19
|
+
opts = args.pop if args.last.is_a? Hash and args.size != 1
|
20
|
+
opts ||= {}
|
21
|
+
opts = @prop.clone.update(opts)
|
22
|
+
|
23
|
+
data = args.shift
|
24
|
+
|
25
|
+
data = {:type => :exception,
|
26
|
+
:name => data.class.to_s.intern,
|
27
|
+
:backtrace => data.backtrace,
|
28
|
+
:message => data.message} if data.is_a? Exception
|
29
|
+
|
30
|
+
(@tags + args).each do |tag|
|
31
|
+
tag = tag.to_sym
|
32
|
+
case tag
|
33
|
+
when :timestamp
|
34
|
+
opts.update :timestamp => Time.now
|
35
|
+
when :hostname
|
36
|
+
@hostname ||= { :hostname => `hostname`.strip }
|
37
|
+
opts.update @hostname
|
38
|
+
when :process
|
39
|
+
@process_id ||= { :process_id => Process.pid,
|
40
|
+
:process_name => $0,
|
41
|
+
:process_parent_id => Process.ppid,
|
42
|
+
:thread_id => Thread.current.object_id }
|
43
|
+
opts.update :process => @process_id
|
44
|
+
else
|
45
|
+
(opts[:tags] ||= []) << tag
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.update(:severity => severity,
|
50
|
+
:msg => data)
|
51
|
+
|
52
|
+
print(opts)
|
53
|
+
unless Logger.disabled?
|
54
|
+
AMQP::Channel.new.fanout('logging', :durable => true).publish Marshal.dump(opts)
|
55
|
+
end
|
56
|
+
|
57
|
+
opts
|
58
|
+
end
|
59
|
+
alias :method_missing :log
|
60
|
+
|
61
|
+
def print(data = nil, &block)
|
62
|
+
if block
|
63
|
+
@printer = block
|
64
|
+
elsif data.is_a? Proc
|
65
|
+
@printer = data
|
66
|
+
elsif data
|
67
|
+
(pr = @printer || self.class.printer) and pr.call(data)
|
68
|
+
else
|
69
|
+
@printer
|
70
|
+
end
|
71
|
+
end
|
72
|
+
alias :printer :print
|
73
|
+
|
74
|
+
def self.printer &block
|
75
|
+
@printer = block if block
|
76
|
+
@printer
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.disabled?
|
80
|
+
!!@disabled
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.enable
|
84
|
+
@disabled = false
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.disable
|
88
|
+
@disabled = true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
require "amqp/deprecated/mq"
|
95
|
+
class MQ
|
96
|
+
# @note This class will be removed before 1.0 release.
|
97
|
+
# @deprecated
|
98
|
+
class Logger < ::AMQP::Logger; end
|
99
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Alias for AMQP::Channel.
|
2
|
+
#
|
3
|
+
# @note This class will be removed before 1.0 release.
|
4
|
+
# @deprecated
|
5
|
+
class MQ < AMQP::Channel; end
|
6
|
+
|
7
|
+
|
8
|
+
class MQ
|
9
|
+
# Alias for AMQP::Exchange.
|
10
|
+
#
|
11
|
+
# @note This class will be removed before 1.0 release.
|
12
|
+
# @deprecated
|
13
|
+
class Exchange < ::AMQP::Exchange; end
|
14
|
+
|
15
|
+
# Alias for AMQP::Queue.
|
16
|
+
#
|
17
|
+
# @note This class will be removed before 1.0 release.
|
18
|
+
# @deprecated
|
19
|
+
class Queue < ::AMQP::Queue; end
|
20
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
if defined?(BasicObject)
|
5
|
+
# @private
|
6
|
+
class BlankSlate < BasicObject; end
|
7
|
+
else
|
8
|
+
# @private
|
9
|
+
class BlankSlate
|
10
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__/ }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# Basic RPC (remote procedure call) facility.
|
16
|
+
#
|
17
|
+
# Needs more detail and explanation.
|
18
|
+
#
|
19
|
+
# EM.run do
|
20
|
+
# server = AMQP::Channel.new.rpc('hash table node', Hash)
|
21
|
+
#
|
22
|
+
# client = AMQP::Channel.new.rpc('hash table node')
|
23
|
+
# client[:now] = Time.now
|
24
|
+
# client[:one] = 1
|
25
|
+
#
|
26
|
+
# client.values do |res|
|
27
|
+
# p 'client', :values => res
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# client.keys do |res|
|
31
|
+
# p 'client', :keys => res
|
32
|
+
# EM.stop_event_loop
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
#
|
37
|
+
# @note This class will be removed before 1.0 release.
|
38
|
+
# @deprecated
|
39
|
+
class RPC < ::AMQP::BlankSlate
|
40
|
+
|
41
|
+
#
|
42
|
+
# API
|
43
|
+
#
|
44
|
+
|
45
|
+
|
46
|
+
attr_reader :name
|
47
|
+
|
48
|
+
# Takes a channel, queue and optional object.
|
49
|
+
#
|
50
|
+
# The optional object may be a class name, module name or object
|
51
|
+
# instance. When given a class or module name, the object is instantiated
|
52
|
+
# during this setup. The passed queue is automatically subscribed to so
|
53
|
+
# it passes all messages (and their arguments) to the object.
|
54
|
+
#
|
55
|
+
# Marshalling and unmarshalling the objects is handled internally. This
|
56
|
+
# marshalling is subject to the same restrictions as defined in the
|
57
|
+
# {http://ruby-doc.org/core/classes/Marshal.html Marshal} standard
|
58
|
+
# library. See that documentation for further reference.
|
59
|
+
#
|
60
|
+
# When the optional object is not passed, the returned rpc reference is
|
61
|
+
# used to send messages and arguments to the queue. See #method_missing
|
62
|
+
# which does all of the heavy lifting with the proxy. Some client
|
63
|
+
# elsewhere must call this method *with* the optional block so that
|
64
|
+
# there is a valid destination. Failure to do so will just enqueue
|
65
|
+
# marshalled messages that are never consumed.
|
66
|
+
#
|
67
|
+
def initialize(channel, queue, obj = nil)
|
68
|
+
@name = queue
|
69
|
+
@channel = channel
|
70
|
+
@channel.register_rpc(self)
|
71
|
+
|
72
|
+
if @obj = normalize(obj)
|
73
|
+
@delegate = Server.new(channel, queue, @obj)
|
74
|
+
else
|
75
|
+
@delegate = Client.new(channel, queue)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def client?
|
81
|
+
@obj.nil?
|
82
|
+
end
|
83
|
+
|
84
|
+
def server?
|
85
|
+
!client?
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def method_missing(selector, *args, &block)
|
90
|
+
@delegate.__send__(selector, *args, &block)
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# @private
|
95
|
+
class Client
|
96
|
+
attr_accessor :identifier
|
97
|
+
|
98
|
+
def initialize(channel, server_queue_name)
|
99
|
+
@channel = channel
|
100
|
+
@exchange = AMQP::Exchange.default(@channel)
|
101
|
+
@server_queue_name = server_queue_name
|
102
|
+
|
103
|
+
@handlers = Hash.new
|
104
|
+
@queue = channel.queue("__amqp_gem_rpc_client_#{rand(1_000_000)}", :auto_delete => true)
|
105
|
+
|
106
|
+
@queue.subscribe do |header, payload|
|
107
|
+
*response_args = Marshal.load(payload)
|
108
|
+
handler = @handlers[header.message_id]
|
109
|
+
|
110
|
+
handler.call(*response_args)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def method_missing(selector, *args, &block)
|
115
|
+
@channel.once_open do
|
116
|
+
message_id = "message_identifier_#{rand(1_000_000)}"
|
117
|
+
|
118
|
+
if block
|
119
|
+
@handlers[message_id] = block
|
120
|
+
@exchange.publish(Marshal.dump([selector, *args]), :routing_key => @server_queue_name, :reply_to => @queue.name, :message_id => message_id)
|
121
|
+
else
|
122
|
+
@exchange.publish(Marshal.dump([selector, *args]), :routing_key => @server_queue_name, :message_id => message_id)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end # Client
|
127
|
+
|
128
|
+
# @private
|
129
|
+
class Server
|
130
|
+
def initialize(channel, queue_name, impl)
|
131
|
+
@channel = channel
|
132
|
+
@exchange = AMQP::Exchange.default(@channel)
|
133
|
+
@queue = @channel.queue(queue_name)
|
134
|
+
@impl = impl
|
135
|
+
|
136
|
+
@handlers = Hash.new
|
137
|
+
@id = "client_identifier_#{rand(1_000_000)}"
|
138
|
+
|
139
|
+
@queue.subscribe(:ack => true) do |header, payload|
|
140
|
+
selector, *args = Marshal.load(payload)
|
141
|
+
result = @impl.__send__(selector, *args)
|
142
|
+
|
143
|
+
respond_to(header, result) if header.to_hash[:reply_to]
|
144
|
+
header.ack
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def respond_to(header, result)
|
149
|
+
@exchange.publish(Marshal.dump(result), :message_id => header.message_id, :routing_key => header.reply_to)
|
150
|
+
end
|
151
|
+
end # Server
|
152
|
+
|
153
|
+
|
154
|
+
|
155
|
+
protected
|
156
|
+
|
157
|
+
def normalize(input)
|
158
|
+
case input
|
159
|
+
when ::Class
|
160
|
+
input.new
|
161
|
+
when ::Module
|
162
|
+
(::Class.new do include(obj) end).new
|
163
|
+
else
|
164
|
+
input
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end # RPC
|
168
|
+
end # AMQP
|
data/lib/amqp/exchange.rb
CHANGED
@@ -1,13 +1,43 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require "amq/client/exchange"
|
4
|
+
|
3
5
|
module AMQP
|
4
|
-
|
6
|
+
|
7
|
+
# h2. What are AMQP exchanges?
|
8
|
+
#
|
9
|
+
# AMQP exchange is where AMQP clients send messages. AMQP
|
5
10
|
# exchange may also be described as a router or a matcher. Every
|
6
11
|
# published message is received by an exchange which, depending on its
|
7
|
-
# type
|
12
|
+
# type and message attributes, determines how to deliver the message.
|
13
|
+
#
|
14
|
+
#
|
15
|
+
# h2. Exchange types
|
16
|
+
#
|
17
|
+
# There are 4 supported exchange types: direct, fanout, topic and headers.
|
18
|
+
#
|
19
|
+
# As part of the standard, the server _must_ predeclare the direct exchange
|
20
|
+
# 'amq.direct' and the fanout exchange 'amq.fanout'. All exchange names
|
21
|
+
# starting with 'amq.' are reserved: attempts to declare an exchange using
|
22
|
+
# 'amq.' as the name will raise an AMQP::Error and fail.
|
8
23
|
#
|
9
|
-
#
|
10
|
-
#
|
24
|
+
# Note that durability of exchanges and durability of messages published to exchanges
|
25
|
+
# are different concepts. Sending messages to durable exchanges does not make
|
26
|
+
# messages themselves persistent.
|
27
|
+
#
|
28
|
+
#
|
29
|
+
# h2. AMQP bindings
|
30
|
+
#
|
31
|
+
# Closely related to exchange is a concept of bindings. A binding is
|
32
|
+
# the relationship between an exchange and a message queue that tells
|
33
|
+
# the exchange how to route messages. Bindings are set up by
|
34
|
+
# AMQP applications (usually the app owning and using the message queue
|
35
|
+
# sets up bindings for it). Exchange may be bound to none, 1 or more than 1
|
36
|
+
# queue.
|
37
|
+
#
|
38
|
+
#
|
39
|
+
# Defines, intializes and returns an Exchange to act as an ingress
|
40
|
+
# point for all published messages.
|
11
41
|
#
|
12
42
|
# There are three (3) supported Exchange types: direct, fanout and topic.
|
13
43
|
#
|
@@ -17,222 +47,292 @@ module AMQP
|
|
17
47
|
# 'amq.' as the name will raise an AMQP::Error and fail. In practice these
|
18
48
|
# default exchanges are never used directly by client code.
|
19
49
|
#
|
20
|
-
# These predececlared exchanges are used when the client code declares
|
21
|
-
# an exchange without a name. In these cases the library will use
|
22
|
-
# the default exchange for publishing the messages.
|
23
50
|
#
|
24
|
-
|
51
|
+
# h2. Direct exchanges
|
52
|
+
#
|
53
|
+
# Direct exchanges are useful for 1:1 communication scenarios.
|
54
|
+
# Queues are bound to direct exchanges with a parameter called "routing key". When messages
|
55
|
+
# arrive to a direct exchange, broker takes that message's routing key (if any), finds a queue bound
|
56
|
+
# to the exchange with the same routing key and routes message there.
|
57
|
+
#
|
58
|
+
# Because very often queues are bound with the same routing key as queue's name, AMQP 0.9.1 has
|
59
|
+
# a pre-declared direct exchange known as default exchange. Default exchange is a bit special: broker
|
60
|
+
# automatically binds all the queues (in the same virtual host) to it with routing key equal to
|
61
|
+
# queue names. In other words, messages delivered to default exchange are routed to queues when
|
62
|
+
# message routing key equals queue name. Default exchange name is an empty string.
|
63
|
+
#
|
64
|
+
#
|
65
|
+
#
|
66
|
+
# h2. Fanout exchanges
|
67
|
+
#
|
68
|
+
# Fanout exchanges are useful for 1:n and n:m communication where one or more producer
|
69
|
+
# feeds multiple consumers. messages published
|
70
|
+
# to a fanout exchange are delivered to queues that are bound to that exchange name (unconditionally).
|
71
|
+
# Each queue gets it's own copy of the message.
|
72
|
+
#
|
73
|
+
#
|
74
|
+
#
|
75
|
+
# h2. Topic exchanges
|
76
|
+
#
|
77
|
+
# Topic exchanges are used for 1:n and n:m communication scenarios.
|
78
|
+
# Exchange of this type uses the routing key
|
79
|
+
# to determine which queues to deliver the message. Wildcard matching
|
80
|
+
# is allowed. The topic must be declared using dot notation to separate
|
81
|
+
# each subtopic.
|
82
|
+
#
|
83
|
+
# As part of the AMQP standard, each server _should_ predeclare a topic
|
84
|
+
# exchange called 'amq.topic'.
|
85
|
+
#
|
86
|
+
# The classic example is delivering market data. When publishing market
|
87
|
+
# data for stocks, we may subdivide the stream based on 2
|
88
|
+
# characteristics: nation code and trading symbol. The topic tree for
|
89
|
+
# Apple may look like stock.us.aapl. NASDAQ updates may use topic stocks.us.nasdaq,
|
90
|
+
# while DAX may use stock.de.dax.
|
91
|
+
#
|
92
|
+
# When publishing data to the exchange, bound queues subscribing to the
|
93
|
+
# exchange indicate which data interests them by passing a routing key
|
94
|
+
# for matching against the published routing key.
|
95
|
+
#
|
96
|
+
#
|
97
|
+
# h2. Headers exchanges
|
98
|
+
#
|
99
|
+
# As part of the AMQP standard, each server _should_ predeclare a headers
|
100
|
+
# exchange named 'amq.match'.
|
101
|
+
#
|
102
|
+
# When publishing data to the exchange, bound queues subscribing to the
|
103
|
+
# exchange indicate which data interests them by passing arguments
|
104
|
+
# for matching against the headers in published messages. The
|
105
|
+
# form of the matching can be controlled by the 'x-match' argument, which
|
106
|
+
# may be 'any' or 'all'. If unspecified, it defaults to "all".
|
107
|
+
#
|
108
|
+
# A value of 'all' for 'x-match' implies that all values must match (i.e.
|
109
|
+
# it does an AND of the headers ), while a value of 'any' implies that
|
110
|
+
# at least one should match (ie. it does an OR).
|
111
|
+
#
|
112
|
+
#
|
113
|
+
# h2. Exchange durability and persistence of messages.
|
114
|
+
#
|
115
|
+
# AMQP separates concept of durability of entities (queues, exchanges) from messages persistence.
|
116
|
+
# Exchanges can be durable or transient. Durable exchanges survive broker restart, transient exchanges don't (they
|
117
|
+
# have to be redeclared when broker comes back online). Not all scenarios and use cases mandate exchanges to be
|
118
|
+
# durable.
|
119
|
+
#
|
120
|
+
# The concept of messages persistence is separate: messages may be published as persistent. That makes
|
121
|
+
# AMQP broker persist them to disk. If the server is restarted, the system ensures that received persistent messages
|
122
|
+
# are not lost. Simply publishing message to a durable exchange or the fact that queue(s) they are routed to
|
123
|
+
# is durable doesn't make messages persistent: it all depends on persistence mode of the messages itself.
|
124
|
+
# Publishing messages as persistent affects performance (just like with data stores, durability comes at a certain cost
|
125
|
+
# in performance and vise versa). Pass :persistent => true to {Exchange#publish} to publish your message as persistent.
|
126
|
+
#
|
127
|
+
# Note that *only durable queues can be bound to durable exchanges*.
|
128
|
+
#
|
129
|
+
#
|
130
|
+
# h2. RabbitMQ extensions.
|
131
|
+
#
|
132
|
+
# AMQP gem supports several RabbitMQ extensions taht extend Exchange functionality.
|
133
|
+
# Learn more in {file:docs/VendorSpecificExtensions.textile}
|
134
|
+
#
|
135
|
+
#
|
136
|
+
# h2. Key methods
|
137
|
+
#
|
138
|
+
# Key methods of Exchange class are
|
139
|
+
#
|
140
|
+
# * {Exchange#publish}
|
141
|
+
# * {Exchange#delete}
|
142
|
+
# * {Exchange.default}
|
143
|
+
#
|
144
|
+
#
|
145
|
+
# @note Please make sure you read a section on exchanges durability vs. messages
|
146
|
+
# persistence.
|
147
|
+
#
|
148
|
+
# @see http://www.rabbitmq.com/faq.html#managing-concepts-exchanges Exchanges explained in the RabbitMQ FAQ
|
149
|
+
# @see http://www.rabbitmq.com/faq.html#Binding-and-Routing Bindings and routing explained in the RabbitMQ FAQ
|
150
|
+
# @see Channel#default_exchange
|
151
|
+
# @see Channel#direct
|
152
|
+
# @see Channel#fanout
|
153
|
+
# @see Channel#topic
|
154
|
+
# @see Channel#headers
|
155
|
+
# @see Queue
|
156
|
+
# @see http://bit.ly/hw2ELX AMQP 0.9.1 specification (Section 2.1.1)
|
157
|
+
# @see http://bit.ly/hw2ELX AMQP 0.9.1 specification (Section 2.1.5)
|
158
|
+
# @see http://bit.ly/hw2ELX AMQP 0.9.1 specification (Section 3.1.3)
|
159
|
+
class Exchange < AMQ::Client::Exchange
|
25
160
|
|
26
161
|
#
|
27
162
|
# API
|
28
163
|
#
|
29
164
|
|
165
|
+
DEFAULT_CONTENT_TYPE = "application/octet-stream".freeze
|
30
166
|
|
31
|
-
# The default exchange.
|
32
|
-
# Every queue is bind to this (direct) exchange by default.
|
33
|
-
# You can't remove it or bind there queue explicitly.
|
34
|
-
|
35
|
-
# Do NOT confuse with amq.direct: it's only a normal direct
|
36
|
-
# exchange and the only special thing about it is that it's
|
37
|
-
# predefined in the system, so you can use it straightaway.
|
38
167
|
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
|
43
|
-
#
|
168
|
+
# The default exchange. Default exchange is a direct exchange that is predefined.
|
169
|
+
# It cannot be removed. Every queue is bind to this (direct) exchange by default with
|
170
|
+
# the following routing semantics: messages will be routed to the queue withe same
|
171
|
+
# same name as message's routing key. In other words, if a message is published with
|
172
|
+
# a routing key of "weather.usa.ca.sandiego" and there is a queue Q with this name,
|
173
|
+
# that message will be routed to Q.
|
174
|
+
#
|
175
|
+
# @param [AMQP::Channel] channel Channel to use. If not given, new AMQP channel
|
176
|
+
# will be opened on the default AMQP connection (accessible as AMQP.connection).
|
177
|
+
#
|
178
|
+
# @example Publishing a messages to the tasks queue
|
179
|
+
# channel = AMQP::Channel.new(connection)
|
180
|
+
# tasks_queue = channel.queue("tasks")
|
181
|
+
# AMQP::Exchange.default(channel).publish("make clean", routing_key => "tasks")
|
182
|
+
#
|
183
|
+
# @see Exchange
|
184
|
+
# @see http://bit.ly/hw2ELX AMQP 0.9.1 specification (Section 2.1.2.4)
|
185
|
+
# @note Do not confuse default exchange with amq.direct: amq.direct is a pre-defined direct
|
186
|
+
# exchange that doesn't have any special routing semantics.
|
187
|
+
# @return [Exchange] An instance that corresponds to the default exchange (of type direct).
|
188
|
+
# @api public
|
44
189
|
def self.default(channel = nil)
|
45
|
-
self.new(channel || AMQP::Channel.new, :direct,
|
190
|
+
self.new(channel || AMQP::Channel.new, :direct, AMQ::Protocol::EMPTY_STRING, :no_declare => true)
|
46
191
|
end
|
47
192
|
|
48
|
-
|
49
|
-
|
193
|
+
|
194
|
+
# @return [String]
|
195
|
+
attr_reader :name
|
196
|
+
|
197
|
+
# Type of this exchange (one of: :direct, :fanout, :topic, :headers).
|
198
|
+
# @return [Symbol]
|
199
|
+
attr_reader :type
|
200
|
+
|
201
|
+
# @return [Symbol]
|
202
|
+
# @api plugin
|
203
|
+
attr_reader :status
|
204
|
+
|
205
|
+
# Options hash this exchange instance was instantiated with
|
206
|
+
# @return [Hash]
|
207
|
+
attr_accessor :opts
|
208
|
+
|
209
|
+
# @return [#call] A callback that is executed once declaration notification (exchange.declare-ok)
|
210
|
+
# from the broker arrives.
|
211
|
+
attr_accessor :on_declare
|
212
|
+
|
213
|
+
# @return [String]
|
214
|
+
attr_reader :default_routing_key
|
215
|
+
alias key default_routing_key
|
216
|
+
|
217
|
+
# Compatibility alias for #on_declare.
|
218
|
+
#
|
219
|
+
# @api public
|
220
|
+
# @deprecated
|
221
|
+
# @return [#call]
|
222
|
+
def callback
|
223
|
+
@on_declare
|
50
224
|
end
|
51
225
|
|
52
|
-
|
53
|
-
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# As part of the standard, the server _must_ predeclare the direct exchange
|
58
|
-
# 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
|
59
|
-
# starting with 'amq.' are reserved). Attempts to declare an exchange using
|
60
|
-
# 'amq.' as the name will raise an AMQP::Error and fail. In practice these
|
61
|
-
# default exchanges are never used directly by client code.
|
62
|
-
#
|
63
|
-
# == Direct
|
64
|
-
# A direct exchange is useful for 1:1 communication between a publisher and
|
65
|
-
# subscriber. Messages are routed to the queue with a binding that shares
|
66
|
-
# the same name as the exchange. Alternately, the messages are routed to
|
67
|
-
# the bound queue that shares the same name as the routing key used for
|
68
|
-
# defining the exchange. This exchange type does not honor the :key option
|
69
|
-
# when defining a new instance with a name. It _will_ honor the :key option
|
70
|
-
# if the exchange name is the empty string. This is because an exchange
|
71
|
-
# defined with the empty string uses the default pre-declared exchange
|
72
|
-
# called 'amq.direct'. In this case it needs to use :key to do its matching.
|
73
|
-
#
|
74
|
-
# # exchange is named 'foo'
|
75
|
-
# exchange = AMQP::Channel::Exchange.new(AMQP::Channel.new, :direct, 'foo')
|
76
|
-
#
|
77
|
-
# # or, the exchange can use the default name (amq.direct) and perform
|
78
|
-
# # routing comparisons using the :key
|
79
|
-
# exchange = AMQP::Channel::Exchange.new(AMQP::Channel.new, :direct, "", :key => 'foo')
|
80
|
-
# exchange.publish('some data') # will be delivered to queue bound to 'foo'
|
81
|
-
#
|
82
|
-
# queue = AMQP::Channel::Queue.new(AMQP::Channel.new, 'foo')
|
83
|
-
# # can receive data since the queue name and the exchange key match exactly
|
84
|
-
# queue.pop { |data| puts "received data [#{data}]" }
|
85
|
-
#
|
86
|
-
# == Fanout
|
87
|
-
# A fanout exchange is useful for 1:N communication where one publisher
|
88
|
-
# feeds multiple subscribers. Like direct exchanges, messages published
|
89
|
-
# to a fanout exchange are delivered to queues whose name matches the
|
90
|
-
# exchange name (or are bound to that exchange name). Each queue gets
|
91
|
-
# its own copy of the message.
|
92
|
-
#
|
93
|
-
# Like the direct exchange type, this exchange type does not honor the
|
94
|
-
# :key option when defining a new instance with a name. It _will_ honor
|
95
|
-
# the :key option if the exchange name is the empty string. Fanout exchanges
|
96
|
-
# defined with the empty string as the name use the default 'amq.fanout'.
|
97
|
-
# In this case it needs to use :key to do its matching.
|
98
|
-
#
|
99
|
-
# EM.run do
|
100
|
-
# clock = AMQP::Channel::Exchange.new(AMQP::Channel.new, :fanout, 'clock')
|
101
|
-
# EM.add_periodic_timer(1) do
|
102
|
-
# puts "\npublishing #{time = Time.now}"
|
103
|
-
# clock.publish(Marshal.dump(time))
|
104
|
-
# end
|
105
|
-
#
|
106
|
-
# # one way of defining a queue
|
107
|
-
# amq = AMQP::Channel::Queue.new(AMQP::Channel.new, 'every second')
|
108
|
-
# amq.bind(AMQP::Channel.fanout('clock')).subscribe do |time|
|
109
|
-
# puts "every second received #{Marshal.load(time)}"
|
110
|
-
# end
|
111
|
-
#
|
112
|
-
# # defining a queue using the convenience method
|
113
|
-
# # note the string passed to #bind
|
114
|
-
# AMQP::Channel.queue('every 5 seconds').bind('clock').subscribe do |time|
|
115
|
-
# time = Marshal.load(time)
|
116
|
-
# puts "every 5 seconds received #{time}" if time.strftime('%S').to_i%5 == 0
|
117
|
-
# end
|
118
|
-
# end
|
119
|
-
#
|
120
|
-
# == Topic
|
121
|
-
# A topic exchange allows for messages to be published to an exchange
|
122
|
-
# tagged with a specific routing key. The Exchange uses the routing key
|
123
|
-
# to determine which queues to deliver the message. Wildcard matching
|
124
|
-
# is allowed. The topic must be declared using dot notation to separate
|
125
|
-
# each subtopic.
|
126
|
-
#
|
127
|
-
# This is the only exchange type to honor the :key parameter.
|
128
|
-
#
|
129
|
-
# As part of the AMQP standard, each server _should_ predeclare a topic
|
130
|
-
# exchange called 'amq.topic' (this is not required by the standard).
|
131
|
-
#
|
132
|
-
# The classic example is delivering market data. When publishing market
|
133
|
-
# data for stocks, we may subdivide the stream based on 2
|
134
|
-
# characteristics: nation code and trading symbol. The topic tree for
|
135
|
-
# Apple Computer would look like:
|
136
|
-
# 'stock.us.aapl'
|
137
|
-
# For a foreign stock, it may look like:
|
138
|
-
# 'stock.de.dax'
|
139
|
-
#
|
140
|
-
# When publishing data to the exchange, bound queues subscribing to the
|
141
|
-
# exchange indicate which data interests them by passing a routing key
|
142
|
-
# for matching against the published routing key.
|
143
|
-
#
|
144
|
-
# EM.run do
|
145
|
-
# exch = AMQP::Channel::Exchange.new(AMQP::Channel.new, :topic, "stocks")
|
146
|
-
# keys = ['stock.us.aapl', 'stock.de.dax']
|
147
|
-
#
|
148
|
-
# EM.add_periodic_timer(1) do # every second
|
149
|
-
# puts
|
150
|
-
# exch.publish(10+rand(10), :routing_key => keys[rand(2)])
|
151
|
-
# end
|
152
|
-
#
|
153
|
-
# # match against one dot-separated item
|
154
|
-
# AMQP::Channel.queue('us stocks').bind(exch, :key => 'stock.us.*').subscribe do |price|
|
155
|
-
# puts "us stock price [#{price}]"
|
156
|
-
# end
|
157
|
-
#
|
158
|
-
# # match against multiple dot-separated items
|
159
|
-
# AMQP::Channel.queue('all stocks').bind(exch, :key => 'stock.#').subscribe do |price|
|
160
|
-
# puts "all stocks: price [#{price}]"
|
161
|
-
# end
|
162
|
-
#
|
163
|
-
# # require exact match
|
164
|
-
# AMQP::Channel.queue('only dax').bind(exch, :key => 'stock.de.dax').subscribe do |price|
|
165
|
-
# puts "dax price [#{price}]"
|
166
|
-
# end
|
167
|
-
# end
|
168
|
-
#
|
169
|
-
# For matching, the '*' (asterisk) wildcard matches against one
|
170
|
-
# dot-separated item only. The '#' wildcard (hash or pound symbol)
|
171
|
-
# matches against 0 or more dot-separated items. If none of these
|
172
|
-
# symbols are used, the exchange performs a comparison looking for an
|
173
|
-
# exact match.
|
226
|
+
|
227
|
+
|
228
|
+
# See {Exchange Exchange class documentation} for introduction, information about exchange types,
|
229
|
+
# what uses cases they are good for and so on.
|
174
230
|
#
|
175
|
-
#
|
176
|
-
# * :passive => true | false (default false)
|
177
|
-
# If set, the server will not create the exchange if it does not
|
178
|
-
# already exist. The client can use this to check whether an exchange
|
179
|
-
# exists without modifying the server state.
|
180
|
-
#
|
181
|
-
# * :durable => true | false (default false)
|
182
|
-
# If set when creating a new exchange, the exchange will be marked as
|
183
|
-
# durable. Durable exchanges remain active when a server restarts.
|
184
|
-
# Non-durable exchanges (transient exchanges) are purged if/when a
|
185
|
-
# server restarts.
|
186
|
-
#
|
187
|
-
# A transient exchange (the default) is stored in memory-only
|
188
|
-
# therefore it is a good choice for high-performance and low-latency
|
189
|
-
# message publishing.
|
190
|
-
#
|
191
|
-
# Durable exchanges cause all messages to be written to non-volatile
|
192
|
-
# backing store (i.e. disk) prior to routing to any bound queues.
|
193
|
-
#
|
194
|
-
# * :auto_delete => true | false (default false)
|
195
|
-
# If set, the exchange is deleted when all queues have finished
|
196
|
-
# using it. The server waits for a short period of time before
|
197
|
-
# determining the exchange is unused to give time to the client code
|
198
|
-
# to bind a queue to it.
|
199
|
-
#
|
200
|
-
# If the exchange has been previously declared, this option is ignored
|
201
|
-
# on subsequent declarations.
|
202
|
-
#
|
203
|
-
# * :internal => true | false (default false)
|
204
|
-
# If set, the exchange may not be used directly by publishers, but
|
205
|
-
# only when bound to other exchanges. Internal exchanges are used to
|
206
|
-
# construct wiring that is not visible to applications.
|
231
|
+
# h2. Predeclared exchanges
|
207
232
|
#
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
233
|
+
# If exchange name corresponds to one of those predeclared by AMQP 0.9.1 specification (empty string, amq.direct, amq.fanout, amq.topic, amq.match),
|
234
|
+
# declaration command won't be sent to the broker (because the only possible reply from the broker is to reject it, predefined entities cannot be changed).
|
235
|
+
# Callback, if any, will be executed immediately.
|
236
|
+
#
|
237
|
+
#
|
238
|
+
#
|
239
|
+
# @example Instantiating a fanout exchange using constructor
|
240
|
+
#
|
241
|
+
# AMQP.connect do |connection|
|
242
|
+
# AMQP::Channel.new(connection) do |channel|
|
243
|
+
# AMQP::Exchange.new(channel, :fanout, "search.index.updates") do |exchange, declare_ok|
|
244
|
+
# # by now exchange is ready and waiting
|
245
|
+
# end
|
246
|
+
# end
|
247
|
+
# end
|
248
|
+
#
|
249
|
+
#
|
250
|
+
# @example Instantiating a direct exchange using {Channel#direct}
|
251
|
+
#
|
252
|
+
# AMQP.connect do |connection|
|
253
|
+
# AMQP::Channel.new(connection) do |channel|
|
254
|
+
# channel.direct("email.replies_listener") do |exchange, declare_ok|
|
255
|
+
# # by now exchange is ready and waiting
|
256
|
+
# end
|
257
|
+
# end
|
258
|
+
# end
|
259
|
+
#
|
260
|
+
#
|
261
|
+
# @param [Channel] channel AMQP channel this exchange is associated with
|
262
|
+
# @param [Symbol] type Exchange type
|
263
|
+
# @param [String] name Exchange name
|
212
264
|
#
|
213
|
-
#
|
214
|
-
# If set, the
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
265
|
+
#
|
266
|
+
# @option opts [Boolean] :passive (false) If set, the server will not create the exchange if it does not
|
267
|
+
# already exist. The client can use this to check whether an exchange
|
268
|
+
# exists without modifying the server state.
|
269
|
+
#
|
270
|
+
# @option opts [Boolean] :durable (false) If set when creating a new exchange, the exchange will be marked as
|
271
|
+
# durable. Durable exchanges and their bindings are recreated upon a server
|
272
|
+
# restart (information about them is persisted). Non-durable (transient) exchanges
|
273
|
+
# do not survive if/when a server restarts (information about them is stored exclusively
|
274
|
+
# in RAM).
|
275
|
+
#
|
276
|
+
#
|
277
|
+
# @option opts [Boolean] :auto_delete (false) If set, the exchange is deleted when all queues have finished
|
278
|
+
# using it. The server waits for a short period of time before
|
279
|
+
# determining the exchange is unused to give time to the client code
|
280
|
+
# to bind a queue to it.
|
281
|
+
#
|
282
|
+
# @option opts [Boolean] :internal (false) If set, the exchange may not be used directly by publishers, but
|
283
|
+
# only when bound to other exchanges. Internal exchanges are used to
|
284
|
+
# construct wiring that is not visible to applications. *This is a RabbitMQ-specific
|
285
|
+
# extension.*
|
286
|
+
#
|
287
|
+
# @option opts [Boolean] :nowait (true) If set, the server will not respond to the method. The client should
|
288
|
+
# not wait for a reply method. If the server could not complete the
|
289
|
+
# method it will raise a channel or connection exception.
|
290
|
+
#
|
291
|
+
# @option opts [Boolean] :no_declare (true) If set, exchange declaration command won't be sent to the broker. Allows to forcefully
|
292
|
+
# avoid declaration. We recommend that only experienced developers consider this option.
|
293
|
+
#
|
294
|
+
# @option opts [String] :default_routing_key (nil) Default routing key that will be used by {Exchange#publish} when no routing key is not passed explicitly.
|
295
|
+
# It is perfectly fine for applications to always specify routing key to {Exchange#publish}.
|
296
|
+
#
|
297
|
+
#
|
298
|
+
# @raise [AMQP::Error] Raised when exchange is redeclared with parameters different from original declaration.
|
299
|
+
# @raise [AMQP::Error] Raised when exchange is declared with :passive => true and the exchange does not exist.
|
300
|
+
#
|
301
|
+
# @yield [exchange, declare_ok] Yields successfully declared exchange instance and AMQP method (exchange.declare-ok) instance. The latter is optional.
|
302
|
+
# @yieldparam [Exchange] exchange Exchange that is successfully declared and is ready to be used.
|
303
|
+
# @yieldparam [AMQP::Protocol::Exchange::DeclareOk] declare_ok AMQP exchange.declare-ok) instance.
|
304
|
+
#
|
305
|
+
# @see Channel#default_exchange
|
306
|
+
# @see Channel#direct
|
307
|
+
# @see Channel#fanout
|
308
|
+
# @see Channel#topic
|
309
|
+
# @see Channel#headers
|
310
|
+
# @see Queue
|
311
|
+
# @see http://bit.ly/hw2ELX AMQP 0.9.1 specification (Section 3.1.3)
|
312
|
+
#
|
313
|
+
# @return [Exchange]
|
314
|
+
# @api public
|
315
|
+
def initialize(channel, type, name, opts = {}, &block)
|
316
|
+
@channel = channel
|
317
|
+
@type = type
|
318
|
+
@opts = self.class.add_default_options(type, name, opts, block)
|
319
|
+
@default_routing_key = opts[:routing_key] || opts[:key]
|
320
|
+
@name = name unless name.empty?
|
321
|
+
|
322
|
+
@status = :unknown
|
323
|
+
@default_publish_options = (opts.delete(:default_publish_options) || {
|
324
|
+
:routing_key => AMQ::Protocol::EMPTY_STRING,
|
325
|
+
:mandatory => false,
|
326
|
+
:immediate => false
|
327
|
+
}).freeze
|
328
|
+
|
329
|
+
@default_headers = (opts.delete(:default_headers) || {
|
330
|
+
:content_type => DEFAULT_CONTENT_TYPE,
|
331
|
+
:persistent => false,
|
332
|
+
:priority => 0
|
333
|
+
}).freeze
|
334
|
+
|
335
|
+
super(channel.connection, channel, name, type)
|
236
336
|
|
237
337
|
# The AMQP 0.8 specification (as well as 0.9.1) in 1.1.4.2 mentiones
|
238
338
|
# that Exchange.Declare-Ok confirms the name of the exchange (because
|
@@ -244,98 +344,118 @@ module AMQP
|
|
244
344
|
# or nameless exchange), so if we'd send Exchange.Declare(exchange=""),
|
245
345
|
# then RabbitMQ interpret it as if we'd try to redefine this default
|
246
346
|
# exchange so it'd produce an error.
|
247
|
-
unless name == "amq.#{type}" or name
|
248
|
-
@status = :
|
249
|
-
|
250
|
-
|
251
|
-
|
347
|
+
unless name == "amq.#{type}" or name.empty? or opts[:no_declare]
|
348
|
+
@status = :opening
|
349
|
+
|
350
|
+
shim = Proc.new do |exchange, declare_ok|
|
351
|
+
case block.arity
|
352
|
+
when 1 then block.call(exchange)
|
353
|
+
else
|
354
|
+
block.call(exchange, declare_ok)
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
unless @opts[:no_declare]
|
359
|
+
@channel.once_open do
|
360
|
+
self.declare(passive = @opts[:passive], durable = @opts[:durable], exclusive = @opts[:exclusive], auto_delete = @opts[:auto_delete], nowait = @opts[:nowait], nil, &shim)
|
361
|
+
end
|
362
|
+
end
|
252
363
|
else
|
253
364
|
# Call the callback immediately, as given exchange is already
|
254
365
|
# declared.
|
255
|
-
@status = :
|
366
|
+
@status = :opened
|
256
367
|
block.call(self) if block
|
257
368
|
end
|
258
369
|
|
259
|
-
|
370
|
+
@on_declare = block
|
260
371
|
end
|
261
372
|
|
262
|
-
|
263
|
-
|
264
|
-
|
373
|
+
# @return [Channel]
|
374
|
+
# @api public
|
265
375
|
def channel
|
266
|
-
@
|
267
|
-
end
|
376
|
+
@channel
|
377
|
+
end
|
268
378
|
|
269
|
-
#
|
270
|
-
#
|
271
|
-
# configuration
|
272
|
-
# transaction, if any, is committed.
|
379
|
+
# Publishes message to the exchange. The message will be routed to queues by the exchange
|
380
|
+
# and distributed to any active consumers. Routing logic is determined by exchange type and
|
381
|
+
# configuration as well as message attributes (like :routing_key).
|
273
382
|
#
|
274
|
-
#
|
275
|
-
# exchange.publish("some data")
|
383
|
+
# h2. Data serialization
|
276
384
|
#
|
277
|
-
#
|
278
|
-
#
|
385
|
+
# Note that this method calls #to_s on payload argument value. You are encouraged to take care of
|
386
|
+
# data serialization before publishing (using JSON, Thrift, Protocol Buffers or other serialization library).
|
387
|
+
# Note that because AMQP is a binary protocol, text formats like JSON lose lose their strong point of being easy
|
388
|
+
# to inspect data as it travels across network.
|
279
389
|
#
|
280
|
-
# * :routing_key => 'string'
|
281
390
|
#
|
282
|
-
# Specifies the routing key for the message. The routing key is
|
283
|
-
# used for routing messages depending on the exchange configuration.
|
284
391
|
#
|
285
|
-
#
|
392
|
+
# h2. Event loop blocking
|
286
393
|
#
|
287
|
-
#
|
288
|
-
# routed to a queue. If this flag is set, the server will return an
|
289
|
-
# unroutable message with a Return method. If this flag is zero, the
|
290
|
-
# server silently drops the message.
|
394
|
+
# To minimize blocking of EventMachine event loop, this method performs network I/O on the next event loop tick.
|
291
395
|
#
|
292
|
-
#
|
396
|
+
# @param [#to_s] payload Message payload (content). Note that this method calls #to_s on payload argument value.
|
397
|
+
# You are encouraged to take care of data serialization before publishing (using JSON, Thrift,
|
398
|
+
# Protocol Buffers or other serialization library).
|
293
399
|
#
|
294
|
-
#
|
295
|
-
#
|
296
|
-
#
|
297
|
-
# If this flag is zero, the server will queue the message, but with
|
298
|
-
# no guarantee that it will ever be consumed.
|
400
|
+
# @option options [String] :routing_key (nil) Specifies message routing key. Routing key determines
|
401
|
+
# what queues messages are delivered to (exact routing algorithms vary
|
402
|
+
# between exchange types).
|
299
403
|
#
|
300
|
-
#
|
301
|
-
#
|
302
|
-
#
|
303
|
-
#
|
404
|
+
# @option options [Boolean] :mandatory (false) This flag tells the server how to react if the message cannot be
|
405
|
+
# routed to a queue. If message is mandatory, the server will return
|
406
|
+
# unroutable message back to the client with basic.return AMQPmethod.
|
407
|
+
# If message is not mandatory, the server silently drops the message.
|
304
408
|
#
|
305
|
-
#
|
306
|
-
#
|
307
|
-
#
|
409
|
+
# @option options [Boolean] :immediate (false) This flag tells the server how to react if the message cannot be
|
410
|
+
# routed to a queue consumer immediately. If this flag is set, the
|
411
|
+
# server will return an undeliverable message with a Return method.
|
412
|
+
# If this flag is zero, the server will queue the message, but with
|
413
|
+
# no guarantee that it will ever be consumed.
|
308
414
|
#
|
309
|
-
#
|
310
|
-
#
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
415
|
+
# @option options [Boolean] :persistent (false) When true, this message will be persisted to disk and remain in the queue until
|
416
|
+
# it is consumed. When false, the message is only kept in a transient store
|
417
|
+
# and will lost in case of server restart.
|
418
|
+
# When performance and latency are more important than durability, set :persistent => false.
|
419
|
+
# If durability is more important, set :persistent => true.
|
420
|
+
#
|
421
|
+
# @option options [String] :content_type (application/octet-stream) Content-type of message payload.
|
422
|
+
#
|
423
|
+
#
|
424
|
+
# @example Publishing without routing key
|
425
|
+
# exchange = channel.fanout('search.indexer')
|
426
|
+
# # fanout exchanges deliver messages to bound queues unconditionally,
|
427
|
+
# # so routing key is unnecessary here
|
428
|
+
# exchange.publish("some data")
|
429
|
+
#
|
430
|
+
# @example Publishing with a routing key
|
431
|
+
# exchange = channel.direct('search.indexer')
|
432
|
+
# exchange.publish("some data", :routing_key => "search.index.updates")
|
433
|
+
#
|
434
|
+
# @return [Exchange] self
|
435
|
+
#
|
436
|
+
# @note Please make sure you read {Exchange Exchange class} documentation section on exchanges durability vs. messages
|
437
|
+
# persistence.
|
438
|
+
# @api public
|
439
|
+
def publish(payload, options = {}, &block)
|
440
|
+
EM.next_tick do
|
441
|
+
opts = @default_publish_options.merge(options)
|
324
442
|
|
325
|
-
|
443
|
+
@channel.once_open do
|
444
|
+
super(payload.to_s, opts[:key] || opts[:routing_key] || @default_routing_key, @default_headers.merge(options), opts[:mandatory], opts[:immediate], &block)
|
445
|
+
end
|
446
|
+
end
|
326
447
|
|
327
|
-
@mq.send *out
|
328
|
-
}
|
329
448
|
self
|
330
449
|
end
|
331
450
|
|
451
|
+
|
332
452
|
# This method deletes an exchange. When an exchange is deleted all queue
|
333
453
|
# bindings on the exchange are cancelled.
|
334
454
|
#
|
335
455
|
# Further attempts to publish messages to a deleted exchange will raise
|
336
456
|
# an AMQP::Channel::Error due to a channel close exception.
|
337
457
|
#
|
338
|
-
# exchange = AMQP::Channel.direct('name', :
|
458
|
+
# exchange = AMQP::Channel.direct('name', :routing_key => 'foo.bar')
|
339
459
|
# exchange.delete
|
340
460
|
#
|
341
461
|
# == Options
|
@@ -351,43 +471,51 @@ module AMQP
|
|
351
471
|
# bindings. If the exchange has queue bindings the server does not
|
352
472
|
# delete it but raises a channel exception instead (AMQP::Error).
|
353
473
|
#
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
474
|
+
# @api public
|
475
|
+
def delete(opts = {}, &block)
|
476
|
+
@channel.once_open do
|
477
|
+
super(opts.fetch(:if_unused, false), opts.fetch(:nowait, false), &block)
|
478
|
+
end
|
479
|
+
|
480
|
+
# backwards compatibility
|
360
481
|
nil
|
361
482
|
end
|
362
483
|
|
363
|
-
|
484
|
+
# @return [Boolean] true if this exchange is durable
|
485
|
+
# @note Please make sure you read {Exchange Exchange class} documentation section on exchanges durability vs. messages
|
486
|
+
# persistence.
|
487
|
+
# @api public
|
364
488
|
def durable?
|
365
489
|
!!@opts[:durable]
|
366
490
|
end # durable?
|
367
491
|
|
492
|
+
# @return [Boolean] true if this exchange is transient (non-durable)
|
493
|
+
# @note Please make sure you read {Exchange Exchange class} documentation section on exchanges durability vs. messages
|
494
|
+
# persistence.
|
495
|
+
# @api public
|
368
496
|
def transient?
|
369
497
|
!self.durable?
|
370
498
|
end # transient?
|
499
|
+
alias temporary? transient?
|
371
500
|
|
501
|
+
# @return [Boolean] true if this exchange is automatically deleted when it is no longer used
|
502
|
+
# @api public
|
372
503
|
def auto_deleted?
|
373
504
|
!!@opts[:auto_delete]
|
374
505
|
end # auto_deleted?
|
375
506
|
alias auto_deletable? auto_deleted?
|
376
507
|
|
377
|
-
|
508
|
+
# Resets queue state. Useful for error handling.
|
509
|
+
# @api plugin
|
378
510
|
def reset
|
379
|
-
@
|
380
|
-
initialize @mq, @type, @name, @opts
|
511
|
+
initialize(@channel, @type, @name, @opts)
|
381
512
|
end
|
382
513
|
|
383
514
|
|
515
|
+
protected
|
384
516
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
def receive_response(response)
|
390
|
-
self.callback && self.callback.call(self)
|
391
|
-
end # receive_response
|
517
|
+
def self.add_default_options(type, name, opts, block)
|
518
|
+
{ :exchange => name, :type => type, :nowait => block.nil? }.merge(opts)
|
519
|
+
end
|
392
520
|
end # Exchange
|
393
521
|
end # AMQP
|