amqp 0.5.9 → 0.6.0
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 +4 -4
- data/examples/mq/logger.rb +12 -2
- data/examples/mq/simple-ack.rb +46 -0
- data/examples/mq/simple-get.rb +43 -0
- data/examples/mq/simple.rb +1 -1
- data/lib/amqp.rb +51 -13
- data/lib/amqp/buffer.rb +4 -4
- data/lib/amqp/client.rb +43 -8
- data/lib/amqp/frame.rb +2 -2
- data/lib/amqp/protocol.rb +25 -0
- data/lib/amqp/spec.rb +3 -2
- data/lib/ext/blankslate.rb +1 -1
- data/lib/ext/em.rb +3 -2
- data/lib/ext/emfork.rb +2 -0
- data/lib/mq.rb +585 -18
- data/lib/mq/exchange.rb +265 -3
- data/lib/mq/header.rb +33 -0
- data/lib/mq/logger.rb +15 -1
- data/lib/mq/queue.rb +363 -7
- data/lib/mq/rpc.rb +54 -0
- data/protocol/codegen.rb +1 -0
- metadata +10 -7
data/README
CHANGED
@@ -51,8 +51,8 @@ AMQP resources:
|
|
51
51
|
|
52
52
|
Servers:
|
53
53
|
RabbitMQ (Rabbit Technologies, Erlang/OTP, MPL) - http://rabbitmq.com
|
54
|
-
ZeroMQ (
|
55
|
-
OpenAMQ (
|
54
|
+
ZeroMQ (iMatix/FastMQ/Intel, C++, GPL3) - http://www.zeromq.org
|
55
|
+
OpenAMQ (iMatix, C, GPL2) - http://openamq.org
|
56
56
|
ActiveMQ (Apache Foundation, Java, apache2) - http://activemq.apache.org
|
57
57
|
|
58
58
|
Steve Vinoski explains AMQP in his column, Towards Integration
|
@@ -67,8 +67,8 @@ AMQP resources:
|
|
67
67
|
ZeroMQ's analysis of the messaging technology market
|
68
68
|
http://www.zeromq.org/whitepapers:market-analysis
|
69
69
|
|
70
|
-
|
71
|
-
http://www.
|
70
|
+
Pieter Hintjens's background to AMQP
|
71
|
+
http://www.openamq.org/doc:amqp-background
|
72
72
|
|
73
73
|
Barry Pederson's py-amqplib
|
74
74
|
http://barryp.org/software/py-amqplib/
|
data/examples/mq/logger.rb
CHANGED
@@ -13,8 +13,8 @@ EM.run{
|
|
13
13
|
pp(msg)
|
14
14
|
puts
|
15
15
|
}
|
16
|
-
|
17
|
-
|
16
|
+
|
17
|
+
elsif ARGV[0] == 'client'
|
18
18
|
|
19
19
|
log = Logger.new
|
20
20
|
log.debug 'its working!'
|
@@ -39,6 +39,16 @@ EM.run{
|
|
39
39
|
|
40
40
|
AMQP.stop{ EM.stop_event_loop }
|
41
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
|
+
|
42
52
|
end
|
43
53
|
}
|
44
54
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + '/../../lib'
|
2
|
+
require 'mq'
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
# For ack to work appropriatly you must shutdown AMQP gracefully,
|
6
|
+
# otherwise all items in your queue will be returned
|
7
|
+
Signal.trap('INT') { AMQP.stop{ EM.stop } }
|
8
|
+
Signal.trap('TERM'){ AMQP.stop{ EM.stop } }
|
9
|
+
|
10
|
+
EM.run do
|
11
|
+
MQ.queue('awesome').publish('Totally rad 1')
|
12
|
+
MQ.queue('awesome').publish('Totally rad 2')
|
13
|
+
MQ.queue('awesome').publish('Totally rad 3')
|
14
|
+
|
15
|
+
i = 0
|
16
|
+
|
17
|
+
# Stopping after the second item was acked will keep the 3rd item in the queue
|
18
|
+
MQ.queue('awesome').subscribe(:ack => true) do |h,m|
|
19
|
+
if (i+=1) == 3
|
20
|
+
puts 'Shutting down...'
|
21
|
+
AMQP.stop{ EM.stop }
|
22
|
+
end
|
23
|
+
|
24
|
+
if AMQP.closing?
|
25
|
+
puts "#{m} (ignored, redelivered later)"
|
26
|
+
else
|
27
|
+
puts m
|
28
|
+
h.ack
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
__END__
|
34
|
+
|
35
|
+
Totally rad 1
|
36
|
+
Totally rad 2
|
37
|
+
Shutting down...
|
38
|
+
Totally rad 3 (ignored, redelivered later)
|
39
|
+
|
40
|
+
When restarted:
|
41
|
+
|
42
|
+
Totally rad 3
|
43
|
+
Totally rad 1
|
44
|
+
Shutting down...
|
45
|
+
Totally rad 2 (ignored, redelivered later)
|
46
|
+
Totally rad 3 (ignored, redelivered later)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + '/../../lib'
|
2
|
+
require 'mq'
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
Signal.trap('INT') { AMQP.stop{ EM.stop } }
|
6
|
+
Signal.trap('TERM'){ AMQP.stop{ EM.stop } }
|
7
|
+
|
8
|
+
AMQP.start do
|
9
|
+
queue = MQ.queue('awesome')
|
10
|
+
|
11
|
+
queue.publish('Totally rad 1')
|
12
|
+
queue.publish('Totally rad 2')
|
13
|
+
EM.add_timer(5){ queue.publish('Totally rad 3') }
|
14
|
+
|
15
|
+
queue.pop{ |msg|
|
16
|
+
unless msg
|
17
|
+
# queue was empty
|
18
|
+
p [Time.now, :queue_empty!]
|
19
|
+
|
20
|
+
# try again in 1 second
|
21
|
+
EM.add_timer(1){ queue.pop }
|
22
|
+
else
|
23
|
+
# process this message
|
24
|
+
p [Time.now, msg]
|
25
|
+
|
26
|
+
# get the next message in the queue
|
27
|
+
queue.pop
|
28
|
+
end
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
__END__
|
33
|
+
|
34
|
+
[Wed Oct 15 15:24:30 -0700 2008, "Totally rad 1"]
|
35
|
+
[Wed Oct 15 15:24:30 -0700 2008, "Totally rad 2"]
|
36
|
+
[Wed Oct 15 15:24:30 -0700 2008, :queue_empty!]
|
37
|
+
[Wed Oct 15 15:24:31 -0700 2008, :queue_empty!]
|
38
|
+
[Wed Oct 15 15:24:32 -0700 2008, :queue_empty!]
|
39
|
+
[Wed Oct 15 15:24:33 -0700 2008, :queue_empty!]
|
40
|
+
[Wed Oct 15 15:24:34 -0700 2008, :queue_empty!]
|
41
|
+
[Wed Oct 15 15:24:35 -0700 2008, "Totally rad 3"]
|
42
|
+
[Wed Oct 15 15:24:35 -0700 2008, :queue_empty!]
|
43
|
+
[Wed Oct 15 15:24:36 -0700 2008, :queue_empty!]
|
data/examples/mq/simple.rb
CHANGED
@@ -5,7 +5,7 @@ require 'pp'
|
|
5
5
|
EM.run do
|
6
6
|
|
7
7
|
# connect to the amqp server
|
8
|
-
connection = AMQP.connect(:host => '
|
8
|
+
connection = AMQP.connect(:host => 'localhost', :logging => false)
|
9
9
|
|
10
10
|
# open a channel on the AMQP connection
|
11
11
|
channel = MQ.new(connection)
|
data/lib/amqp.rb
CHANGED
@@ -14,7 +14,8 @@ module AMQP
|
|
14
14
|
class << self
|
15
15
|
@logging = false
|
16
16
|
attr_accessor :logging
|
17
|
-
attr_reader :conn
|
17
|
+
attr_reader :conn, :closing
|
18
|
+
alias :closing? :closing
|
18
19
|
alias :connection :conn
|
19
20
|
end
|
20
21
|
|
@@ -41,24 +42,61 @@ module AMQP
|
|
41
42
|
}
|
42
43
|
end
|
43
44
|
|
44
|
-
|
45
|
-
|
45
|
+
# Must be called to startup the connection to the AMQP server.
|
46
|
+
#
|
47
|
+
# The method takes several arguments and an optional block.
|
48
|
+
#
|
49
|
+
# This takes any option that is also accepted by EventMachine::connect.
|
50
|
+
# Additionally, there are several AMQP-specific options.
|
51
|
+
#
|
52
|
+
# * :user => String (default 'guest')
|
53
|
+
# The username as defined by the AMQP server.
|
54
|
+
# * :pass => String (default 'guest')
|
55
|
+
# The password for the associated :user as defined by the AMQP server.
|
56
|
+
# * :vhost => String (default '/')
|
57
|
+
# The virtual host as defined by the AMQP server.
|
58
|
+
# * :timeout => Numeric (default nil)
|
59
|
+
# Measured in seconds.
|
60
|
+
# * :logging => true | false (default false)
|
61
|
+
# Toggle the extremely verbose logging of all protocol communications
|
62
|
+
# between the client and the server. Extremely useful for debugging.
|
63
|
+
#
|
64
|
+
# AMQP.start do
|
65
|
+
# # default is to connect to localhost:5672
|
66
|
+
#
|
67
|
+
# # define queues, exchanges and bindings here.
|
68
|
+
# # also define all subscriptions and/or publishers
|
69
|
+
# # here.
|
70
|
+
#
|
71
|
+
# # this block never exits unless EM.stop_event_loop
|
72
|
+
# # is called.
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# Most code will use the MQ api. Any calls to MQ.direct / MQ.fanout /
|
76
|
+
# MQ.topic / MQ.queue will implicitly call #start. In those cases,
|
77
|
+
# it is sufficient to put your code inside of an EventMachine.run
|
78
|
+
# block. See the code examples in MQ for details.
|
79
|
+
#
|
80
|
+
def self.start *args, &blk
|
81
|
+
EM.run{
|
82
|
+
@conn ||= connect *args
|
83
|
+
@conn.callback(&blk) if blk
|
84
|
+
@conn
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
class << self
|
89
|
+
alias :run :start
|
46
90
|
end
|
47
91
|
|
48
92
|
def self.stop
|
49
|
-
if @conn
|
93
|
+
if @conn and not @closing
|
94
|
+
@closing = true
|
50
95
|
@conn.close{
|
51
96
|
yield if block_given?
|
52
97
|
@conn = nil
|
98
|
+
@closing = false
|
53
99
|
}
|
54
100
|
end
|
55
101
|
end
|
56
|
-
|
57
|
-
def self.run *args
|
58
|
-
EM.run{
|
59
|
-
AMQP.start(*args).callback{
|
60
|
-
yield
|
61
|
-
}
|
62
|
-
}
|
63
|
-
end
|
64
|
-
end
|
102
|
+
end
|
data/lib/amqp/buffer.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
if [].map.respond_to? :with_index
|
2
|
-
class Array
|
2
|
+
class Array #:nodoc:
|
3
3
|
def enum_with_index
|
4
4
|
each.with_index
|
5
5
|
end
|
@@ -9,9 +9,9 @@ else
|
|
9
9
|
end
|
10
10
|
|
11
11
|
module AMQP
|
12
|
-
class Buffer
|
13
|
-
class Overflow <
|
14
|
-
class InvalidType <
|
12
|
+
class Buffer #:nodoc: all
|
13
|
+
class Overflow < StandardError; end
|
14
|
+
class InvalidType < StandardError; end
|
15
15
|
|
16
16
|
def initialize data = ''
|
17
17
|
@data = data
|
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,13 +30,14 @@ 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)
|
37
37
|
|
38
38
|
when Protocol::Connection::Close
|
39
|
-
raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
|
39
|
+
# raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
|
40
|
+
STDERR.puts "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
|
40
41
|
|
41
42
|
when Protocol::Connection::CloseOk
|
42
43
|
@on_disconnect.call if @on_disconnect
|
@@ -61,7 +62,7 @@ module AMQP
|
|
61
62
|
@settings = opts
|
62
63
|
extend AMQP.client
|
63
64
|
|
64
|
-
@on_disconnect
|
65
|
+
@on_disconnect ||= proc{ raise Error, "Could not connect to server #{opts[:host]}:#{opts[:port]}" }
|
65
66
|
|
66
67
|
timeout @settings[:timeout] if @settings[:timeout]
|
67
68
|
errback{ @on_disconnect.call }
|
@@ -69,7 +70,11 @@ module AMQP
|
|
69
70
|
|
70
71
|
def connection_completed
|
71
72
|
log 'connected'
|
72
|
-
@on_disconnect = proc{ raise Error, 'Disconnected from server' }
|
73
|
+
# @on_disconnect = proc{ raise Error, 'Disconnected from server' }
|
74
|
+
unless @closing
|
75
|
+
@on_disconnect = method(:reconnect)
|
76
|
+
@reconnecting = false
|
77
|
+
end
|
73
78
|
@buf = Buffer.new
|
74
79
|
send_data HEADER
|
75
80
|
send_data [1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4')
|
@@ -77,7 +82,7 @@ module AMQP
|
|
77
82
|
|
78
83
|
def unbind
|
79
84
|
log 'disconnected'
|
80
|
-
@on_disconnect.call
|
85
|
+
EM.next_tick{ @on_disconnect.call }
|
81
86
|
end
|
82
87
|
|
83
88
|
def add_channel mq
|
@@ -115,13 +120,21 @@ module AMQP
|
|
115
120
|
send_data data.to_s
|
116
121
|
end
|
117
122
|
|
123
|
+
#:stopdoc:
|
118
124
|
# def send_data data
|
119
125
|
# log 'send_data', data
|
120
126
|
# super
|
121
127
|
# end
|
128
|
+
#:startdoc:
|
122
129
|
|
123
130
|
def close &on_disconnect
|
124
|
-
|
131
|
+
if on_disconnect
|
132
|
+
@closing = true
|
133
|
+
@on_disconnect = proc{
|
134
|
+
on_disconnect.call
|
135
|
+
@closing = false
|
136
|
+
}
|
137
|
+
end
|
125
138
|
|
126
139
|
callback{ |c|
|
127
140
|
if c.channels.any?
|
@@ -136,7 +149,29 @@ module AMQP
|
|
136
149
|
end
|
137
150
|
}
|
138
151
|
end
|
139
|
-
|
152
|
+
|
153
|
+
def reconnect force = false
|
154
|
+
if @reconnecting and not force
|
155
|
+
# wait 1 second after first reconnect attempt, in between each subsequent attempt
|
156
|
+
EM.add_timer(1){ reconnect(true) }
|
157
|
+
return
|
158
|
+
end
|
159
|
+
|
160
|
+
unless @reconnecting
|
161
|
+
@deferred_status = nil
|
162
|
+
initialize(@settings)
|
163
|
+
|
164
|
+
mqs = @channels
|
165
|
+
@channels = {}
|
166
|
+
mqs.each{ |_,mq| mq.reset } if mqs
|
167
|
+
|
168
|
+
@reconnecting = true
|
169
|
+
end
|
170
|
+
|
171
|
+
log 'reconnecting'
|
172
|
+
EM.reconnect @settings[:host], @settings[:port], self
|
173
|
+
end
|
174
|
+
|
140
175
|
def self.connect opts = {}
|
141
176
|
opts = AMQP.settings.merge(opts)
|
142
177
|
EM.connect opts[:host], opts[:port], self, opts
|
data/lib/amqp/frame.rb
CHANGED
@@ -3,7 +3,7 @@ require 'amqp/buffer'
|
|
3
3
|
require 'amqp/protocol'
|
4
4
|
|
5
5
|
module AMQP
|
6
|
-
class Frame
|
6
|
+
class Frame #:nodoc: all
|
7
7
|
def initialize payload = nil, channel = 0
|
8
8
|
@channel, @payload = channel, payload
|
9
9
|
end
|
@@ -33,7 +33,7 @@ module AMQP
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
class Invalid <
|
36
|
+
class Invalid < StandardError; end
|
37
37
|
|
38
38
|
class Method
|
39
39
|
def initialize payload = nil, channel = 0
|
data/lib/amqp/protocol.rb
CHANGED
@@ -3,6 +3,7 @@ require 'amqp/buffer'
|
|
3
3
|
|
4
4
|
module AMQP
|
5
5
|
module Protocol
|
6
|
+
#:stopdoc:
|
6
7
|
class Class::Method
|
7
8
|
def initialize *args
|
8
9
|
opts = args.pop if args.last.is_a? Hash
|
@@ -64,6 +65,29 @@ module AMQP
|
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
68
|
+
#:startdoc:
|
69
|
+
#
|
70
|
+
# Contains a properties hash that holds some potentially interesting
|
71
|
+
# information.
|
72
|
+
# * :delivery_mode
|
73
|
+
# 1 equals transient.
|
74
|
+
# 2 equals persistent. Unconsumed persistent messages will survive
|
75
|
+
# a server restart when they are stored in a durable queue.
|
76
|
+
# * :redelivered
|
77
|
+
# True or False
|
78
|
+
# * :routing_key
|
79
|
+
# The routing string used for matching this message to this queue.
|
80
|
+
# * :priority
|
81
|
+
# An integer in the range of 0 to 9 inclusive.
|
82
|
+
# * :content_type
|
83
|
+
# Always "application/octet-stream" (byte stream)
|
84
|
+
# * :exchange
|
85
|
+
# The source exchange which published this message.
|
86
|
+
# * :message_count
|
87
|
+
# The number of unconsumed messages contained in the queue.
|
88
|
+
# * :delivery_tag
|
89
|
+
# A monotonically increasing integer. This number should not be trusted
|
90
|
+
# as a sequence number. There is no guarantee it won't get reset.
|
67
91
|
class Header
|
68
92
|
def initialize *args
|
69
93
|
opts = args.pop if args.last.is_a? Hash
|
@@ -132,6 +156,7 @@ module AMQP
|
|
132
156
|
class_id, method_id = buf.read(:short, :short)
|
133
157
|
classes[class_id].methods[method_id].new(buf)
|
134
158
|
end
|
159
|
+
#:stopdoc:
|
135
160
|
end
|
136
161
|
end
|
137
162
|
|
data/lib/amqp/spec.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
#
|
2
|
+
#:stopdoc:
|
3
|
+
# this file was autogenerated on Sat Jan 03 14:05:57 -0600 2009
|
4
|
+
# using amqp-0.8.json (mtime: Sat Jan 03 08:58:13 -0600 2009)
|
4
5
|
#
|
5
6
|
# DO NOT EDIT! (edit protocol/codegen.rb instead, and run `rake codegen`)
|
6
7
|
|