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 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
 
@@ -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
@@ -1,74 +1,13 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
+ require 'mq/logger'
3
4
 
4
- class Logger
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)
@@ -7,50 +7,52 @@ EM.run{
7
7
  p [ Time.now, *args ]
8
8
  end
9
9
 
10
- # AMQP.logging = true
11
-
12
- EM.add_periodic_timer(1){
13
- puts
14
-
15
- log :publishing, 'stock.usd.appl', price = 170+rand(1000)/100.0
16
- MQ.topic.publish(price, :key => 'stock.usd.appl', :headers => {:symbol => 'appl'})
17
-
18
- log :publishing, 'stock.usd.msft', price = 22+rand(500)/100.0
19
- MQ.topic.publish(price, :key => 'stock.usd.msft', :headers => {:symbol => 'msft'})
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
- Thread.new{
23
- amq = MQ.new
24
- amq.queue('apple stock').bind(amq.topic, :key => 'stock.usd.appl').subscribe{ |price|
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
- Thread.new{
30
- amq = MQ.new
31
- amq.queue('us stocks').bind(amq.topic, :key => 'stock.usd.*').subscribe{ |info, price|
32
- log 'us stock', info.headers[:symbol], price
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
- [Thu Jul 17 14:51:07 -0700 2008, :publishing, "stock.usd.appl", 170.84]
41
- [Thu Jul 17 14:51:07 -0700 2008, :publishing, "stock.usd.msft", 23.68]
42
- [Thu Jul 17 14:51:07 -0700 2008, "apple stock", "170.84"]
43
- [Thu Jul 17 14:51:07 -0700 2008, "us stock", "appl", "170.84"]
44
- [Thu Jul 17 14:51:07 -0700 2008, "us stock", "msft", "23.68"]
45
-
46
- [Thu Jul 17 14:51:08 -0700 2008, :publishing, "stock.usd.appl", 173.61]
47
- [Thu Jul 17 14:51:08 -0700 2008, :publishing, "stock.usd.msft", 25.8]
48
- [Thu Jul 17 14:51:08 -0700 2008, "apple stock", "173.61"]
49
- [Thu Jul 17 14:51:08 -0700 2008, "us stock", "appl", "173.61"]
50
- [Thu Jul 17 14:51:08 -0700 2008, "us stock", "msft", "25.8"]
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"]
@@ -1,5 +1,5 @@
1
1
  module AMQP
2
- VERSION = '0.5.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
18
+ alias :connection :conn
17
19
  end
18
20
 
19
21
  def self.connect *args
@@ -22,9 +24,19 @@ 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
@@ -41,4 +53,12 @@ module AMQP
41
53
  }
42
54
  end
43
55
  end
56
+
57
+ def self.run *args
58
+ EM.run{
59
+ AMQP.start(*args).callback{
60
+ yield
61
+ }
62
+ }
63
+ end
44
64
  end
@@ -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
+ @on_disconnect.call unless $!
81
+ end
82
+
72
83
  def add_channel mq
73
- channels[ key = (channels.keys.max || 0) + 1 ] = mq
74
- key
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 mq = nil
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.keys.any?
115
- c.channels.each do |_, mq|
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
 
@@ -1,6 +1,6 @@
1
1
 
2
- # this file was autogenerated on Sun Jul 20 04:06:05 -0700 2008
3
- # using amqp-0.8.json (mtime: Wed Jul 16 12:10:42 -0700 2008)
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/mq.rb CHANGED
@@ -43,7 +43,7 @@ class MQ
43
43
  if @body.length >= @header.size
44
44
  @header.properties.update(@method.arguments)
45
45
  @consumer.receive @header, @body
46
- @body = ''
46
+ @body = @header = @consumer = @method = nil
47
47
  end
48
48
 
49
49
  when Frame::Method
@@ -70,25 +70,28 @@ class MQ
70
70
  @body = ''
71
71
  @consumer = queues[ method.consumer_tag ]
72
72
 
73
-
74
73
  when Protocol::Channel::Close
75
- raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
74
+ raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]} on #{@channel}"
76
75
 
77
76
  when Protocol::Channel::CloseOk
78
77
  @closing = false
79
78
  conn.callback{ |c|
80
- c.channels.delete(@channel)
81
- c.close unless c.channels.keys.any?
79
+ c.channels.delete @channel
80
+ c.close if c.channels.empty?
82
81
  }
83
82
  end
84
83
  end
85
84
  end
86
85
 
87
- def send data
88
- data.ticket = @ticket if @ticket and data.respond_to? :ticket
86
+ def send *args
89
87
  conn.callback{ |c|
90
- log :sending, data
91
- c.send data, :channel => @channel
88
+ (@_send_mutex ||= Mutex.new).synchronize do
89
+ args.each do |data|
90
+ data.ticket = @ticket if @ticket and data.respond_to? :ticket=
91
+ log :sending, data
92
+ c.send data, :channel => @channel
93
+ end
94
+ end
92
95
  }
93
96
  end
94
97
 
@@ -145,10 +148,11 @@ class MQ
145
148
  alias :conn :connection
146
149
  end
147
150
 
148
- # convenience wrapper for thread-local MQ object
151
+ # convenience wrapper (read: HACK) for thread-local MQ object
149
152
 
150
153
  class MQ
151
154
  def MQ.default
155
+ # XXX clear this when connection is closed
152
156
  Thread.current[:mq] ||= MQ.new
153
157
  end
154
158
 
@@ -18,16 +18,21 @@ class MQ
18
18
 
19
19
  def publish data, opts = {}
20
20
  @mq.callback{
21
- @mq.send Protocol::Basic::Publish.new({ :exchange => name,
22
- :routing_key => opts.delete(:key) || @key }.merge(opts))
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
- @mq.send Protocol::Header.new(Protocol::Basic,
27
- data.length, { :content_type => 'application/octet-stream',
28
- :delivery_mode => (opts.delete(:persistent) ? 2 : 1),
29
- :priority => 0 }.merge(opts))
30
- @mq.send Frame::Body.new(data)
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
@@ -0,0 +1,75 @@
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
+ MQ.fanout('logging', :durable => true).publish Marshal.dump(opts)
52
+
53
+ opts
54
+ end
55
+ alias :method_missing :log
56
+
57
+ def print data = nil, &block
58
+ if block
59
+ @printer = block
60
+ elsif data.is_a? Proc
61
+ @printer = data
62
+ elsif data
63
+ (pr = @printer || self.class.printer) and pr.call(data)
64
+ else
65
+ @printer
66
+ end
67
+ end
68
+ alias :printer :print
69
+
70
+ def self.printer &block
71
+ @printer = block if block
72
+ @printer
73
+ end
74
+ end
75
+ end
@@ -21,6 +21,25 @@ class MQ
21
21
  }
22
22
  self
23
23
  end
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
24
43
 
25
44
  def subscribe opts = {}, &blk
26
45
  @on_msg = blk
@@ -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: amqp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.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-08-01 00:00:00 -07:00
12
+ date: 2008-09-01 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -51,6 +51,7 @@ files:
51
51
  - lib/ext/em.rb
52
52
  - lib/ext/emfork.rb
53
53
  - lib/mq/exchange.rb
54
+ - lib/mq/logger.rb
54
55
  - lib/mq/queue.rb
55
56
  - lib/mq/rpc.rb
56
57
  - lib/mq.rb