tmm1-amqp 0.5.5 → 0.5.9
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/README +11 -5
- data/examples/mq/logger.rb +15 -66
- data/examples/mq/stocks.rb +39 -37
- data/lib/amqp.rb +25 -3
- data/lib/amqp/buffer.rb +2 -2
- data/lib/amqp/client.rb +20 -14
- data/lib/amqp/frame.rb +1 -1
- data/lib/amqp/spec.rb +15 -2
- data/lib/ext/em.rb +1 -2
- data/lib/mq.rb +64 -14
- data/lib/mq/exchange.rb +20 -7
- data/lib/mq/logger.rb +89 -0
- data/lib/mq/queue.rb +98 -4
- data/protocol/amqp-0.8.json +12 -1
- metadata +4 -3
data/README
CHANGED
@@ -7,6 +7,8 @@ Simple AMQP driver for Ruby/EventMachine
|
|
7
7
|
http://groups.google.com/group/ruby-amqp
|
8
8
|
http://lists.rabbitmq.com/pipermail/rabbitmq-discuss/2008-July/001417.html
|
9
9
|
|
10
|
+
This library works with Ruby 1.8, Ruby 1.9 and JRuby.
|
11
|
+
|
10
12
|
This library was tested primarily with RabbitMQ, although it should be compatible with any
|
11
13
|
server implementing the AMQP 0-8 spec.
|
12
14
|
|
@@ -49,8 +51,8 @@ AMQP resources:
|
|
49
51
|
|
50
52
|
Servers:
|
51
53
|
RabbitMQ (Rabbit Technologies, Erlang/OTP, MPL) - http://rabbitmq.com
|
52
|
-
ZeroMQ (
|
53
|
-
OpenAMQ (
|
54
|
+
ZeroMQ (iMatix/FastMQ/Intel, C++, GPL3) - http://www.zeromq.org
|
55
|
+
OpenAMQ (iMatix, C, GPL2) - http://openamq.org
|
54
56
|
ActiveMQ (Apache Foundation, Java, apache2) - http://activemq.apache.org
|
55
57
|
|
56
58
|
Steve Vinoski explains AMQP in his column, Towards Integration
|
@@ -65,8 +67,8 @@ AMQP resources:
|
|
65
67
|
ZeroMQ's analysis of the messaging technology market
|
66
68
|
http://www.zeromq.org/whitepapers:market-analysis
|
67
69
|
|
68
|
-
|
69
|
-
http://www.
|
70
|
+
Pieter Hintjens's background to AMQP
|
71
|
+
http://www.openamq.org/doc:amqp-background
|
70
72
|
|
71
73
|
Barry Pederson's py-amqplib
|
72
74
|
http://barryp.org/software/py-amqplib/
|
@@ -86,6 +88,7 @@ AMQP resources:
|
|
86
88
|
http://hg.rabbitmq.com/rabbitmq-codegen/
|
87
89
|
|
88
90
|
Erlang Exchange presentation on the implementation of RabbitMQ
|
91
|
+
http://skillsmatter.com/podcast/erlang/presenting-rabbitmq-an-erlang-based-implementatio-nof-amqp
|
89
92
|
http://www.lshift.net/blog/2008/07/01/slides-from-our-erlang-exchange-talk
|
90
93
|
|
91
94
|
Jonathan Conway's series on RabbitMQ and using it with Ruby/Merb
|
@@ -113,5 +116,8 @@ Messaging and distributed systems resources:
|
|
113
116
|
Metaprotocol Taxonomy and Communications Patterns
|
114
117
|
http://hessian.caucho.com/doc/metaprotocol-taxonomy.xtp
|
115
118
|
|
116
|
-
Joe Armstrong on Erlang messaging vs RPC
|
119
|
+
Joe Armstrong on Erlang messaging vs RPC
|
117
120
|
http://armstrongonsoftware.blogspot.com/2008/05/road-we-didnt-go-down.html
|
121
|
+
|
122
|
+
SEDA: scalable internet services using message queues
|
123
|
+
http://www.eecs.harvard.edu/~mdw/papers/seda-sosp01.pdf
|
data/examples/mq/logger.rb
CHANGED
@@ -1,81 +1,20 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__) + '/../../lib'
|
2
2
|
require 'mq'
|
3
|
+
require 'mq/logger'
|
3
4
|
|
4
|
-
|
5
|
-
def initialize *args, &block
|
6
|
-
opts = args.pop if args.last.is_a? Hash
|
7
|
-
opts ||= {}
|
8
|
-
printer(block) if block
|
9
|
-
@log = []
|
10
|
-
@tags = ([:timestamp] + args).uniq
|
11
|
-
end
|
12
|
-
|
13
|
-
def log severity, *args
|
14
|
-
opts = args.pop if args.last.is_a? Hash and args.size != 1
|
15
|
-
opts ||= {}
|
16
|
-
data = args.shift
|
17
|
-
|
18
|
-
data = {:type => :exception,
|
19
|
-
:name => data.class.to_s.intern,
|
20
|
-
:backtrace => data.backtrace,
|
21
|
-
:message => data.message} if data.is_a? Exception
|
22
|
-
|
23
|
-
(@tags + args).each do |tag|
|
24
|
-
tag = tag.to_sym
|
25
|
-
case tag
|
26
|
-
when :timestamp
|
27
|
-
opts.update :timestamp => Time.now.to_i
|
28
|
-
when :hostname
|
29
|
-
@hostname ||= { :hostname => `hostname`.strip }
|
30
|
-
opts.update @hostname
|
31
|
-
when :process
|
32
|
-
@process_id ||= { :process_id => Process.pid,
|
33
|
-
:process_name => $0,
|
34
|
-
:process_parent_id => Process.ppid,
|
35
|
-
:thread_id => Thread.current.object_id }
|
36
|
-
opts.update :process => @process_id
|
37
|
-
else
|
38
|
-
(opts[:tags] ||= []) << tag
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
opts.update(:severity => severity,
|
43
|
-
:data => data)
|
44
|
-
|
45
|
-
print(opts)
|
46
|
-
MQ.queue('logger', :durable => true).publish(Marshal.dump(opts), :persistent => true)
|
47
|
-
opts
|
48
|
-
end
|
49
|
-
alias :method_missing :log
|
50
|
-
|
51
|
-
def print data = nil, &block
|
52
|
-
if block
|
53
|
-
@printer = block
|
54
|
-
elsif data.is_a? Proc
|
55
|
-
@printer = data
|
56
|
-
elsif @printer and data
|
57
|
-
@printer.call(data)
|
58
|
-
else
|
59
|
-
@printer
|
60
|
-
end
|
61
|
-
end
|
62
|
-
alias :printer :print
|
63
|
-
end
|
5
|
+
Logger = MQ::Logger
|
64
6
|
|
65
7
|
EM.run{
|
66
|
-
# AMQP.logging = true
|
67
|
-
# MQ.logging = true
|
68
|
-
|
69
8
|
if ARGV[0] == 'server'
|
70
9
|
|
71
|
-
MQ.queue('logger', :durable => true).subscribe{|msg|
|
10
|
+
MQ.queue('logger').bind(MQ.fanout('logging', :durable => true)).subscribe{|msg|
|
72
11
|
msg = Marshal.load(msg)
|
73
12
|
require 'pp'
|
74
13
|
pp(msg)
|
75
14
|
puts
|
76
15
|
}
|
77
|
-
|
78
|
-
|
16
|
+
|
17
|
+
elsif ARGV[0] == 'client'
|
79
18
|
|
80
19
|
log = Logger.new
|
81
20
|
log.debug 'its working!'
|
@@ -100,6 +39,16 @@ EM.run{
|
|
100
39
|
|
101
40
|
AMQP.stop{ EM.stop_event_loop }
|
102
41
|
|
42
|
+
else
|
43
|
+
|
44
|
+
puts
|
45
|
+
puts "#{$0} <client|server>"
|
46
|
+
puts " client: send logs to message queue"
|
47
|
+
puts " server: read logs from message queue"
|
48
|
+
puts
|
49
|
+
|
50
|
+
EM.stop
|
51
|
+
|
103
52
|
end
|
104
53
|
}
|
105
54
|
|
data/examples/mq/stocks.rb
CHANGED
@@ -7,50 +7,52 @@ EM.run{
|
|
7
7
|
p [ Time.now, *args ]
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
10
|
+
def publish_stock_prices
|
11
|
+
mq = MQ.new
|
12
|
+
EM.add_periodic_timer(1){
|
13
|
+
puts
|
14
|
+
|
15
|
+
{ :appl => 170+rand(1000)/100.0,
|
16
|
+
:msft => 22+rand(500)/100.0
|
17
|
+
}.each do |stock, price|
|
18
|
+
stock = "usd.#{stock}"
|
19
|
+
|
20
|
+
log :publishing, stock, price
|
21
|
+
mq.topic('stocks').publish(price, :key => stock)
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
21
25
|
|
22
|
-
|
23
|
-
|
24
|
-
|
26
|
+
def watch_appl_stock
|
27
|
+
mq = MQ.new
|
28
|
+
mq.queue('apple stock').bind(mq.topic('stocks'), :key => 'usd.appl').subscribe{ |price|
|
25
29
|
log 'apple stock', price
|
26
30
|
}
|
27
|
-
|
31
|
+
end
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
log 'us stock', info.
|
33
|
+
def watch_us_stocks
|
34
|
+
mq = MQ.new
|
35
|
+
mq.queue('us stocks').bind(mq.topic('stocks'), :key => 'usd.*').subscribe{ |info, price|
|
36
|
+
log 'us stock', info.routing_key, price
|
33
37
|
}
|
34
|
-
|
38
|
+
end
|
39
|
+
|
40
|
+
publish_stock_prices
|
41
|
+
watch_appl_stock
|
42
|
+
watch_us_stocks
|
35
43
|
|
36
44
|
}
|
37
45
|
|
38
46
|
__END__
|
39
47
|
|
40
|
-
[
|
41
|
-
[
|
42
|
-
[
|
43
|
-
[
|
44
|
-
[
|
45
|
-
|
46
|
-
[
|
47
|
-
[
|
48
|
-
[
|
49
|
-
[
|
50
|
-
[
|
51
|
-
|
52
|
-
[Thu Jul 17 14:51:09 -0700 2008, :publishing, "stock.usd.appl", 173.94]
|
53
|
-
[Thu Jul 17 14:51:09 -0700 2008, :publishing, "stock.usd.msft", 24.88]
|
54
|
-
[Thu Jul 17 14:51:09 -0700 2008, "apple stock", "173.94"]
|
55
|
-
[Thu Jul 17 14:51:09 -0700 2008, "us stock", "appl", "173.94"]
|
56
|
-
[Thu Jul 17 14:51:09 -0700 2008, "us stock", "msft", "24.88"]
|
48
|
+
[Fri Aug 15 01:39:00 -0700 2008, :publishing, "usd.appl", 173.45]
|
49
|
+
[Fri Aug 15 01:39:00 -0700 2008, :publishing, "usd.msft", 26.98]
|
50
|
+
[Fri Aug 15 01:39:00 -0700 2008, "apple stock", "173.45"]
|
51
|
+
[Fri Aug 15 01:39:00 -0700 2008, "us stock", "usd.appl", "173.45"]
|
52
|
+
[Fri Aug 15 01:39:00 -0700 2008, "us stock", "usd.msft", "26.98"]
|
53
|
+
|
54
|
+
[Fri Aug 15 01:39:01 -0700 2008, :publishing, "usd.appl", 179.72]
|
55
|
+
[Fri Aug 15 01:39:01 -0700 2008, :publishing, "usd.msft", 26.56]
|
56
|
+
[Fri Aug 15 01:39:01 -0700 2008, "apple stock", "179.72"]
|
57
|
+
[Fri Aug 15 01:39:01 -0700 2008, "us stock", "usd.appl", "179.72"]
|
58
|
+
[Fri Aug 15 01:39:01 -0700 2008, "us stock", "usd.msft", "26.56"]
|
data/lib/amqp.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module AMQP
|
2
|
-
VERSION = '0.5.
|
2
|
+
VERSION = '0.5.9'
|
3
3
|
|
4
4
|
DIR = File.expand_path(File.dirname(File.expand_path(__FILE__)))
|
5
5
|
$:.unshift DIR
|
@@ -14,6 +14,8 @@ module AMQP
|
|
14
14
|
class << self
|
15
15
|
@logging = false
|
16
16
|
attr_accessor :logging
|
17
|
+
attr_reader :conn, :closing
|
18
|
+
alias :connection :conn
|
17
19
|
end
|
18
20
|
|
19
21
|
def self.connect *args
|
@@ -22,19 +24,39 @@ module AMQP
|
|
22
24
|
|
23
25
|
def self.settings
|
24
26
|
@settings ||= {
|
27
|
+
# server address
|
28
|
+
:host => '127.0.0.1',
|
29
|
+
:port => PORT,
|
30
|
+
|
31
|
+
# login details
|
25
32
|
:user => 'guest',
|
26
33
|
:pass => 'guest',
|
27
34
|
:vhost => '/',
|
35
|
+
|
36
|
+
# connection timeout
|
37
|
+
:timeout => nil,
|
38
|
+
|
39
|
+
# logging
|
28
40
|
:logging => false
|
29
41
|
}
|
30
42
|
end
|
31
43
|
|
32
|
-
def self.start *args
|
33
|
-
|
44
|
+
def self.start *args, &blk
|
45
|
+
EM.run{
|
46
|
+
@conn ||= connect *args
|
47
|
+
@conn.callback(&blk) if blk
|
48
|
+
@conn
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
class << self
|
53
|
+
alias :run :start
|
34
54
|
end
|
35
55
|
|
36
56
|
def self.stop
|
37
57
|
if @conn
|
58
|
+
puts "Shutting down..."
|
59
|
+
@closing = true
|
38
60
|
@conn.close{
|
39
61
|
yield if block_given?
|
40
62
|
@conn = nil
|
data/lib/amqp/buffer.rb
CHANGED
data/lib/amqp/client.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'amqp/frame'
|
2
2
|
|
3
3
|
module AMQP
|
4
|
-
class Error <
|
4
|
+
class Error < StandardError; end
|
5
5
|
|
6
6
|
module BasicClient
|
7
7
|
def process_frame frame
|
@@ -30,7 +30,7 @@ module AMQP
|
|
30
30
|
|
31
31
|
send Protocol::Connection::Open.new(:virtual_host => @settings[:vhost],
|
32
32
|
:capabilities => '',
|
33
|
-
:insist =>
|
33
|
+
:insist => @settings[:insist])
|
34
34
|
|
35
35
|
when Protocol::Connection::OpenOk
|
36
36
|
succeed(self)
|
@@ -60,21 +60,34 @@ module AMQP
|
|
60
60
|
def initialize opts = {}
|
61
61
|
@settings = opts
|
62
62
|
extend AMQP.client
|
63
|
+
|
64
|
+
@on_disconnect = proc{ raise Error, "Could not connect to server #{opts[:host]}:#{opts[:port]}" }
|
65
|
+
|
66
|
+
timeout @settings[:timeout] if @settings[:timeout]
|
67
|
+
errback{ @on_disconnect.call }
|
63
68
|
end
|
64
69
|
|
65
70
|
def connection_completed
|
66
71
|
log 'connected'
|
72
|
+
@on_disconnect = proc{ raise Error, 'Disconnected from server' }
|
67
73
|
@buf = Buffer.new
|
68
74
|
send_data HEADER
|
69
75
|
send_data [1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4')
|
70
76
|
end
|
71
77
|
|
78
|
+
def unbind
|
79
|
+
log 'disconnected'
|
80
|
+
EM.next_tick{ @on_disconnect.call }
|
81
|
+
end
|
82
|
+
|
72
83
|
def add_channel mq
|
73
|
-
|
74
|
-
|
84
|
+
(@_channel_mutex ||= Mutex.new).synchronize do
|
85
|
+
channels[ key = (channels.keys.max || 0) + 1 ] = mq
|
86
|
+
key
|
87
|
+
end
|
75
88
|
end
|
76
89
|
|
77
|
-
def channels
|
90
|
+
def channels
|
78
91
|
@channels ||= {}
|
79
92
|
end
|
80
93
|
|
@@ -111,8 +124,8 @@ module AMQP
|
|
111
124
|
@on_disconnect = on_disconnect if on_disconnect
|
112
125
|
|
113
126
|
callback{ |c|
|
114
|
-
if c.channels.
|
115
|
-
c.channels.each do |
|
127
|
+
if c.channels.any?
|
128
|
+
c.channels.each do |ch, mq|
|
116
129
|
mq.close
|
117
130
|
end
|
118
131
|
else
|
@@ -123,16 +136,9 @@ module AMQP
|
|
123
136
|
end
|
124
137
|
}
|
125
138
|
end
|
126
|
-
|
127
|
-
def unbind
|
128
|
-
log 'disconnected'
|
129
|
-
end
|
130
139
|
|
131
140
|
def self.connect opts = {}
|
132
141
|
opts = AMQP.settings.merge(opts)
|
133
|
-
opts[:host] ||= '127.0.0.1'
|
134
|
-
opts[:port] ||= PORT
|
135
|
-
|
136
142
|
EM.connect opts[:host], opts[:port], self, opts
|
137
143
|
end
|
138
144
|
|
data/lib/amqp/frame.rb
CHANGED
data/lib/amqp/spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
|
2
|
-
# this file was autogenerated on
|
3
|
-
# using amqp-0.8.json (mtime:
|
2
|
+
# this file was autogenerated on Fri Aug 01 15:08:30 -0700 2008
|
3
|
+
# using amqp-0.8.json (mtime: Fri Aug 01 15:08:22 -0700 2008)
|
4
4
|
#
|
5
5
|
# DO NOT EDIT! (edit protocol/codegen.rb instead, and run `rake codegen`)
|
6
6
|
|
@@ -340,6 +340,8 @@ module AMQP
|
|
340
340
|
class PurgeOk < Method( 31, :purge_ok ); end
|
341
341
|
class Delete < Method( 40, :delete ); end
|
342
342
|
class DeleteOk < Method( 41, :delete_ok ); end
|
343
|
+
class Unbind < Method( 50, :unbind ); end
|
344
|
+
class UnbindOk < Method( 51, :unbind_ok ); end
|
343
345
|
|
344
346
|
class Declare
|
345
347
|
short :ticket
|
@@ -392,6 +394,17 @@ module AMQP
|
|
392
394
|
long :message_count
|
393
395
|
end
|
394
396
|
|
397
|
+
class Unbind
|
398
|
+
short :ticket
|
399
|
+
shortstr :queue
|
400
|
+
shortstr :exchange
|
401
|
+
shortstr :routing_key
|
402
|
+
table :arguments
|
403
|
+
end
|
404
|
+
|
405
|
+
class UnbindOk
|
406
|
+
end
|
407
|
+
|
395
408
|
end
|
396
409
|
|
397
410
|
class Basic
|
data/lib/ext/em.rb
CHANGED
data/lib/mq.rb
CHANGED
@@ -11,7 +11,7 @@ class MQ
|
|
11
11
|
attr_accessor :logging
|
12
12
|
end
|
13
13
|
|
14
|
-
class Error <
|
14
|
+
class Error < StandardError; end
|
15
15
|
end
|
16
16
|
|
17
17
|
class MQ
|
@@ -42,8 +42,8 @@ class MQ
|
|
42
42
|
@body << frame.payload
|
43
43
|
if @body.length >= @header.size
|
44
44
|
@header.properties.update(@method.arguments)
|
45
|
-
@consumer.receive @header, @body
|
46
|
-
@body =
|
45
|
+
@consumer.receive @header, @body if @consumer
|
46
|
+
@body = @header = @consumer = @method = nil
|
47
47
|
end
|
48
48
|
|
49
49
|
when Frame::Method
|
@@ -52,7 +52,8 @@ class MQ
|
|
52
52
|
send Protocol::Access::Request.new(:realm => '/data',
|
53
53
|
:read => true,
|
54
54
|
:write => true,
|
55
|
-
:active => true
|
55
|
+
:active => true,
|
56
|
+
:passive => true)
|
56
57
|
|
57
58
|
when Protocol::Access::RequestOk
|
58
59
|
@ticket = method.ticket
|
@@ -64,31 +65,55 @@ class MQ
|
|
64
65
|
} if @closing
|
65
66
|
succeed
|
66
67
|
|
67
|
-
when Protocol::Basic::
|
68
|
+
when Protocol::Basic::CancelOk
|
69
|
+
if @consumer = consumers[ method.consumer_tag ]
|
70
|
+
@consumer.cancelled
|
71
|
+
else
|
72
|
+
MQ.error "Basic.CancelOk for invalid consumer tag: #{method.consumer_tag}"
|
73
|
+
end
|
74
|
+
|
75
|
+
when Protocol::Queue::DeclareOk
|
76
|
+
queues[ method.queue ].recieve_status method
|
77
|
+
|
78
|
+
when Protocol::Basic::Deliver, Protocol::Basic::GetOk
|
68
79
|
@method = method
|
69
80
|
@header = nil
|
70
81
|
@body = ''
|
71
|
-
@consumer = queues[ method.consumer_tag ]
|
72
82
|
|
83
|
+
if method.is_a? Protocol::Basic::GetOk
|
84
|
+
@consumer = get_queue{|q| q.shift }
|
85
|
+
MQ.error "No pending Basic.GetOk requests" unless @consumer
|
86
|
+
else
|
87
|
+
@consumer = consumers[ method.consumer_tag ]
|
88
|
+
MQ.error "Basic.Deliver for invalid consumer tag: #{method.consumer_tag}" unless @consumer
|
89
|
+
end
|
90
|
+
|
91
|
+
when Protocol::Basic::GetEmpty
|
92
|
+
@consumer = get_queue{|q| q.shift }
|
93
|
+
@consumer.receive nil, nil
|
73
94
|
|
74
95
|
when Protocol::Channel::Close
|
75
|
-
raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
|
96
|
+
raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]} on #{@channel}"
|
76
97
|
|
77
98
|
when Protocol::Channel::CloseOk
|
78
99
|
@closing = false
|
79
100
|
conn.callback{ |c|
|
80
|
-
c.channels.delete
|
81
|
-
c.close
|
101
|
+
c.channels.delete @channel
|
102
|
+
c.close if c.channels.empty?
|
82
103
|
}
|
83
104
|
end
|
84
105
|
end
|
85
106
|
end
|
86
107
|
|
87
|
-
def send
|
88
|
-
data.ticket = @ticket if @ticket and data.respond_to? :ticket
|
108
|
+
def send *args
|
89
109
|
conn.callback{ |c|
|
90
|
-
|
91
|
-
|
110
|
+
(@_send_mutex ||= Mutex.new).synchronize do
|
111
|
+
args.each do |data|
|
112
|
+
data.ticket = @ticket if @ticket and data.respond_to? :ticket=
|
113
|
+
log :sending, data
|
114
|
+
c.send data, :channel => @channel
|
115
|
+
end
|
116
|
+
end
|
92
117
|
}
|
93
118
|
end
|
94
119
|
|
@@ -119,6 +144,16 @@ class MQ
|
|
119
144
|
end
|
120
145
|
end
|
121
146
|
|
147
|
+
# error callback
|
148
|
+
|
149
|
+
def self.error msg = nil, &blk
|
150
|
+
if blk
|
151
|
+
@error_callback = blk
|
152
|
+
else
|
153
|
+
@error_callback.call(msg) if @error_callback and msg
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
122
157
|
# keep track of proxy objects
|
123
158
|
|
124
159
|
def exchanges
|
@@ -129,10 +164,24 @@ class MQ
|
|
129
164
|
@queues ||= {}
|
130
165
|
end
|
131
166
|
|
167
|
+
def get_queue
|
168
|
+
if block_given?
|
169
|
+
(@get_queue_mutex ||= Mutex.new).synchronize{
|
170
|
+
yield( @get_queue ||= [] )
|
171
|
+
}
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
132
175
|
def rpcs
|
133
176
|
@rcps ||= {}
|
134
177
|
end
|
135
178
|
|
179
|
+
# queue objects keyed on their consumer tags
|
180
|
+
|
181
|
+
def consumers
|
182
|
+
@consumers ||= {}
|
183
|
+
end
|
184
|
+
|
136
185
|
private
|
137
186
|
|
138
187
|
def log *args
|
@@ -145,10 +194,11 @@ class MQ
|
|
145
194
|
alias :conn :connection
|
146
195
|
end
|
147
196
|
|
148
|
-
# convenience wrapper for thread-local MQ object
|
197
|
+
# convenience wrapper (read: HACK) for thread-local MQ object
|
149
198
|
|
150
199
|
class MQ
|
151
200
|
def MQ.default
|
201
|
+
# XXX clear this when connection is closed
|
152
202
|
Thread.current[:mq] ||= MQ.new
|
153
203
|
end
|
154
204
|
|
data/lib/mq/exchange.rb
CHANGED
@@ -18,18 +18,31 @@ class MQ
|
|
18
18
|
|
19
19
|
def publish data, opts = {}
|
20
20
|
@mq.callback{
|
21
|
-
|
22
|
-
|
21
|
+
out = []
|
22
|
+
|
23
|
+
out << Protocol::Basic::Publish.new({ :exchange => name,
|
24
|
+
:routing_key => opts.delete(:key) || @key }.merge(opts))
|
23
25
|
|
24
26
|
data = data.to_s
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
out << Protocol::Header.new(Protocol::Basic,
|
29
|
+
data.length, { :content_type => 'application/octet-stream',
|
30
|
+
:delivery_mode => (opts.delete(:persistent) ? 2 : 1),
|
31
|
+
:priority => 0 }.merge(opts))
|
32
|
+
|
33
|
+
out << Frame::Body.new(data)
|
34
|
+
|
35
|
+
@mq.send *out
|
31
36
|
}
|
32
37
|
self
|
33
38
|
end
|
39
|
+
|
40
|
+
def delete opts = {}
|
41
|
+
@mq.callback{
|
42
|
+
@mq.send Protocol::Exchange::Delete.new({ :exchange => name,
|
43
|
+
:nowait => true }.merge(opts))
|
44
|
+
}
|
45
|
+
nil
|
46
|
+
end
|
34
47
|
end
|
35
48
|
end
|
data/lib/mq/logger.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
class MQ
|
2
|
+
class Logger
|
3
|
+
def initialize *args, &block
|
4
|
+
opts = args.pop if args.last.is_a? Hash
|
5
|
+
opts ||= {}
|
6
|
+
|
7
|
+
printer(block) if block
|
8
|
+
|
9
|
+
@prop = opts
|
10
|
+
@tags = ([:timestamp] + args).uniq
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :prop
|
14
|
+
alias :base :prop
|
15
|
+
|
16
|
+
def log severity, *args
|
17
|
+
opts = args.pop if args.last.is_a? Hash and args.size != 1
|
18
|
+
opts ||= {}
|
19
|
+
opts = @prop.clone.update(opts)
|
20
|
+
|
21
|
+
data = args.shift
|
22
|
+
|
23
|
+
data = {:type => :exception,
|
24
|
+
:name => data.class.to_s.intern,
|
25
|
+
:backtrace => data.backtrace,
|
26
|
+
:message => data.message} if data.is_a? Exception
|
27
|
+
|
28
|
+
(@tags + args).each do |tag|
|
29
|
+
tag = tag.to_sym
|
30
|
+
case tag
|
31
|
+
when :timestamp
|
32
|
+
opts.update :timestamp => Time.now
|
33
|
+
when :hostname
|
34
|
+
@hostname ||= { :hostname => `hostname`.strip }
|
35
|
+
opts.update @hostname
|
36
|
+
when :process
|
37
|
+
@process_id ||= { :process_id => Process.pid,
|
38
|
+
:process_name => $0,
|
39
|
+
:process_parent_id => Process.ppid,
|
40
|
+
:thread_id => Thread.current.object_id }
|
41
|
+
opts.update :process => @process_id
|
42
|
+
else
|
43
|
+
(opts[:tags] ||= []) << tag
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.update(:severity => severity,
|
48
|
+
:msg => data)
|
49
|
+
|
50
|
+
print(opts)
|
51
|
+
unless Logger.disabled?
|
52
|
+
MQ.fanout('logging', :durable => true).publish Marshal.dump(opts)
|
53
|
+
end
|
54
|
+
|
55
|
+
opts
|
56
|
+
end
|
57
|
+
alias :method_missing :log
|
58
|
+
|
59
|
+
def print data = nil, &block
|
60
|
+
if block
|
61
|
+
@printer = block
|
62
|
+
elsif data.is_a? Proc
|
63
|
+
@printer = data
|
64
|
+
elsif data
|
65
|
+
(pr = @printer || self.class.printer) and pr.call(data)
|
66
|
+
else
|
67
|
+
@printer
|
68
|
+
end
|
69
|
+
end
|
70
|
+
alias :printer :print
|
71
|
+
|
72
|
+
def self.printer &block
|
73
|
+
@printer = block if block
|
74
|
+
@printer
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.disabled?
|
78
|
+
!!@disabled
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.enable
|
82
|
+
@disabled = false
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.disable
|
86
|
+
@disabled = true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/mq/queue.rb
CHANGED
@@ -21,26 +21,120 @@ class MQ
|
|
21
21
|
}
|
22
22
|
self
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
|
+
def unbind exchange, opts = {}
|
26
|
+
@mq.callback{
|
27
|
+
@mq.send Protocol::Queue::Unbind.new({ :queue => name,
|
28
|
+
:exchange => exchange.respond_to?(:name) ? exchange.name : exchange,
|
29
|
+
:routing_key => opts.delete(:key),
|
30
|
+
:nowait => true }.merge(opts))
|
31
|
+
}
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete opts = {}
|
36
|
+
@mq.callback{
|
37
|
+
@mq.send Protocol::Queue::Delete.new({ :queue => name,
|
38
|
+
:nowait => true }.merge(opts))
|
39
|
+
}
|
40
|
+
@mq.queues.delete @name
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def pop opts = {}, &blk
|
45
|
+
@ack = opts[:no_ack] === false
|
46
|
+
|
47
|
+
@on_pop = blk if blk
|
48
|
+
|
49
|
+
@mq.callback{
|
50
|
+
@mq.send Protocol::Basic::Get.new({ :queue => name,
|
51
|
+
:consumer_tag => name,
|
52
|
+
:no_ack => true,
|
53
|
+
:nowait => true }.merge(opts))
|
54
|
+
@mq.get_queue{ |q|
|
55
|
+
q.push(self)
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
25
62
|
def subscribe opts = {}, &blk
|
63
|
+
@consumer_tag = "#{name}-#{Kernel.rand(999_999_999_999)}"
|
64
|
+
@mq.consumers[@consumer_tag] = self
|
65
|
+
|
66
|
+
raise Error, 'already subscribed to the queue' if @on_msg
|
67
|
+
|
26
68
|
@on_msg = blk
|
69
|
+
@ack = opts[:no_ack] === false
|
70
|
+
|
27
71
|
@mq.callback{
|
28
72
|
@mq.send Protocol::Basic::Consume.new({ :queue => name,
|
29
|
-
:consumer_tag =>
|
73
|
+
:consumer_tag => @consumer_tag,
|
30
74
|
:no_ack => true,
|
31
75
|
:nowait => true }.merge(opts))
|
32
76
|
}
|
33
77
|
self
|
34
78
|
end
|
35
79
|
|
80
|
+
def unsubscribe opts = {}, &blk
|
81
|
+
@on_msg = nil
|
82
|
+
@on_cancel = blk
|
83
|
+
@mq.callback{
|
84
|
+
@mq.send Protocol::Basic::Cancel.new({ :consumer_tag => @consumer_tag }.merge(opts))
|
85
|
+
}
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
36
89
|
def publish data, opts = {}
|
37
90
|
exchange.publish(data, opts)
|
38
91
|
end
|
39
92
|
|
40
93
|
def receive headers, body
|
41
|
-
if
|
42
|
-
|
94
|
+
if AMQP.closing
|
95
|
+
#You don't need this if your using ack, and if you aren't it doesn't do much good either
|
96
|
+
#@mq.callback{
|
97
|
+
# @mq.send Protocol::Basic::Reject.new({
|
98
|
+
# :delivery_tag => headers.properties[:delivery_tag],
|
99
|
+
# :requeue => true
|
100
|
+
# })
|
101
|
+
#}
|
102
|
+
return
|
43
103
|
end
|
104
|
+
|
105
|
+
if cb = (@on_msg || @on_pop)
|
106
|
+
cb.call *(cb.arity == 1 ? [body] : [headers, body])
|
107
|
+
end
|
108
|
+
|
109
|
+
if @ack && headers && !AMQP.closing
|
110
|
+
@mq.callback{
|
111
|
+
@mq.send Protocol::Basic::Ack.new({ :delivery_tag => headers.properties[:delivery_tag] })
|
112
|
+
}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def status opts = {}, &blk
|
117
|
+
@on_status = blk
|
118
|
+
@mq.callback{
|
119
|
+
@mq.send Protocol::Queue::Declare.new({ :queue => name,
|
120
|
+
:passive => true }.merge(opts))
|
121
|
+
}
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
def recieve_status declare_ok
|
126
|
+
if @on_status
|
127
|
+
m, c = declare_ok.message_count, declare_ok.consumer_count
|
128
|
+
@on_status.call *(@on_status.arity == 1 ? [m] : [m, c])
|
129
|
+
@on_status = nil
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def cancelled
|
134
|
+
@on_cancel.call if @on_cancel
|
135
|
+
@on_cancel = @on_msg = nil
|
136
|
+
@mq.consumers.delete @consumer_tag
|
137
|
+
@consumer_tag = nil
|
44
138
|
end
|
45
139
|
|
46
140
|
private
|
data/protocol/amqp-0.8.json
CHANGED
@@ -275,7 +275,18 @@
|
|
275
275
|
"name": "delete"},
|
276
276
|
{"id": 41,
|
277
277
|
"arguments": [{"type": "long", "name": "message-count"}],
|
278
|
-
"name": "delete-ok"}
|
278
|
+
"name": "delete-ok"},
|
279
|
+
{"id": 50,
|
280
|
+
"arguments": [{"type": "short", "name": "ticket"},
|
281
|
+
{"type": "shortstr", "name": "queue"},
|
282
|
+
{"type": "shortstr", "name": "exchange"},
|
283
|
+
{"type": "shortstr", "name": "routing-key"},
|
284
|
+
{"type": "table", "name": "arguments"}],
|
285
|
+
"name": "unbind"},
|
286
|
+
{"id": 51,
|
287
|
+
"arguments": [],
|
288
|
+
"name": "unbind-ok"}
|
289
|
+
],
|
279
290
|
"name": "queue"
|
280
291
|
},
|
281
292
|
{
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tmm1-amqp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aman Gupta
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-09-01 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.12.
|
22
|
+
version: 0.12.2
|
23
23
|
version:
|
24
24
|
description: AMQP client implementation in Ruby/EventMachine
|
25
25
|
email: amqp@tmm1.net
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- lib/ext/em.rb
|
51
51
|
- lib/ext/emfork.rb
|
52
52
|
- lib/mq/exchange.rb
|
53
|
+
- lib/mq/logger.rb
|
53
54
|
- lib/mq/queue.rb
|
54
55
|
- lib/mq/rpc.rb
|
55
56
|
- lib/mq.rb
|