amqp 0.6.0 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -21,10 +21,14 @@ To use with RabbitMQ, first run the server:
21
21
 
22
22
  To get started, refer to the various bundled examples:
23
23
 
24
- ruby examples/mq/simple.rb # low-level Queue/Exchange api
25
24
  ruby examples/mq/pingpong.rb # 1-1 communication using amq.direct
26
25
  ruby examples/mq/clock.rb # 1-N communication using amq.fanout
27
26
  ruby examples/mq/stocks.rb # 1-subscriber communication using amq.topic
27
+
28
+ ruby examples/mq/multiclock.rb # header based routing (new rabbitmq feature)
29
+ ruby examples/mq/ack.rb # using ack
30
+ ruby examples/mq/pop.rb # pop off messages one at a time
31
+
28
32
  ruby examples/mq/hashtable.rb # simple async rpc layer
29
33
  ruby examples/mq/primes.rb 4 # parallelized prime number generation
30
34
  ruby examples/mq/logger.rb # simple logging api
@@ -32,6 +36,7 @@ To get started, refer to the various bundled examples:
32
36
  For more details into the lower level AMQP client API, run the simple client example:
33
37
 
34
38
  ruby examples/amqp/simple.rb # low-level AMQP api
39
+ ruby examples/mq/internal.rb # low-level Queue/Exchange api
35
40
 
36
41
  Or refer to protocol/doc.txt, which enumerates packets sent between a server and client
37
42
  during a typical session, in both binary and decoded formats.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ task :codegen do
2
+ sh 'ruby protocol/codegen.rb > lib/amqp/spec.rb'
3
+ sh 'ruby lib/amqp/spec.rb'
4
+ end
5
+
6
+ task :spec do
7
+ sh 'bacon lib/amqp.rb'
8
+ end
9
+
10
+ task :gem do
11
+ sh 'gem build *.gemspec'
12
+ end
13
+
14
+ task :pkg => :gem
15
+ task :package => :gem
data/amqp.gemspec ADDED
@@ -0,0 +1,83 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'amqp'
3
+ s.version = '0.6.4'
4
+ s.date = '2009-01-09'
5
+ s.summary = 'AMQP client implementation in Ruby/EventMachine'
6
+ s.email = "amqp@tmm1.net"
7
+ s.homepage = "http://amqp.rubyforge.org/"
8
+ s.description = "AMQP client implementation in Ruby/EventMachine"
9
+ s.has_rdoc = true
10
+ s.rdoc_options = '--include=examples'
11
+
12
+ # ruby -rpp -e' pp `git ls-files`.split("\n").grep(/^(doc|README)/) '
13
+ s.extra_rdoc_files = [
14
+ "README",
15
+ "doc/EXAMPLE_01_PINGPONG",
16
+ "doc/EXAMPLE_02_CLOCK",
17
+ "doc/EXAMPLE_03_STOCKS",
18
+ "doc/EXAMPLE_04_MULTICLOCK",
19
+ "doc/EXAMPLE_05_ACK",
20
+ "doc/EXAMPLE_05_POP",
21
+ "doc/EXAMPLE_06_HASHTABLE"
22
+ ]
23
+
24
+ s.authors = ["Aman Gupta"]
25
+ s.add_dependency('eventmachine', '>= 0.12.4')
26
+
27
+ # ruby -rpp -e' pp `git ls-files`.split("\n") '
28
+ s.files = [
29
+ "README",
30
+ "Rakefile",
31
+ "amqp.gemspec",
32
+ "amqp.todo",
33
+ "doc/EXAMPLE_01_PINGPONG",
34
+ "doc/EXAMPLE_02_CLOCK",
35
+ "doc/EXAMPLE_03_STOCKS",
36
+ "doc/EXAMPLE_04_MULTICLOCK",
37
+ "doc/EXAMPLE_05_ACK",
38
+ "doc/EXAMPLE_05_POP",
39
+ "doc/EXAMPLE_06_HASHTABLE",
40
+ "examples/amqp/simple.rb",
41
+ "examples/mq/ack.rb",
42
+ "examples/mq/clock.rb",
43
+ "examples/mq/pop.rb",
44
+ "examples/mq/hashtable.rb",
45
+ "examples/mq/internal.rb",
46
+ "examples/mq/logger.rb",
47
+ "examples/mq/multiclock.rb",
48
+ "examples/mq/pingpong.rb",
49
+ "examples/mq/primes-simple.rb",
50
+ "examples/mq/primes.rb",
51
+ "examples/mq/stocks.rb",
52
+ "lib/amqp.rb",
53
+ "lib/amqp/buffer.rb",
54
+ "lib/amqp/client.rb",
55
+ "lib/amqp/frame.rb",
56
+ "lib/amqp/protocol.rb",
57
+ "lib/amqp/server.rb",
58
+ "lib/amqp/spec.rb",
59
+ "lib/ext/blankslate.rb",
60
+ "lib/ext/em.rb",
61
+ "lib/ext/emfork.rb",
62
+ "lib/mq.rb",
63
+ "lib/mq/exchange.rb",
64
+ "lib/mq/header.rb",
65
+ "lib/mq/logger.rb",
66
+ "lib/mq/queue.rb",
67
+ "lib/mq/rpc.rb",
68
+ "old/README",
69
+ "old/Rakefile",
70
+ "old/amqp-0.8.json",
71
+ "old/amqp_spec.rb",
72
+ "old/amqpc.rb",
73
+ "old/codegen.rb",
74
+ "protocol/amqp-0.8.json",
75
+ "protocol/amqp-0.8.xml",
76
+ "protocol/codegen.rb",
77
+ "protocol/doc.txt",
78
+ "research/api.rb",
79
+ "research/primes-forked.rb",
80
+ "research/primes-processes.rb",
81
+ "research/primes-threaded.rb"
82
+ ]
83
+ end
data/amqp.todo ADDED
@@ -0,0 +1,32 @@
1
+ - breaks with header values that are nil
2
+ - breaks with header values that are ruby objects (convert to strings?)
3
+ - sending utf8 data in 1.9 breaks
4
+
5
+ - generate amqp/spec.rb from original xml spec
6
+ - add peek and pop to queues
7
+ - use rabbitmq generated consumer tag from basic.consume-ok reply
8
+
9
+ - allow temporary queues with amq.queue(nil) syntax (use uuids)
10
+ - use as temp queue in rpc
11
+ - use uuids for message ids in rpc
12
+
13
+ - add ack/completed responses for messages
14
+ - deleting queues/bindings/exchanges
15
+ + queue.unbind
16
+ - queue.remove or queue.close or queue.delete
17
+ - exchange.remove
18
+ - rpc.remove
19
+
20
+ - handle errors and exceptions
21
+ binding to a non-existent (or not yet created in clock.rb) exchange
22
+ #<AMQP::Protocol::Channel::Close:0x11d35d4
23
+ @class_id=50,
24
+ @debug=1,
25
+ @method_id=20,
26
+ @reply_code=404,
27
+ @reply_text="NOT_FOUND - no exchange 'clock' in vhost '/'">>]
28
+
29
+ - handle connection.redirect during connect (for rabbitmq in distributed mode) [or just set insist to true]
30
+
31
+ - add amq.queue('name').size{ |num| "#{num} messages in the queue" } (send declare passive, look at declare-ok response)
32
+ - clean up MQ.default on disconnect
@@ -0,0 +1,2 @@
1
+ == Ping Pong Example
2
+ :include: mq/pingpong.rb
@@ -0,0 +1,2 @@
1
+ == Clock Example
2
+ :include: mq/clock.rb
@@ -0,0 +1,2 @@
1
+ == Stocks Example
2
+ :include: mq/stocks.rb
@@ -0,0 +1,2 @@
1
+ == Muti-format Clock Example
2
+ :include: mq/multiclock.rb
@@ -0,0 +1,2 @@
1
+ == Ack Example
2
+ :include: mq/ack.rb
@@ -0,0 +1,2 @@
1
+ == Pop Example
2
+ :include: mq/pop.rb
@@ -0,0 +1,2 @@
1
+ == HashTable RPC Example
2
+ :include: mq/hashtable.rb
@@ -1,13 +1,12 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
- require 'pp'
4
3
 
5
- # For ack to work appropriatly you must shutdown AMQP gracefully,
4
+ # For ack to work appropriately you must shutdown AMQP gracefully,
6
5
  # otherwise all items in your queue will be returned
7
6
  Signal.trap('INT') { AMQP.stop{ EM.stop } }
8
7
  Signal.trap('TERM'){ AMQP.stop{ EM.stop } }
9
8
 
10
- EM.run do
9
+ AMQP.start(:host => 'localhost') do
11
10
  MQ.queue('awesome').publish('Totally rad 1')
12
11
  MQ.queue('awesome').publish('Totally rad 2')
13
12
  MQ.queue('awesome').publish('Totally rad 3')
data/examples/mq/clock.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
3
 
4
- EM.run{
4
+ AMQP.start(:host => 'localhost') do
5
5
 
6
6
  def log *args
7
7
  p args
@@ -28,7 +28,7 @@ EM.run{
28
28
  log 'every 5 seconds', :received, time if time.strftime('%S').to_i%5 == 0
29
29
  }
30
30
 
31
- }
31
+ end
32
32
 
33
33
  __END__
34
34
 
@@ -1,7 +1,7 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
3
 
4
- EM.run{
4
+ AMQP.start(:host => 'localhost') do
5
5
 
6
6
  def log *args
7
7
  p args
@@ -37,10 +37,10 @@ EM.run{
37
37
  client.set(:one, 1)
38
38
  client.keys do |res|
39
39
  log 'client', :keys => res
40
- EM.stop_event_loop
40
+ AMQP.stop{ EM.stop }
41
41
  end
42
42
 
43
- }
43
+ end
44
44
 
45
45
  __END__
46
46
 
File without changes
@@ -4,7 +4,7 @@ require 'mq/logger'
4
4
 
5
5
  Logger = MQ::Logger
6
6
 
7
- EM.run{
7
+ AMQP.start(:host => 'localhost') do
8
8
  if ARGV[0] == 'server'
9
9
 
10
10
  MQ.queue('logger').bind(MQ.fanout('logging', :durable => true)).subscribe{|msg|
@@ -37,7 +37,7 @@ EM.run{
37
37
  log = Logger.new(:webserver, :timestamp, :hostname, &log.printer)
38
38
  log.info 'Request for /', :GET, :session => 'abc'
39
39
 
40
- AMQP.stop{ EM.stop_event_loop }
40
+ AMQP.stop{ EM.stop }
41
41
 
42
42
  else
43
43
 
@@ -50,7 +50,7 @@ EM.run{
50
50
  EM.stop
51
51
 
52
52
  end
53
- }
53
+ end
54
54
 
55
55
  __END__
56
56
 
@@ -0,0 +1,49 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../../lib'
2
+ require 'mq'
3
+ require 'time'
4
+
5
+ AMQP.start(:host => 'localhost') do
6
+
7
+ def log *args
8
+ p args
9
+ end
10
+
11
+ #AMQP.logging = true
12
+
13
+ clock = MQ.new.headers('multiformat_clock')
14
+ EM.add_periodic_timer(1){
15
+ puts
16
+
17
+ time = Time.new
18
+ ["iso8601","rfc2822"].each do |format|
19
+ formatted_time = time.send(format)
20
+ log :publish, format, formatted_time
21
+ clock.publish "#{formatted_time}", :headers => {"format" => format}
22
+ end
23
+ }
24
+
25
+ ["iso8601","rfc2822"].each do |format|
26
+ amq = MQ.new
27
+ amq.queue(format.to_s).bind(amq.headers('multiformat_clock'), :arguments => {"format" => format}).subscribe{ |time|
28
+ log "received #{format}", time
29
+ }
30
+ end
31
+
32
+ end
33
+
34
+ __END__
35
+
36
+ [:publish, "iso8601", "2009-02-13T19:55:40-08:00"]
37
+ [:publish, "rfc2822", "Fri, 13 Feb 2009 19:55:40 -0800"]
38
+ ["received iso8601", "2009-02-13T19:55:40-08:00"]
39
+ ["received rfc2822", "Fri, 13 Feb 2009 19:55:40 -0800"]
40
+
41
+ [:publish, "iso8601", "2009-02-13T19:55:41-08:00"]
42
+ [:publish, "rfc2822", "Fri, 13 Feb 2009 19:55:41 -0800"]
43
+ ["received iso8601", "2009-02-13T19:55:41-08:00"]
44
+ ["received rfc2822", "Fri, 13 Feb 2009 19:55:41 -0800"]
45
+
46
+ [:publish, "iso8601", "2009-02-13T19:55:42-08:00"]
47
+ [:publish, "rfc2822", "Fri, 13 Feb 2009 19:55:42 -0800"]
48
+ ["received iso8601", "2009-02-13T19:55:42-08:00"]
49
+ ["received rfc2822", "Fri, 13 Feb 2009 19:55:42 -0800"]
@@ -1,7 +1,7 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
3
 
4
- EM.run{
4
+ AMQP.start(:host => 'localhost') do
5
5
 
6
6
  def log *args
7
7
  p [ Time.now, *args ]
@@ -28,7 +28,7 @@ EM.run{
28
28
  log 'two', :received, msg
29
29
  }
30
30
 
31
- }
31
+ end
32
32
 
33
33
  __END__
34
34
 
File without changes
@@ -11,7 +11,7 @@ end
11
11
 
12
12
  # spawn workers
13
13
  workers = ARGV[0] ? (Integer(ARGV[0]) rescue 1) : 1
14
- EM.fork(workers) do
14
+ AMQP.fork(workers) do
15
15
 
16
16
  log MQ.id, :started
17
17
 
@@ -33,7 +33,7 @@ EM.fork(workers) do
33
33
  end
34
34
 
35
35
  # use workers to check which numbers are prime
36
- EM.run{
36
+ AMQP.start(:host => 'localhost') do
37
37
 
38
38
  prime_checker = MQ.rpc('prime checker')
39
39
 
@@ -52,7 +52,7 @@ EM.run{
52
52
 
53
53
  end
54
54
 
55
- }
55
+ end
56
56
 
57
57
  __END__
58
58
 
@@ -1,7 +1,7 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
3
 
4
- EM.run{
4
+ AMQP.start(:host => 'localhost') do
5
5
 
6
6
  def log *args
7
7
  p [ Time.now, *args ]
@@ -41,7 +41,7 @@ EM.run{
41
41
  watch_appl_stock
42
42
  watch_us_stocks
43
43
 
44
- }
44
+ end
45
45
 
46
46
  __END__
47
47
 
data/lib/amqp.rb CHANGED
@@ -38,7 +38,10 @@ module AMQP
38
38
  :timeout => nil,
39
39
 
40
40
  # logging
41
- :logging => false
41
+ :logging => false,
42
+
43
+ # ssl
44
+ :ssl => false
42
45
  }
43
46
  end
44
47
 
@@ -99,4 +102,14 @@ module AMQP
99
102
  }
100
103
  end
101
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
102
115
  end
data/lib/amqp/client.rb CHANGED
@@ -9,7 +9,7 @@ module AMQP
9
9
  mq.process_frame(frame)
10
10
  return
11
11
  end
12
-
12
+
13
13
  case frame
14
14
  when Frame::Method
15
15
  case method = frame.payload
@@ -49,7 +49,7 @@ module AMQP
49
49
  def self.client
50
50
  @client ||= BasicClient
51
51
  end
52
-
52
+
53
53
  def self.client= mod
54
54
  mod.__send__ :include, AMQP
55
55
  @client = mod
@@ -65,23 +65,35 @@ module AMQP
65
65
  @on_disconnect ||= proc{ raise Error, "Could not connect to server #{opts[:host]}:#{opts[:port]}" }
66
66
 
67
67
  timeout @settings[:timeout] if @settings[:timeout]
68
- errback{ @on_disconnect.call }
68
+ errback{ @on_disconnect.call } unless @reconnecting
69
+
70
+ @connected = false
69
71
  end
70
72
 
71
73
  def connection_completed
74
+ start_tls if @settings[:ssl]
72
75
  log 'connected'
73
76
  # @on_disconnect = proc{ raise Error, 'Disconnected from server' }
74
77
  unless @closing
75
- @on_disconnect = method(:reconnect)
78
+ @on_disconnect = method(:disconnected)
76
79
  @reconnecting = false
77
80
  end
81
+
82
+ @connected = true
83
+ @connection_status.call(:connected) if @connection_status
84
+
78
85
  @buf = Buffer.new
79
86
  send_data HEADER
80
87
  send_data [1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4')
81
88
  end
82
89
 
90
+ def connected?
91
+ @connected
92
+ end
93
+
83
94
  def unbind
84
95
  log 'disconnected'
96
+ @connected = false
85
97
  EM.next_tick{ @on_disconnect.call }
86
98
  end
87
99
 
@@ -95,7 +107,7 @@ module AMQP
95
107
  def channels
96
108
  @channels ||= {}
97
109
  end
98
-
110
+
99
111
  def receive_data data
100
112
  # log 'receive_data', data
101
113
  @buf << data
@@ -110,7 +122,7 @@ module AMQP
110
122
  # this is a stub meant to be
111
123
  # replaced by the module passed into initialize
112
124
  end
113
-
125
+
114
126
  def send data, opts = {}
115
127
  channel = opts[:channel] ||= 0
116
128
  data = data.to_frame(channel) unless data.is_a? Frame
@@ -158,14 +170,14 @@ module AMQP
158
170
  end
159
171
 
160
172
  unless @reconnecting
173
+ @reconnecting = true
174
+
161
175
  @deferred_status = nil
162
176
  initialize(@settings)
163
177
 
164
178
  mqs = @channels
165
179
  @channels = {}
166
180
  mqs.each{ |_,mq| mq.reset } if mqs
167
-
168
- @reconnecting = true
169
181
  end
170
182
 
171
183
  log 'reconnecting'
@@ -176,9 +188,18 @@ module AMQP
176
188
  opts = AMQP.settings.merge(opts)
177
189
  EM.connect opts[:host], opts[:port], self, opts
178
190
  end
179
-
191
+
192
+ def connection_status &blk
193
+ @connection_status = blk
194
+ end
195
+
180
196
  private
181
-
197
+
198
+ def disconnected
199
+ @connection_status.call(:disconnected) if @connection_status
200
+ reconnect
201
+ end
202
+
182
203
  def log *args
183
204
  return unless @settings[:logging] or AMQP.logging
184
205
  require 'pp'
@@ -186,4 +207,4 @@ module AMQP
186
207
  puts
187
208
  end
188
209
  end
189
- end
210
+ end