tmm1-amqp 0.6.0 → 0.6.1

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.
@@ -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
@@ -0,0 +1,83 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'amqp'
3
+ s.version = '0.6.1'
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
@@ -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')
@@ -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
 
@@ -99,4 +99,14 @@ module AMQP
99
99
  }
100
100
  end
101
101
  end
102
+
103
+ def self.fork workers
104
+ EM.fork(workers) do
105
+ # clean up globals in the fork
106
+ Thread.current[:mq] = nil
107
+ AMQP.instance_variable_set('@conn', nil)
108
+
109
+ yield
110
+ end
111
+ end
102
112
  end
@@ -65,7 +65,7 @@ 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
69
  end
70
70
 
71
71
  def connection_completed
@@ -158,14 +158,14 @@ module AMQP
158
158
  end
159
159
 
160
160
  unless @reconnecting
161
+ @reconnecting = true
162
+
161
163
  @deferred_status = nil
162
164
  initialize(@settings)
163
165
 
164
166
  mqs = @channels
165
167
  @channels = {}
166
168
  mqs.each{ |_,mq| mq.reset } if mqs
167
-
168
- @reconnecting = true
169
169
  end
170
170
 
171
171
  log 'reconnecting'
@@ -186,4 +186,4 @@ module AMQP
186
186
  puts
187
187
  end
188
188
  end
189
- end
189
+ end
@@ -0,0 +1,99 @@
1
+ require 'amqp/frame'
2
+
3
+ module AMQP
4
+ module Server
5
+ def post_init
6
+ @buf = ''
7
+ @channels = {}
8
+ @started = false
9
+ end
10
+
11
+ def receive_data data
12
+ @buf << data
13
+
14
+ unless @started
15
+ if @buf.size >= 8
16
+ if @buf.slice!(0,8) == "AMQP\001\001\b\000"
17
+ send Protocol::Connection::Start.new(
18
+ 8,
19
+ 0,
20
+ {
21
+ :information => 'Licensed under the Ruby license. See http://github.com/tmm1/amqp',
22
+ :copyright => 'Copyright (c) 2008-2009 Aman Gupta',
23
+ :platform => 'Ruby/EventMachine',
24
+ :version => '0.6.1',
25
+ :product => 'SquirrelMQ'
26
+ },
27
+ 'PLAIN AMQPLAIN',
28
+ 'en_US'
29
+ )
30
+ else
31
+ close_connection
32
+ return
33
+ end
34
+ @started = true
35
+ else
36
+ return
37
+ end
38
+ end
39
+
40
+ while frame = Frame.parse(@buf)
41
+ process_frame frame
42
+ end
43
+ end
44
+
45
+ def process_frame frame
46
+ channel = frame.channel
47
+
48
+ case method = frame.payload
49
+ when Protocol::Connection::StartOk
50
+ @user = method.response[:LOGIN]
51
+ @pass = method.response[:PASSWORD]
52
+ send Protocol::Connection::Tune.new(0, 131072, 0)
53
+
54
+ when Protocol::Connection::TuneOk
55
+ # mnnk
56
+
57
+ when Protocol::Connection::Open
58
+ @vhost = method.virtual_host
59
+ send Protocol::Connection::OpenOk.new('localhost')
60
+
61
+ when Protocol::Channel::Open
62
+ @channels[channel] = []
63
+ send Protocol::Channel::OpenOk.new, :channel => channel
64
+
65
+ when Protocol::Access::Request
66
+ send Protocol::Access::RequestOk.new(101)
67
+ end
68
+ end
69
+
70
+ def send data, opts = {}
71
+ channel = opts[:channel] ||= 0
72
+ data = data.to_frame(channel) unless data.is_a? Frame
73
+ data.channel = channel
74
+
75
+ log 'send', data
76
+ send_data data.to_s
77
+ end
78
+
79
+ def self.start host = 'localhost', port = 5672
80
+ EM.start_server host, port, self
81
+ end
82
+
83
+ private
84
+
85
+ def log *args
86
+ require 'pp'
87
+ pp args
88
+ puts
89
+ end
90
+ end
91
+ end
92
+
93
+ if __FILE__ == $0
94
+ require 'rubygems'
95
+ require 'eventmachine'
96
+ EM.run{
97
+ AMQP::Server.start
98
+ }
99
+ end