amqp 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|