amqp 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -2
- data/CHANGELOG +25 -0
- data/Gemfile +4 -2
- data/README.md +2 -0
- data/{amqp.todo → TODO} +1 -3
- data/amqp.gemspec +3 -3
- data/bin/irb +2 -2
- data/bin/jenkins.sh +25 -0
- data/bin/set_test_suite_realms_up.sh +21 -0
- data/doc/EXAMPLE_01_PINGPONG +1 -1
- data/doc/EXAMPLE_02_CLOCK +1 -1
- data/doc/EXAMPLE_03_STOCKS +1 -1
- data/doc/EXAMPLE_04_MULTICLOCK +1 -1
- data/doc/EXAMPLE_05_ACK +1 -1
- data/doc/EXAMPLE_05_POP +1 -1
- data/doc/EXAMPLE_06_HASHTABLE +1 -1
- data/examples/{mq/ack.rb → ack.rb} +6 -6
- data/examples/{mq/automatic_binding_for_default_direct_exchange.rb → automatic_binding_for_default_direct_exchange.rb} +4 -4
- data/examples/{mq/callbacks.rb → callbacks.rb} +2 -2
- data/examples/{mq/clock.rb → clock.rb} +5 -5
- data/examples/{mq/hashtable.rb → hashtable.rb} +4 -4
- data/examples/{mq/internal.rb → internal.rb} +5 -5
- data/examples/{mq/logger.rb → logger.rb} +5 -5
- data/examples/{mq/multiclock.rb → multiclock.rb} +4 -4
- data/examples/{mq/pingpong.rb → pingpong.rb} +5 -5
- data/examples/{mq/pop.rb → pop.rb} +3 -3
- data/examples/{mq/primes-simple.rb → primes-simple.rb} +0 -0
- data/examples/{mq/primes.rb → primes.rb} +6 -6
- data/examples/{amqp/simple.rb → simple.rb} +1 -1
- data/examples/{mq/stocks.rb → stocks.rb} +5 -5
- data/lib/amqp.rb +8 -112
- data/lib/amqp/basic_client.rb +58 -0
- data/lib/amqp/channel.rb +937 -0
- data/lib/amqp/client.rb +72 -79
- data/lib/{mq → amqp}/collection.rb +12 -2
- data/lib/amqp/connection.rb +115 -0
- data/lib/amqp/exceptions.rb +18 -0
- data/lib/{mq → amqp}/exchange.rb +32 -34
- data/lib/{ext → amqp/ext}/em.rb +1 -1
- data/lib/{ext → amqp/ext}/emfork.rb +0 -0
- data/lib/amqp/frame.rb +3 -3
- data/lib/{mq → amqp}/header.rb +5 -11
- data/lib/{mq → amqp}/logger.rb +2 -2
- data/lib/amqp/protocol.rb +2 -2
- data/lib/{mq → amqp}/queue.rb +20 -17
- data/lib/{mq → amqp}/rpc.rb +20 -8
- data/lib/amqp/server.rb +1 -1
- data/lib/amqp/version.rb +1 -1
- data/lib/mq.rb +20 -964
- data/protocol/codegen.rb +1 -1
- data/research/api.rb +3 -3
- data/research/primes-forked.rb +5 -5
- data/research/primes-processes.rb +5 -5
- data/research/primes-threaded.rb +5 -5
- data/spec/integration/authentication_spec.rb +114 -0
- data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +13 -12
- data/spec/{unit/mq → integration}/channel_close_spec.rb +2 -2
- data/spec/{unit/mq → integration}/exchange_declaration_spec.rb +26 -14
- data/spec/{unit/mq → integration}/queue_declaration_spec.rb +4 -4
- data/spec/integration/queue_exclusivity_spec.rb +95 -0
- data/spec/integration/reply_queue_communication_spec.rb +63 -0
- data/spec/integration/store_and_forward_spec.rb +121 -0
- data/spec/integration/topic_subscription_spec.rb +193 -0
- data/spec/integration/workload_distribution_spec.rb +245 -0
- data/spec/spec_helper.rb +16 -32
- data/spec/unit/{mq/mq_basic_spec.rb → amqp/basic_spec.rb} +4 -4
- data/spec/unit/{mq → amqp}/collection_spec.rb +22 -7
- data/spec/unit/amqp/connection_spec.rb +116 -0
- data/spec/unit/amqp/frame_spec.rb +18 -18
- data/spec/unit/amqp/protocol_spec.rb +9 -11
- metadata +54 -49
- data/lib/ext/blankslate.rb +0 -9
- data/spec/mq_helper.rb +0 -70
- data/spec/unit/amqp/client_spec.rb +0 -472
- data/spec/unit/amqp/misc_spec.rb +0 -123
- data/spec/unit/mq/misc_spec.rb +0 -228
- data/spec/unit/mq/queue_spec.rb +0 -71
data/lib/amqp/client.rb
CHANGED
@@ -1,60 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require
|
3
|
+
require "amqp/basic_client"
|
4
4
|
|
5
5
|
require 'uri'
|
6
6
|
|
7
7
|
module AMQP
|
8
|
-
class Error < StandardError; end
|
9
|
-
|
10
|
-
module BasicClient
|
11
|
-
attr_reader :broker
|
12
|
-
|
13
|
-
def process_frame(frame)
|
14
|
-
if mq = channels[frame.channel]
|
15
|
-
mq.process_frame(frame)
|
16
|
-
return
|
17
|
-
end
|
18
|
-
|
19
|
-
case frame
|
20
|
-
when Frame::Method
|
21
|
-
case method = frame.payload
|
22
|
-
when Protocol::Connection::Start
|
23
|
-
@broker = method
|
24
|
-
send Protocol::Connection::StartOk.new({:platform => 'Ruby/EventMachine',
|
25
|
-
:product => 'AMQP',
|
26
|
-
:information => 'http://github.com/ruby-amqp/amqp',
|
27
|
-
:version => VERSION},
|
28
|
-
'AMQPLAIN',
|
29
|
-
{:LOGIN => @settings[:user],
|
30
|
-
:PASSWORD => @settings[:pass]},
|
31
|
-
'en_US')
|
32
|
-
|
33
|
-
when Protocol::Connection::Tune
|
34
|
-
send Protocol::Connection::TuneOk.new(:channel_max => 0,
|
35
|
-
:frame_max => 131072,
|
36
|
-
:heartbeat => 0)
|
37
|
-
|
38
|
-
send Protocol::Connection::Open.new(:virtual_host => @settings[:vhost],
|
39
|
-
:capabilities => '',
|
40
|
-
:insist => @settings[:insist])
|
41
|
-
|
42
|
-
@on_disconnect = method(:disconnected)
|
43
|
-
|
44
|
-
when Protocol::Connection::OpenOk
|
45
|
-
succeed(self)
|
46
|
-
|
47
|
-
when Protocol::Connection::Close
|
48
|
-
# raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
|
49
|
-
STDERR.puts "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
|
50
|
-
|
51
|
-
when Protocol::Connection::CloseOk
|
52
|
-
@on_disconnect.call if @on_disconnect
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
8
|
def self.client
|
59
9
|
@client ||= BasicClient
|
60
10
|
end
|
@@ -78,25 +28,55 @@ module AMQP
|
|
78
28
|
timeout @settings[:timeout] if @settings[:timeout]
|
79
29
|
errback { @on_disconnect.call } unless @reconnecting
|
80
30
|
|
81
|
-
|
31
|
+
# TCP connection "openness"
|
32
|
+
@tcp_connection_established = false
|
33
|
+
# AMQP connection "openness"
|
34
|
+
@connected = false
|
82
35
|
end
|
83
36
|
|
84
37
|
def connection_completed
|
85
|
-
|
38
|
+
if @settings[:ssl].is_a? Hash
|
39
|
+
start_tls @settings[:ssl]
|
40
|
+
elsif @settings[:ssl]
|
41
|
+
start_tls
|
42
|
+
end
|
43
|
+
|
86
44
|
log 'connected'
|
87
45
|
# @on_disconnect = proc { raise Error, 'Disconnected from server' }
|
88
46
|
unless @closing
|
89
47
|
@reconnecting = false
|
90
48
|
end
|
91
49
|
|
92
|
-
@
|
93
|
-
@connection_status.call(:connected) if @connection_status
|
50
|
+
@tcp_connection_established = true
|
94
51
|
|
95
52
|
@buf = Buffer.new
|
96
53
|
send_data HEADER
|
97
54
|
send_data [1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4')
|
55
|
+
|
56
|
+
if heartbeat = @settings[:heartbeat]
|
57
|
+
init_heartbeat if (@settings[:heartbeat] = heartbeat.to_i) > 0
|
58
|
+
end
|
98
59
|
end
|
99
60
|
|
61
|
+
def init_heartbeat
|
62
|
+
@last_server_heartbeat = Time.now
|
63
|
+
|
64
|
+
@timer ||= EM::PeriodicTimer.new(@settings[:heartbeat]) do
|
65
|
+
if connected?
|
66
|
+
if @last_server_heartbeat < (Time.now - (@settings[:heartbeat] * 2))
|
67
|
+
log "Reconnecting due to missing server heartbeats"
|
68
|
+
reconnect(true)
|
69
|
+
else
|
70
|
+
send AMQP::Frame::Heartbeat.new
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def tcp_connection_established?
|
77
|
+
@tcp_connection_established
|
78
|
+
end # tcp_connection_established?
|
79
|
+
|
100
80
|
def connected?
|
101
81
|
@connected
|
102
82
|
end
|
@@ -104,7 +84,7 @@ module AMQP
|
|
104
84
|
def unbind
|
105
85
|
log 'disconnected'
|
106
86
|
@connected = false
|
107
|
-
EM.next_tick { @on_disconnect.call }
|
87
|
+
EM.next_tick { @on_disconnect.call; @tcp_connection_established = false }
|
108
88
|
end
|
109
89
|
|
110
90
|
def add_channel(mq)
|
@@ -198,17 +178,27 @@ module AMQP
|
|
198
178
|
EM.reconnect @settings[:host], @settings[:port], self
|
199
179
|
end
|
200
180
|
|
201
|
-
def self.connect
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
181
|
+
def self.connect(arg = nil)
|
182
|
+
opts = case arg
|
183
|
+
when String then
|
184
|
+
opts = parse_connection_uri(arg)
|
185
|
+
when Hash then
|
186
|
+
arg
|
187
|
+
else
|
188
|
+
Hash.new
|
189
|
+
end
|
190
|
+
|
191
|
+
options = AMQP.settings.merge(opts)
|
192
|
+
|
193
|
+
if options[:username]
|
194
|
+
options[:user] = options.delete(:username)
|
208
195
|
end
|
209
196
|
|
210
|
-
|
211
|
-
|
197
|
+
if options[:password]
|
198
|
+
options[:pass] = options.delete(:password)
|
199
|
+
end
|
200
|
+
|
201
|
+
EM.connect options[:host], options[:port], self, options
|
212
202
|
end
|
213
203
|
|
214
204
|
def connection_status(&blk)
|
@@ -217,6 +207,23 @@ module AMQP
|
|
217
207
|
|
218
208
|
private
|
219
209
|
|
210
|
+
def self.parse_connection_uri(connection_string)
|
211
|
+
uri = URI.parse(connection_string)
|
212
|
+
raise("amqp:// uri required!") unless %w{amqp amqps}.include?(uri.scheme)
|
213
|
+
|
214
|
+
opts = {}
|
215
|
+
|
216
|
+
opts[:user] = URI.unescape(uri.user) if uri.user
|
217
|
+
opts[:pass] = URI.unescape(uri.password) if uri.password
|
218
|
+
opts[:vhost] = URI.unescape(uri.path) if uri.path
|
219
|
+
opts[:host] = uri.host if uri.host
|
220
|
+
opts[:port] = uri.port || Hash["amqp" => 5672, "amqps" => 5671][uri.scheme]
|
221
|
+
opts[:ssl] = uri.scheme == "amqps"
|
222
|
+
|
223
|
+
opts
|
224
|
+
end
|
225
|
+
|
226
|
+
|
220
227
|
def disconnected
|
221
228
|
@connection_status.call(:disconnected) if @connection_status
|
222
229
|
reconnect
|
@@ -228,19 +235,5 @@ module AMQP
|
|
228
235
|
pp args
|
229
236
|
puts
|
230
237
|
end
|
231
|
-
|
232
|
-
def self.parse_amqp_url(amqp_url)
|
233
|
-
uri = URI.parse(amqp_url)
|
234
|
-
raise("amqp:// uri required!") unless %w{amqp amqps}.include? uri.scheme
|
235
|
-
opts = {}
|
236
|
-
opts[:user] = URI.unescape(uri.user) if uri.user
|
237
|
-
opts[:pass] = URI.unescape(uri.password) if uri.password
|
238
|
-
opts[:vhost] = URI.unescape(uri.path) if uri.path
|
239
|
-
opts[:host] = uri.host if uri.host
|
240
|
-
opts[:port] = uri.port ? uri.port :
|
241
|
-
{"amqp" => 5672, "amqps" => 5671}[uri.scheme]
|
242
|
-
opts[:ssl] = uri.scheme == "amqps"
|
243
|
-
return opts
|
244
|
-
end
|
245
238
|
end
|
246
239
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
#
|
3
|
+
module AMQP
|
4
|
+
# AMQP::Collection is used to store named AMQ model entities (exchanges, queues)
|
5
5
|
class Collection < ::Array
|
6
6
|
class IncompatibleItemError < ArgumentError
|
7
7
|
def initialize(item)
|
@@ -46,5 +46,15 @@ class MQ
|
|
46
46
|
__push__(item)
|
47
47
|
return item
|
48
48
|
end
|
49
|
+
|
50
|
+
def include?(name)
|
51
|
+
not self[name].nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
def delete(name)
|
55
|
+
if self.include?(name)
|
56
|
+
self.delete_at(self.index(self[name]))
|
57
|
+
end
|
58
|
+
end
|
49
59
|
end
|
50
60
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "amqp/ext/em"
|
4
|
+
|
5
|
+
require "amqp/buffer"
|
6
|
+
require "amqp/spec"
|
7
|
+
require "amqp/protocol"
|
8
|
+
require "amqp/frame"
|
9
|
+
require "amqp/client"
|
10
|
+
|
11
|
+
module AMQP
|
12
|
+
class << self
|
13
|
+
@logging = false
|
14
|
+
attr_accessor :logging
|
15
|
+
attr_reader :conn, :closing
|
16
|
+
alias :closing? :closing
|
17
|
+
alias :connection :conn
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.connect *args
|
21
|
+
Client.connect *args
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.settings
|
25
|
+
@settings ||= {
|
26
|
+
# server address
|
27
|
+
:host => '127.0.0.1',
|
28
|
+
:port => PORT,
|
29
|
+
|
30
|
+
# login details
|
31
|
+
:user => 'guest',
|
32
|
+
:pass => 'guest',
|
33
|
+
:vhost => '/',
|
34
|
+
|
35
|
+
# connection timeout
|
36
|
+
:timeout => nil,
|
37
|
+
|
38
|
+
# logging
|
39
|
+
:logging => false,
|
40
|
+
|
41
|
+
# ssl
|
42
|
+
:ssl => false
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Must be called to startup the connection to the AMQP server.
|
47
|
+
#
|
48
|
+
# The method takes several arguments and an optional block.
|
49
|
+
#
|
50
|
+
# This takes any option that is also accepted by EventMachine::connect.
|
51
|
+
# Additionally, there are several AMQP-specific options.
|
52
|
+
#
|
53
|
+
# * :user => String (default 'guest')
|
54
|
+
# The username as defined by the AMQP server.
|
55
|
+
# * :pass => String (default 'guest')
|
56
|
+
# The password for the associated :user as defined by the AMQP server.
|
57
|
+
# * :vhost => String (default '/')
|
58
|
+
# The virtual host as defined by the AMQP server.
|
59
|
+
# * :timeout => Numeric (default nil)
|
60
|
+
# Measured in seconds.
|
61
|
+
# * :logging => true | false (default false)
|
62
|
+
# Toggle the extremely verbose logging of all protocol communications
|
63
|
+
# between the client and the server. Extremely useful for debugging.
|
64
|
+
#
|
65
|
+
# AMQP.start do
|
66
|
+
# # default is to connect to localhost:5672
|
67
|
+
#
|
68
|
+
# # define queues, exchanges and bindings here.
|
69
|
+
# # also define all subscriptions and/or publishers
|
70
|
+
# # here.
|
71
|
+
#
|
72
|
+
# # this block never exits unless EM.stop_event_loop
|
73
|
+
# # is called.
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# Most code will use the MQ api. Any calls to AMQP::Channel.direct / AMQP::Channel.fanout /
|
77
|
+
# AMQP::Channel.topic / AMQP::Channel.queue will implicitly call #start. In those cases,
|
78
|
+
# it is sufficient to put your code inside of an EventMachine.run
|
79
|
+
# block. See the code examples in AMQP for details.
|
80
|
+
#
|
81
|
+
def self.start *args, &blk
|
82
|
+
EM.run {
|
83
|
+
@conn ||= connect *args
|
84
|
+
@conn.callback(&blk) if blk
|
85
|
+
@conn
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
class << self
|
90
|
+
alias :run :start
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.stop
|
94
|
+
if @conn and not @closing
|
95
|
+
@closing = true
|
96
|
+
EM.next_tick do
|
97
|
+
@conn.close {
|
98
|
+
yield if block_given?
|
99
|
+
@conn = nil
|
100
|
+
@closing = false
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.fork workers
|
107
|
+
EM.fork(workers) do
|
108
|
+
# clean up globals in the fork
|
109
|
+
Thread.current[:mq] = nil
|
110
|
+
AMQP.instance_variable_set('@conn', nil)
|
111
|
+
|
112
|
+
yield
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
# Raised whenever an illegal operation is attempted.
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
class IncompatibleOptionsError < Error
|
8
|
+
def initialize(name, opts_1, opts_2)
|
9
|
+
super("There is already an instance called #{name} with options #{opts_1.inspect}, you can't define the same instance with different options (#{opts_2.inspect})!")
|
10
|
+
end
|
11
|
+
end # IncompatibleOptionsError
|
12
|
+
|
13
|
+
class ChannelClosedError < Error
|
14
|
+
def initialize(instance)
|
15
|
+
super("The channel #{instance.channel} was closed, you can't use it anymore!")
|
16
|
+
end
|
17
|
+
end # ChannelClosedError
|
18
|
+
end # AMQP
|
data/lib/{mq → amqp}/exchange.rb
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
module AMQP
|
4
4
|
# An Exchange acts as an ingress point for all published messages. An
|
5
5
|
# exchange may also be described as a router or a matcher. Every
|
6
6
|
# published message is received by an exchange which, depending on its
|
@@ -14,7 +14,7 @@ class MQ
|
|
14
14
|
# As part of the standard, the server _must_ predeclare the direct exchange
|
15
15
|
# 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
|
16
16
|
# starting with 'amq.' are reserved). Attempts to declare an exchange using
|
17
|
-
# 'amq.' as the name will raise an
|
17
|
+
# 'amq.' as the name will raise an AMQP::Error and fail. In practice these
|
18
18
|
# default exchanges are never used directly by client code.
|
19
19
|
#
|
20
20
|
# These predececlared exchanges are used when the client code declares
|
@@ -23,14 +23,6 @@ class MQ
|
|
23
23
|
#
|
24
24
|
class Exchange
|
25
25
|
|
26
|
-
#
|
27
|
-
# Behaviors
|
28
|
-
#
|
29
|
-
|
30
|
-
include AMQP
|
31
|
-
|
32
|
-
|
33
|
-
|
34
26
|
#
|
35
27
|
# API
|
36
28
|
#
|
@@ -45,12 +37,12 @@ class MQ
|
|
45
37
|
# predefined in the system, so you can use it straightaway.
|
46
38
|
|
47
39
|
# Example:
|
48
|
-
#
|
49
|
-
#
|
40
|
+
# AMQP::Channel.new.queue("tasks")
|
41
|
+
# AMQP::Channel::Exchange.default.publish("make clean", routing_key: "tasks")
|
50
42
|
|
51
43
|
# For more info see section 2.1.2.4 Automatic Mode of the AMQP 0.9.1 spec.
|
52
|
-
def self.default
|
53
|
-
|
44
|
+
def self.default(channel = nil)
|
45
|
+
self.new(channel || AMQP::Channel.new, :direct, "", :no_declare => true)
|
54
46
|
end
|
55
47
|
|
56
48
|
def self.add_default_options(type, name, opts, block)
|
@@ -65,7 +57,7 @@ class MQ
|
|
65
57
|
# As part of the standard, the server _must_ predeclare the direct exchange
|
66
58
|
# 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
|
67
59
|
# starting with 'amq.' are reserved). Attempts to declare an exchange using
|
68
|
-
# 'amq.' as the name will raise an
|
60
|
+
# 'amq.' as the name will raise an AMQP::Error and fail. In practice these
|
69
61
|
# default exchanges are never used directly by client code.
|
70
62
|
#
|
71
63
|
# == Direct
|
@@ -80,14 +72,14 @@ class MQ
|
|
80
72
|
# called 'amq.direct'. In this case it needs to use :key to do its matching.
|
81
73
|
#
|
82
74
|
# # exchange is named 'foo'
|
83
|
-
# exchange =
|
75
|
+
# exchange = AMQP::Channel::Exchange.new(AMQP::Channel.new, :direct, 'foo')
|
84
76
|
#
|
85
77
|
# # or, the exchange can use the default name (amq.direct) and perform
|
86
78
|
# # routing comparisons using the :key
|
87
|
-
# exchange =
|
79
|
+
# exchange = AMQP::Channel::Exchange.new(AMQP::Channel.new, :direct, "", :key => 'foo')
|
88
80
|
# exchange.publish('some data') # will be delivered to queue bound to 'foo'
|
89
81
|
#
|
90
|
-
# queue =
|
82
|
+
# queue = AMQP::Channel::Queue.new(AMQP::Channel.new, 'foo')
|
91
83
|
# # can receive data since the queue name and the exchange key match exactly
|
92
84
|
# queue.pop { |data| puts "received data [#{data}]" }
|
93
85
|
#
|
@@ -105,21 +97,21 @@ class MQ
|
|
105
97
|
# In this case it needs to use :key to do its matching.
|
106
98
|
#
|
107
99
|
# EM.run do
|
108
|
-
# clock =
|
100
|
+
# clock = AMQP::Channel::Exchange.new(AMQP::Channel.new, :fanout, 'clock')
|
109
101
|
# EM.add_periodic_timer(1) do
|
110
102
|
# puts "\npublishing #{time = Time.now}"
|
111
103
|
# clock.publish(Marshal.dump(time))
|
112
104
|
# end
|
113
105
|
#
|
114
106
|
# # one way of defining a queue
|
115
|
-
# amq =
|
116
|
-
# amq.bind(
|
107
|
+
# amq = AMQP::Channel::Queue.new(AMQP::Channel.new, 'every second')
|
108
|
+
# amq.bind(AMQP::Channel.fanout('clock')).subscribe do |time|
|
117
109
|
# puts "every second received #{Marshal.load(time)}"
|
118
110
|
# end
|
119
111
|
#
|
120
112
|
# # defining a queue using the convenience method
|
121
113
|
# # note the string passed to #bind
|
122
|
-
#
|
114
|
+
# AMQP::Channel.queue('every 5 seconds').bind('clock').subscribe do |time|
|
123
115
|
# time = Marshal.load(time)
|
124
116
|
# puts "every 5 seconds received #{time}" if time.strftime('%S').to_i%5 == 0
|
125
117
|
# end
|
@@ -150,7 +142,7 @@ class MQ
|
|
150
142
|
# for matching against the published routing key.
|
151
143
|
#
|
152
144
|
# EM.run do
|
153
|
-
# exch =
|
145
|
+
# exch = AMQP::Channel::Exchange.new(AMQP::Channel.new, :topic, "stocks")
|
154
146
|
# keys = ['stock.us.aapl', 'stock.de.dax']
|
155
147
|
#
|
156
148
|
# EM.add_periodic_timer(1) do # every second
|
@@ -159,17 +151,17 @@ class MQ
|
|
159
151
|
# end
|
160
152
|
#
|
161
153
|
# # match against one dot-separated item
|
162
|
-
#
|
154
|
+
# AMQP::Channel.queue('us stocks').bind(exch, :key => 'stock.us.*').subscribe do |price|
|
163
155
|
# puts "us stock price [#{price}]"
|
164
156
|
# end
|
165
157
|
#
|
166
158
|
# # match against multiple dot-separated items
|
167
|
-
#
|
159
|
+
# AMQP::Channel.queue('all stocks').bind(exch, :key => 'stock.#').subscribe do |price|
|
168
160
|
# puts "all stocks: price [#{price}]"
|
169
161
|
# end
|
170
162
|
#
|
171
163
|
# # require exact match
|
172
|
-
#
|
164
|
+
# AMQP::Channel.queue('only dax').bind(exch, :key => 'stock.de.dax').subscribe do |price|
|
173
165
|
# puts "dax price [#{price}]"
|
174
166
|
# end
|
175
167
|
# end
|
@@ -230,8 +222,8 @@ class MQ
|
|
230
222
|
#
|
231
223
|
# == Exceptions
|
232
224
|
# Doing any of these activities are illegal and will raise exceptions:
|
233
|
-
#
|
234
|
-
# * redeclare an already-declared exchange to a different type (raises
|
225
|
+
#
|
226
|
+
# * redeclare an already-declared exchange to a different type (raises AMQP::Channel::IncompatibleOptionsError)
|
235
227
|
# * :passive => true and the exchange does not exist (NOT_FOUND)
|
236
228
|
#
|
237
229
|
def initialize(mq, type, name, opts = {}, &block)
|
@@ -270,12 +262,16 @@ class MQ
|
|
270
262
|
attr_reader :name, :type, :key, :status
|
271
263
|
attr_accessor :opts, :callback
|
272
264
|
|
265
|
+
def channel
|
266
|
+
@mq
|
267
|
+
end # channel
|
268
|
+
|
273
269
|
# This method publishes a staged file message to a specific exchange.
|
274
270
|
# The file message will be routed to queues as defined by the exchange
|
275
271
|
# configuration and distributed to any active consumers when the
|
276
272
|
# transaction, if any, is committed.
|
277
273
|
#
|
278
|
-
# exchange =
|
274
|
+
# exchange = AMQP::Channel.direct('name', :key => 'foo.bar')
|
279
275
|
# exchange.publish("some data")
|
280
276
|
#
|
281
277
|
# The method takes several hash key options which modify the behavior or
|
@@ -310,6 +306,8 @@ class MQ
|
|
310
306
|
# message stays in memory and is never persisted to non-volatile (slow)
|
311
307
|
# storage.
|
312
308
|
#
|
309
|
+
# * :content_type
|
310
|
+
# Content type you want to send the message with. It defaults to "application/octet-stream".
|
313
311
|
def publish(data, opts = {})
|
314
312
|
@mq.callback {
|
315
313
|
out = []
|
@@ -320,7 +318,7 @@ class MQ
|
|
320
318
|
data = data.to_s
|
321
319
|
|
322
320
|
out << Protocol::Header.new(Protocol::Basic,
|
323
|
-
data.bytesize, { :content_type =>
|
321
|
+
data.bytesize, { opts[:content_type] || :content_type => "application/octet-stream",
|
324
322
|
:delivery_mode => (opts[:persistent] ? 2 : 1),
|
325
323
|
:priority => 0 }.merge(opts))
|
326
324
|
|
@@ -335,9 +333,9 @@ class MQ
|
|
335
333
|
# bindings on the exchange are cancelled.
|
336
334
|
#
|
337
335
|
# Further attempts to publish messages to a deleted exchange will raise
|
338
|
-
# an
|
336
|
+
# an AMQP::Channel::Error due to a channel close exception.
|
339
337
|
#
|
340
|
-
# exchange =
|
338
|
+
# exchange = AMQP::Channel.direct('name', :key => 'foo.bar')
|
341
339
|
# exchange.delete
|
342
340
|
#
|
343
341
|
# == Options
|
@@ -351,7 +349,7 @@ class MQ
|
|
351
349
|
# * :if_unused => true | false (default false)
|
352
350
|
# If set, the server will only delete the exchange if it has no queue
|
353
351
|
# bindings. If the exchange has queue bindings the server does not
|
354
|
-
# delete it but raises a channel exception instead (
|
352
|
+
# delete it but raises a channel exception instead (AMQP::Error).
|
355
353
|
#
|
356
354
|
def delete(opts = {})
|
357
355
|
@mq.callback {
|
@@ -392,4 +390,4 @@ class MQ
|
|
392
390
|
self.callback && self.callback.call(self)
|
393
391
|
end # receive_response
|
394
392
|
end # Exchange
|
395
|
-
end #
|
393
|
+
end # AMQP
|