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