amqp 0.5.0 → 0.5.1

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 CHANGED
@@ -1,4 +1,7 @@
1
- Simple AMQP client for Ruby/EventMachine.
1
+ Simple AMQP driver for Ruby/EventMachine.
2
+
3
+ This library was tested primarily with RabbitMQ, although it should be compatible with any
4
+ server implementing the AMQP 0-8 spec.
2
5
 
3
6
  To use with RabbitMQ, first run the server:
4
7
 
@@ -7,11 +10,22 @@ To use with RabbitMQ, first run the server:
7
10
  cd rabbitmq-server
8
11
  make run
9
12
 
10
- Then run the example client:
13
+ To get started, refer to the various bundled examples:
14
+
15
+ ruby examples/pingpong.rb # 1-1 communication using amq.direct
16
+ ruby examples/clock.rb # 1-N communication using amq.fanout
17
+ ruby examples/stocks.rb # 1-subscriber communication using amq.topic
18
+ ruby examples/hashtable.rb # simple async rpc layer
19
+ ruby examples/primes.rb 4 # parallelized prime number generation
20
+
21
+ For more details into the lower level AMQP client API, run the simple client example:
11
22
 
12
23
  ruby examples/simple.rb
13
24
 
14
- To run the specs:
25
+ Or refer to protocol/doc.txt, which enumerates packets sent between a server and client
26
+ during a typical session, in both binary and decoded formats.
27
+
28
+ To run the test suite:
15
29
 
16
30
  rake spec
17
31
 
@@ -24,7 +38,25 @@ Special thanks to Dmitriy Samovskiy, Ben Hood and Tony Garnock-Jones.
24
38
 
25
39
  Other AMQP resources:
26
40
 
27
- Barry Pederson's py-amqplib: http://barryp.org/software/py-amqplib/
28
- Ben Hood's article on writing an AMQP article: http://hopper.squarespace.com/blog/2008/6/21/build-your-own-amqp-client.html
29
- Dmitriy Samovskiy's introduction to ruby+rabbitmq: http://somic-org.homelinux.org/blog/2008/06/24/ruby-amqp-rabbitmq-example/
30
- Ben Hood's AMQP client in AS3: http://github.com/0x6e6562/as3-amqp
41
+ Servers:
42
+ RabbitMQ (Rabbit Technologies, Erlang/OTP, MPL) http://rabbitmq.com
43
+ OpenAMP (iMatrix, C, GPL2) http://openamq.org
44
+ ActiveMQ (Apache Foundation, Java, apache2) http://activemq.apache.org/
45
+
46
+ John O'Hara's article on the history of AMQP:
47
+ http://www.acmqueue.org/modules.php?name=Content&pa=showpage&pid=485
48
+
49
+ Barry Pederson's py-amqplib:
50
+ http://barryp.org/software/py-amqplib/
51
+
52
+ Ben Hood's article on writing an AMQP client:
53
+ http://hopper.squarespace.com/blog/2008/6/21/build-your-own-amqp-client.html
54
+
55
+ Dmitriy Samovskiy's introduction to Ruby+QPid+RabbitMQ:
56
+ http://somic-org.homelinux.org/blog/2008/06/24/ruby-amqp-rabbitmq-example/
57
+
58
+ Ben Hood's AMQP client in AS3:
59
+ http://github.com/0x6e6562/as3-amqp
60
+
61
+ RabbitMQ's protocol code generator:
62
+ http://hg.rabbitmq.com/rabbitmq-codegen/
@@ -9,18 +9,21 @@ EM.run{
9
9
 
10
10
  # AMQP.logging = true
11
11
 
12
+ clock = MQ.new.fanout('clock')
12
13
  EM.add_periodic_timer(1){
13
14
  puts
14
15
 
15
16
  log :publishing, time = Time.now
16
- MQ.new.fanout('clock').publish(Marshal.dump(time))
17
+ clock.publish(Marshal.dump(time))
17
18
  }
18
19
 
19
- MQ.new.queue('every second').bind('clock').subscribe{ |time|
20
+ amq = MQ.new
21
+ amq.queue('every second').bind(amq.fanout('clock')).subscribe{ |time|
20
22
  log 'every second', :received, Marshal.load(time)
21
23
  }
22
24
 
23
- MQ.new.queue('every 5 seconds').bind('clock').subscribe{ |time|
25
+ amq = MQ.new
26
+ amq.queue('every 5 seconds').bind(amq.fanout('clock')).subscribe{ |time|
24
27
  time = Marshal.load(time)
25
28
  log 'every 5 seconds', :received, time if time.strftime('%S').to_i%5 == 0
26
29
  }
@@ -10,44 +10,36 @@ EM.run{
10
10
  # AMQP.logging = true
11
11
 
12
12
  amq = MQ.new
13
- amq.queue('one').subscribe{ |headers, msg|
14
- log 'one', :received, msg, :from => headers.reply_to
13
+ EM.add_periodic_timer(1){
14
+ puts
15
15
 
16
- if headers.reply_to
17
- msg[1] = 'o'
16
+ log :sending, 'ping'
17
+ amq.queue('one').publish('ping')
18
+ }
18
19
 
19
- log 'one', :sending, msg, :to => headers.reply_to
20
- amq.direct.publish(msg, :key => headers.reply_to)
21
- else
22
- puts
23
- end
20
+ amq = MQ.new
21
+ amq.queue('one').subscribe{ |msg|
22
+ log 'one', :received, msg, :sending, 'pong'
23
+ amq.queue('two').publish('pong')
24
24
  }
25
25
 
26
26
  amq = MQ.new
27
27
  amq.queue('two').subscribe{ |msg|
28
28
  log 'two', :received, msg
29
- puts
30
- }
31
-
32
- amq = MQ.new
33
- amq.direct.publish('ding', :key => 'one')
34
- EM.add_periodic_timer(1){
35
- log :sending, 'ping', :to => 'one', :from => 'two'
36
- amq.direct.publish('ping', :key => 'one', :reply_to => 'two')
37
29
  }
38
30
 
39
31
  }
40
32
 
41
33
  __END__
42
34
 
43
- [Thu Jul 17 21:23:55 -0700 2008, "one", :received, "ding", {:from=>nil}]
35
+ [Sun Jul 20 03:52:24 -0700 2008, :sending, "ping"]
36
+ [Sun Jul 20 03:52:24 -0700 2008, "one", :received, "ping", :sending, "pong"]
37
+ [Sun Jul 20 03:52:24 -0700 2008, "two", :received, "pong"]
44
38
 
45
- [Thu Jul 17 21:23:56 -0700 2008, :sending, "ping", {:from=>"two", :to=>"one"}]
46
- [Thu Jul 17 21:23:56 -0700 2008, "one", :received, "ping", {:from=>"two"}]
47
- [Thu Jul 17 21:23:56 -0700 2008, "one", :sending, "pong", {:to=>"two"}]
48
- [Thu Jul 17 21:23:56 -0700 2008, "two", :received, "pong"]
39
+ [Sun Jul 20 03:52:25 -0700 2008, :sending, "ping"]
40
+ [Sun Jul 20 03:52:25 -0700 2008, "one", :received, "ping", :sending, "pong"]
41
+ [Sun Jul 20 03:52:25 -0700 2008, "two", :received, "pong"]
49
42
 
50
- [Thu Jul 17 21:23:57 -0700 2008, :sending, "ping", {:from=>"two", :to=>"one"}]
51
- [Thu Jul 17 21:23:57 -0700 2008, "one", :received, "ping", {:from=>"two"}]
52
- [Thu Jul 17 21:23:57 -0700 2008, "one", :sending, "pong", {:to=>"two"}]
53
- [Thu Jul 17 21:23:57 -0700 2008, "two", :received, "pong"]
43
+ [Sun Jul 20 03:52:26 -0700 2008, :sending, "ping"]
44
+ [Sun Jul 20 03:52:26 -0700 2008, "one", :received, "ping", :sending, "pong"]
45
+ [Sun Jul 20 03:52:26 -0700 2008, "two", :received, "pong"]
@@ -0,0 +1,63 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'mq'
3
+
4
+ MAX = 5000
5
+
6
+ def EM.fork &blk
7
+ raise if reactor_running?
8
+
9
+ unless @forks
10
+ at_exit{
11
+ @forks.each{ |pid| Process.kill('KILL', pid) }
12
+ }
13
+ end
14
+
15
+ (@forks ||= []) << Kernel.fork do
16
+ EM.run(&blk)
17
+ end
18
+ end
19
+
20
+ def log *args
21
+ p args
22
+ end
23
+
24
+ # MQ.logging = true
25
+
26
+ # worker
27
+
28
+ workers = ARGV[0] ? (Integer(ARGV[0]) rescue 2) : 2
29
+
30
+ workers.times do
31
+ EM.fork{
32
+ log "prime checker", Process.pid, :started
33
+
34
+ class Fixnum
35
+ def prime?
36
+ ('1' * self) !~ /^1?$|^(11+?)\1+$/
37
+ end
38
+ end
39
+
40
+ MQ.queue('prime checker').subscribe{ |info, num|
41
+ log "prime checker #{Process.pid}", :prime?, num
42
+ if Integer(num).prime?
43
+ MQ.queue(info.reply_to).publish(num, :reply_to => Process.pid)
44
+ end
45
+ }
46
+ }
47
+ end
48
+
49
+ # controller
50
+
51
+ EM.run{
52
+ MQ.queue('prime collector').subscribe{ |info, prime|
53
+ log 'prime collector', :received, prime, :from, info.reply_to
54
+ (@primes ||= []) << Integer(prime)
55
+ EM.stop_event_loop if prime == '499'
56
+ }
57
+
58
+ MAX.times do |i|
59
+ EM.next_tick do
60
+ MQ.queue('prime checker').publish((i+1).to_s, :reply_to => 'prime collector')
61
+ end
62
+ end
63
+ }
@@ -0,0 +1,135 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'mq'
3
+
4
+ MAX = 500
5
+
6
+ EM.run{
7
+
8
+ def log *args
9
+ p args
10
+ end
11
+
12
+ # MQ.logging = true
13
+
14
+ if ARGV[0] == 'worker'
15
+
16
+ log "prime checker #{Process.pid}", :started
17
+
18
+ class Fixnum
19
+ def prime?
20
+ ('1' * self) !~ /^1?$|^(11+?)\1+$/
21
+ end
22
+ end
23
+
24
+ MQ.queue('prime checker').subscribe{ |info, num|
25
+ log "prime checker #{Process.pid}", :prime?, num
26
+ if Integer(num).prime?
27
+ MQ.queue(info.reply_to).publish(num, :reply_to => Process.pid)
28
+ EM.stop_event_loop if num == (MAX-1).to_s
29
+ end
30
+ }
31
+
32
+ elsif ARGV[0] == 'controller'
33
+
34
+ MQ.queue('prime collector').subscribe{ |info, prime|
35
+ log 'prime collector', :received, prime, :from, info.reply_to
36
+ (@primes ||= []) << Integer(prime)
37
+ }
38
+
39
+ MAX.times do |i|
40
+ EM.next_tick do
41
+ MQ.queue('prime checker').publish((i+1).to_s, :reply_to => 'prime collector')
42
+ end
43
+ end
44
+
45
+ else # run n workers and 1 controller as an example
46
+
47
+ workers = ARGV[0] ? (Integer(ARGV[0]) rescue 2) : 2
48
+
49
+ ([ :worker ] * workers + [ :controller ]).each do |type|
50
+ log :spawning, "`ruby #{$0} #{type}`"
51
+
52
+ EM.popen("ruby #{$0} #{type}") do |c|
53
+ def c.receive_data data
54
+ puts data
55
+ end
56
+
57
+ def c.unbind
58
+ EM.stop_event_loop
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ }
65
+
66
+ __END__
67
+
68
+ ["prime checker", :started, {:pid=>1958}]
69
+ ["prime checker", :started, {:pid=>1957}]
70
+ ["prime checker", :started, {:pid=>1956}]
71
+ ["prime checker", :prime?, "1", {:pid=>1958}]
72
+ ["prime checker", :prime?, "2", {:pid=>1957}]
73
+ ["prime collector", :received, "2", {:from_pid=>"1957"}]
74
+ ["prime checker", :prime?, "3", {:pid=>1956}]
75
+ ["prime collector", :received, "3", {:from_pid=>"1956"}]
76
+ ["prime checker", :prime?, "4", {:pid=>1958}]
77
+ ["prime checker", :prime?, "5", {:pid=>1957}]
78
+ ["prime collector", :received, "5", {:from_pid=>"1957"}]
79
+ ["prime checker", :prime?, "6", {:pid=>1956}]
80
+ ["prime checker", :prime?, "7", {:pid=>1958}]
81
+ ["prime collector", :received, "7", {:from_pid=>"1958"}]
82
+ ["prime checker", :prime?, "8", {:pid=>1957}]
83
+ ["prime checker", :prime?, "9", {:pid=>1956}]
84
+ ["prime checker", :prime?, "10", {:pid=>1958}]
85
+ ["prime checker", :prime?, "11", {:pid=>1957}]
86
+ ["prime collector", :received, "11", {:from_pid=>"1957"}]
87
+ ["prime checker", :prime?, "12", {:pid=>1956}]
88
+ ["prime checker", :prime?, "13", {:pid=>1958}]
89
+ ["prime collector", :received, "13", {:from_pid=>"1958"}]
90
+ ["prime checker", :prime?, "14", {:pid=>1957}]
91
+ ["prime checker", :prime?, "15", {:pid=>1956}]
92
+ ["prime checker", :prime?, "16", {:pid=>1958}]
93
+ ["prime checker", :prime?, "17", {:pid=>1957}]
94
+ ["prime collector", :received, "17", {:from_pid=>"1957"}]
95
+ ["prime checker", :prime?, "18", {:pid=>1956}]
96
+ ["prime checker", :prime?, "19", {:pid=>1958}]
97
+ ["prime collector", :received, "19", {:from_pid=>"1958"}]
98
+ ["prime checker", :prime?, "20", {:pid=>1957}]
99
+ ["prime checker", :prime?, "21", {:pid=>1956}]
100
+ ["prime checker", :prime?, "22", {:pid=>1958}]
101
+ ["prime checker", :prime?, "23", {:pid=>1957}]
102
+ ["prime collector", :received, "23", {:from_pid=>"1957"}]
103
+ ["prime checker", :prime?, "24", {:pid=>1956}]
104
+ ["prime checker", :prime?, "25", {:pid=>1958}]
105
+ ["prime checker", :prime?, "26", {:pid=>1957}]
106
+ ["prime checker", :prime?, "27", {:pid=>1956}]
107
+ ["prime checker", :prime?, "28", {:pid=>1958}]
108
+ ["prime checker", :prime?, "29", {:pid=>1957}]
109
+ ["prime collector", :received, "29", {:from_pid=>"1957"}]
110
+ ["prime checker", :prime?, "30", {:pid=>1956}]
111
+ ["prime checker", :prime?, "31", {:pid=>1958}]
112
+ ["prime collector", :received, "31", {:from_pid=>"1958"}]
113
+ ["prime checker", :prime?, "32", {:pid=>1957}]
114
+ ["prime checker", :prime?, "33", {:pid=>1956}]
115
+ ["prime checker", :prime?, "34", {:pid=>1958}]
116
+ ["prime checker", :prime?, "35", {:pid=>1957}]
117
+ ["prime checker", :prime?, "36", {:pid=>1956}]
118
+ ["prime checker", :prime?, "37", {:pid=>1958}]
119
+ ["prime collector", :received, "37", {:from_pid=>"1958"}]
120
+ ["prime checker", :prime?, "38", {:pid=>1957}]
121
+ ["prime checker", :prime?, "39", {:pid=>1956}]
122
+ ["prime checker", :prime?, "40", {:pid=>1958}]
123
+ ["prime checker", :prime?, "41", {:pid=>1957}]
124
+ ["prime collector", :received, "41", {:from_pid=>"1957"}]
125
+ ["prime checker", :prime?, "42", {:pid=>1956}]
126
+ ["prime checker", :prime?, "43", {:pid=>1958}]
127
+ ["prime collector", :received, "43", {:from_pid=>"1958"}]
128
+ ["prime checker", :prime?, "44", {:pid=>1957}]
129
+ ["prime checker", :prime?, "45", {:pid=>1956}]
130
+ ["prime checker", :prime?, "46", {:pid=>1958}]
131
+ ["prime checker", :prime?, "47", {:pid=>1957}]
132
+ ["prime collector", :received, "47", {:from_pid=>"1957"}]
133
+ ["prime checker", :prime?, "48", {:pid=>1956}]
134
+ ["prime checker", :prime?, "49", {:pid=>1958}]
135
+ ["prime checker", :prime?, "50", {:pid=>1957}]
@@ -0,0 +1,19 @@
1
+ MAX = 1000
2
+
3
+ class Fixnum
4
+ def prime?
5
+ ('1' * self) !~ /^1?$|^(11+?)\1+$/
6
+ end
7
+ end
8
+
9
+ class PrimeChecker
10
+ def is_prime? number
11
+ number.prime?
12
+ end
13
+ end
14
+
15
+ prime_checker = PrimeChecker.new
16
+
17
+ (10_000...(10_000+MAX)).each do |n|
18
+ prime_checker.is_prime? n
19
+ end
@@ -0,0 +1,49 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'mq'
3
+
4
+ MAX = 500
5
+
6
+ def log *args
7
+ p args
8
+ end
9
+
10
+ # MQ.logging = true
11
+
12
+ EM.run{
13
+
14
+ # worker
15
+
16
+ log "prime checker", Process.pid, :started
17
+
18
+ class Fixnum
19
+ def prime?
20
+ ('1' * self) !~ /^1?$|^(11+?)\1+$/
21
+ end
22
+ end
23
+
24
+ MQ.queue('prime checker').subscribe{ |info, num|
25
+ EM.defer(proc{
26
+
27
+ log "prime checker #{Process.pid}-#{Thread.current.object_id}", :prime?, num
28
+ if Integer(num).prime?
29
+ MQ.queue(info.reply_to).publish(num, :reply_to => "#{Process.pid}-#{Thread.current.object_id}")
30
+ EM.stop_event_loop if num == '499'
31
+ end
32
+
33
+ })
34
+ }
35
+
36
+ # controller
37
+
38
+ MQ.queue('prime collector').subscribe{ |info, prime|
39
+ log 'prime collector', :received, prime, :from, info.reply_to
40
+ (@primes ||= []) << Integer(prime)
41
+ }
42
+
43
+ MAX.times do |i|
44
+ EM.next_tick do
45
+ MQ.queue('prime checker').publish((i+1).to_s, :reply_to => 'prime collector')
46
+ end
47
+ end
48
+
49
+ }
@@ -0,0 +1,99 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'mq'
3
+
4
+ # check MAX numbers for prime-ness
5
+ MAX = 1000
6
+
7
+ # logging
8
+ def log *args
9
+ p args
10
+ end
11
+
12
+ # spawn workers
13
+ workers = ARGV[0] ? (Integer(ARGV[0]) rescue 1) : 1
14
+ EM.fork(workers) do
15
+
16
+ log MQ.id, :started
17
+
18
+ class Fixnum
19
+ def prime?
20
+ ('1' * self) !~ /^1?$|^(11+?)\1+$/
21
+ end
22
+ end
23
+
24
+ class PrimeChecker
25
+ def is_prime? number
26
+ log "prime checker #{MQ.id}", :prime?, number
27
+ number.prime?
28
+ end
29
+ end
30
+
31
+ MQ.rpc('prime checker', PrimeChecker.new)
32
+
33
+ end
34
+
35
+ # use workers to check which numbers are prime
36
+ EM.run{
37
+
38
+ prime_checker = MQ.rpc('prime checker')
39
+
40
+ (10_000...(10_000+MAX)).each do |num|
41
+ log :checking, num
42
+
43
+ prime_checker.is_prime?(num) { |is_prime|
44
+ log :prime?, num, is_prime
45
+ (@primes||=[]) << num if is_prime
46
+
47
+ if (@responses = (@responses || 0) + 1) == MAX
48
+ log :primes=, @primes
49
+ EM.stop_event_loop
50
+ end
51
+ }
52
+
53
+ end
54
+
55
+ }
56
+
57
+ __END__
58
+
59
+ $ uname -a
60
+ Linux gc 2.6.24-ARCH #1 SMP PREEMPT Sun Mar 30 10:50:22 CEST 2008 x86_64 Intel(R) Xeon(R) CPU X3220 @ 2.40GHz GenuineIntel GNU/Linux
61
+
62
+ $ cat /proc/cpuinfo | grep processor | wc -l
63
+ 4
64
+
65
+ $ time ruby primes-simple.rb
66
+
67
+ real 0m16.055s
68
+ user 0m16.052s
69
+ sys 0m0.000s
70
+
71
+ $ time ruby primes.rb 1 >/dev/null
72
+
73
+ real 0m18.278s
74
+ user 0m0.993s
75
+ sys 0m0.027s
76
+
77
+ $ time ruby primes.rb 2 >/dev/null
78
+
79
+ real 0m17.316s
80
+ user 0m0.967s
81
+ sys 0m0.053s
82
+
83
+ $ time ruby primes.rb 4 >/dev/null
84
+
85
+ real 0m8.229s
86
+ user 0m1.010s
87
+ sys 0m0.030s
88
+
89
+ $ time ruby primes.rb 8 >/dev/null
90
+
91
+ real 0m5.893s
92
+ user 0m1.023s
93
+ sys 0m0.050s
94
+
95
+ $ time ruby primes.rb 16 >/dev/null
96
+
97
+ real 0m5.601s
98
+ user 0m0.990s
99
+ sys 0m0.043s
@@ -13,7 +13,7 @@ EM.run{
13
13
  puts
14
14
 
15
15
  log :publishing, 'stock.usd.appl', price = 170+rand(1000)/100.0
16
- MQ.topic(:key => 'stock.usd.appl').publish(price, :headers => {:symbol => 'appl'})
16
+ MQ.topic.publish(price, :key => 'stock.usd.appl', :headers => {:symbol => 'appl'})
17
17
 
18
18
  log :publishing, 'stock.usd.msft', price = 22+rand(500)/100.0
19
19
  MQ.topic.publish(price, :key => 'stock.usd.msft', :headers => {:symbol => 'msft'})
@@ -1,4 +1,12 @@
1
- require 'enumerator'
1
+ if [].map.respond_to? :with_index
2
+ class Array
3
+ def enum_with_index
4
+ each.with_index
5
+ end
6
+ end
7
+ else
8
+ require 'enumerator'
9
+ end
2
10
 
3
11
  module AMQP
4
12
  class Buffer
@@ -102,17 +110,17 @@ module AMQP
102
110
  key, type = table.read(:shortstr, :octet)
103
111
  key = key.intern
104
112
  t[key] ||= case type
105
- when ?S
113
+ when 83 # 'S'
106
114
  table.read(:longstr)
107
- when ?I
115
+ when 73 # 'I'
108
116
  table.read(:long)
109
- when ?D
117
+ when 68 # 'D'
110
118
  exp = table.read(:octet)
111
119
  num = table.read(:long)
112
120
  num / 10.0**exp
113
- when ?T
121
+ when 84 # 'T'
114
122
  table.read(:timestamp)
115
- when ?F
123
+ when 70 # 'F'
116
124
  table.read(:table)
117
125
  end
118
126
  end
@@ -164,23 +172,23 @@ module AMQP
164
172
 
165
173
  case value
166
174
  when String
167
- table.write(:octet, ?S)
175
+ table.write(:octet, 83) # 'S'
168
176
  table.write(:longstr, value.to_s)
169
177
  when Fixnum
170
- table.write(:octet, ?I)
178
+ table.write(:octet, 73) # 'I'
171
179
  table.write(:long, value)
172
180
  when Float
173
- table.write(:octet, ?D)
181
+ table.write(:octet, 68) # 'D'
174
182
  # XXX there's gotta be a better way to do this..
175
- exp = value.to_s.gsub(/^.+\./,'').length
183
+ exp = value.to_s.split('.').last.length
176
184
  num = value * 10**exp
177
185
  table.write(:octet, exp)
178
186
  table.write(:long, num)
179
187
  when Time
180
- table.write(:octet, ?T)
188
+ table.write(:octet, 84) # 'T'
181
189
  table.write(:timestamp, value)
182
190
  when Hash
183
- table.write(:octet, ?F)
191
+ table.write(:octet, 70) # 'F'
184
192
  table.write(:table, value)
185
193
  end
186
194
 
@@ -73,7 +73,7 @@ module AMQP
73
73
 
74
74
  def receive_data data
75
75
  @buf << data
76
- # log 'receive_data', data
76
+ log 'receive_data', data
77
77
 
78
78
  while frame = Frame.parse(@buf)
79
79
  log 'receive', frame
@@ -95,7 +95,7 @@ module AMQP
95
95
  end
96
96
 
97
97
  def send_data data
98
- # log 'send_data', data
98
+ log 'send_data', data
99
99
  super
100
100
  end
101
101
 
@@ -1,4 +1,9 @@
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)
4
+ #
5
+ # DO NOT EDIT! (edit protocol/codegen.rb instead, and run `rake codegen`)
6
+
2
7
  module AMQP
3
8
  HEADER = "AMQP".freeze
4
9
  VERSION_MAJOR = 8
@@ -0,0 +1,67 @@
1
+ EMFORK = $0 == __FILE__
2
+
3
+ if EMFORK
4
+ require 'rubygems'
5
+ end
6
+
7
+ require 'eventmachine'
8
+
9
+ # helper to fork off EM reactors
10
+ def EM.fork num = 1, &blk
11
+ unless @forks
12
+ trap('CHLD'){
13
+ pid = Process.wait
14
+ p [:pid, pid, :died] if EMFORK
15
+ block = @forks.delete(pid)
16
+ EM.fork(1, &block)
17
+ }
18
+
19
+ trap('EXIT'){
20
+ p [:pid, Process.pid, :exit] if EMFORK
21
+ @forks.keys.each{ |pid|
22
+ p [:pid, Process.pid, :killing, pid] if EMFORK
23
+ Process.kill('USR1', pid)
24
+ }
25
+ }
26
+
27
+ @forks = {}
28
+ end
29
+
30
+ num.times do
31
+ pid = EM.fork_reactor do
32
+ p [:pid, Process.pid, :started] if EMFORK
33
+
34
+ trap('USR1'){ EM.stop_event_loop }
35
+ trap('CHLD'){}
36
+ trap('EXIT'){}
37
+
38
+ blk.call
39
+ end
40
+
41
+ @forks[pid] = blk
42
+ p [:children, EM.forks] if EMFORK
43
+ end
44
+ end
45
+
46
+ def EM.forks
47
+ @forks.keys
48
+ end
49
+
50
+ if EMFORK
51
+ p 'starting reactor'
52
+
53
+ trap('INT'){ EM.stop_event_loop }
54
+
55
+ EM.run{
56
+ p [:parent, Process.pid]
57
+
58
+ EM.fork(2){
59
+ EM.add_periodic_timer(1) do
60
+ p [:fork, Process.pid, :ping]
61
+ end
62
+ }
63
+
64
+ }
65
+
66
+ p 'reactor stopped'
67
+ end
data/lib/mq.rb CHANGED
@@ -1,11 +1,14 @@
1
1
  $:.unshift File.expand_path(File.dirname(File.expand_path(__FILE__)))
2
2
  require 'amqp'
3
3
 
4
- unless defined?(BlankSlate)
5
- class BlankSlate < BasicObject; end if defined?(BasicObject)
4
+ class MQ
5
+ %w[ exchange queue rpc ].each do |file|
6
+ require "mq/#{file}"
7
+ end
6
8
 
7
- class BlankSlate
8
- instance_methods.each { |m| undef_method m unless m =~ /^__/ }
9
+ class << self
10
+ @logging = false
11
+ attr_accessor :logging
9
12
  end
10
13
  end
11
14
 
@@ -13,129 +16,6 @@ class MQ
13
16
  include AMQP
14
17
  include EM::Deferrable
15
18
 
16
- class Exchange
17
- include AMQP
18
-
19
- def initialize mq, type, name, opts = {}
20
- if name.is_a? Hash
21
- opts = name
22
- name = "amq.#{type}"
23
- end
24
-
25
- @mq = mq
26
- @type, @name = type, name
27
- @key = opts[:key]
28
-
29
- @mq.callback{
30
- @mq.send Protocol::Exchange::Declare.new({ :exchange => name,
31
- :type => type,
32
- :nowait => true }.merge(opts))
33
- } unless name == "amq.#{type}"
34
- end
35
- attr_reader :name, :type, :key
36
-
37
- def publish data, opts = {}
38
- @mq.callback{
39
- @mq.send Protocol::Basic::Publish.new({ :exchange => name,
40
- :routing_key => opts.delete(:key) || @key }.merge(opts))
41
-
42
- data = data.to_s
43
-
44
- @mq.send Protocol::Header.new(Protocol::Basic,
45
- data.length, { :content_type => 'application/octet-stream',
46
- :delivery_mode => 1,
47
- :priority => 0 }.merge(opts))
48
- @mq.send Frame::Body.new(data)
49
- }
50
- self
51
- end
52
- end
53
-
54
- class Queue
55
- include AMQP
56
-
57
- def initialize mq, name, opts = {}
58
- @mq = mq
59
- @name = name
60
- @mq.callback{
61
- @mq.send Protocol::Queue::Declare.new({ :queue => name,
62
- :nowait => true }.merge(opts))
63
- }
64
- bind(@mq.direct, :key => name)
65
- end
66
- attr_reader :name
67
-
68
- def bind exchange, opts = {}
69
- @mq.callback{
70
- @mq.send Protocol::Queue::Bind.new({ :queue => name,
71
- :exchange => exchange.respond_to?(:name) ? exchange.name : exchange,
72
- :routing_key => opts.delete(:key),
73
- :nowait => true }.merge(opts))
74
- }
75
- self
76
- end
77
-
78
- def subscribe opts = {}, &blk
79
- @on_msg = blk
80
- @mq.callback{
81
- @mq.send Protocol::Basic::Consume.new({ :queue => name,
82
- :consumer_tag => name,
83
- :no_ack => true,
84
- :nowait => true }.merge(opts))
85
- }
86
- self
87
- end
88
-
89
- def receive headers, body
90
- if @on_msg
91
- @on_msg.call *(@on_msg.arity == 1 ? [body] : [headers, body])
92
- end
93
- end
94
- end
95
-
96
- class RPC < BlankSlate
97
- def initialize mq, queue, obj = nil
98
- @mq = mq
99
-
100
- if obj
101
- @obj = case obj
102
- when Class
103
- obj.new
104
- when Module
105
- (::Class.new do include(obj) end).new
106
- else
107
- obj
108
- end
109
-
110
- @mq.queue(queue).subscribe{ |info, request|
111
- method, *args = Marshal.load(request)
112
- ret = @obj.__send__(method, *args)
113
-
114
- if info.reply_to
115
- @mq.direct.publish(Marshal.dump(ret), :key => info.reply_to, :message_id => info.message_id)
116
- end
117
- }
118
- else
119
- @callbacks ||= {}
120
- @queue = @mq.queue(@name = 'some random identifier for me').subscribe{|info, msg|
121
- ret = Marshal.load(msg)
122
- if blk = @callbacks.delete(info.message_id)
123
- blk.call(ret)
124
- end
125
- }
126
- @exchange = @mq.direct(:key => queue)
127
- end
128
- end
129
-
130
- def method_missing meth, *args, &blk
131
- message_id = "random message id #{rand(999_999_999_999)}"
132
- @callbacks[message_id] = blk if blk
133
- @exchange.publish(Marshal.dump([meth, *args]), :reply_to => blk ? @name : nil, :message_id => message_id)
134
- end
135
- end
136
- end
137
-
138
- class MQ
139
19
  def initialize
140
20
  conn.callback{ |c|
141
21
  @channel = c.add_channel(self)
@@ -145,6 +25,8 @@ class MQ
145
25
  attr_reader :channel
146
26
 
147
27
  def process_frame frame
28
+ log :received, frame
29
+
148
30
  case frame
149
31
  when Frame::Header
150
32
  @header = frame.payload
@@ -180,6 +62,7 @@ class MQ
180
62
  def send data
181
63
  data.ticket = @ticket if @ticket and data.respond_to? :ticket
182
64
  conn.callback{ |c|
65
+ log :sending, data
183
66
  c.send data, :channel => @channel
184
67
  }
185
68
  end
@@ -202,6 +85,14 @@ class MQ
202
85
 
203
86
  private
204
87
 
88
+ def log *args
89
+ return unless MQ.logging
90
+ pp args
91
+ puts
92
+ end
93
+
94
+ # keep track of proxy objects
95
+
205
96
  def exchanges
206
97
  @exchanges ||= {}
207
98
  end
@@ -214,16 +105,29 @@ class MQ
214
105
  @rcps ||= {}
215
106
  end
216
107
 
108
+ # create a class level connection on demand
109
+
217
110
  def connection
218
111
  @@connection ||= AMQP.start
219
112
  end
220
113
  alias :conn :connection
114
+ end
115
+
116
+ # convenience wrapper for thread-local MQ object
117
+
118
+ class MQ
119
+ def MQ.default
120
+ Thread.current[:mq] ||= MQ.new
121
+ end
221
122
 
222
123
  def MQ.method_missing meth, *args, &blk
223
124
  MQ.default.__send__(meth, *args, &blk)
224
125
  end
225
-
226
- def MQ.default
227
- Thread.current[:mq] ||= MQ.new
126
+ end
127
+
128
+ # unique identifier
129
+ class MQ
130
+ def MQ.id
131
+ Thread.current[:mq_id] ||= "#{`hostname`.strip}-#{Process.pid}-#{Thread.current.object_id}"
228
132
  end
229
133
  end
@@ -0,0 +1,36 @@
1
+ class MQ
2
+ class Exchange
3
+ include AMQP
4
+
5
+ def initialize mq, type, name, opts = {}
6
+ @mq = mq
7
+ @type, @name = type, name
8
+ @key = opts[:key]
9
+
10
+ @mq.callback{
11
+ @mq.send Protocol::Exchange::Declare.new({ :exchange => name,
12
+ :type => type,
13
+ :nowait => true }.merge(opts))
14
+ } unless name == "amq.#{type}" or name == ''
15
+ end
16
+ attr_reader :name, :type, :key
17
+
18
+ def publish data, opts = {}
19
+ @mq.callback{
20
+ EM.next_tick do
21
+ @mq.send Protocol::Basic::Publish.new({ :exchange => name,
22
+ :routing_key => opts.delete(:key) || @key }.merge(opts))
23
+
24
+ data = data.to_s
25
+
26
+ @mq.send Protocol::Header.new(Protocol::Basic,
27
+ data.length, { :content_type => 'application/octet-stream',
28
+ :delivery_mode => 1,
29
+ :priority => 0 }.merge(opts))
30
+ @mq.send Frame::Body.new(data)
31
+ end
32
+ }
33
+ self
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,52 @@
1
+ class MQ
2
+ class Queue
3
+ include AMQP
4
+
5
+ def initialize mq, name, opts = {}
6
+ @mq = mq
7
+ @name = name
8
+ @mq.callback{
9
+ @mq.send Protocol::Queue::Declare.new({ :queue => name,
10
+ :nowait => true }.merge(opts))
11
+ }
12
+ end
13
+ attr_reader :name
14
+
15
+ def bind exchange, opts = {}
16
+ @mq.callback{
17
+ @mq.send Protocol::Queue::Bind.new({ :queue => name,
18
+ :exchange => exchange.respond_to?(:name) ? exchange.name : exchange,
19
+ :routing_key => opts.delete(:key),
20
+ :nowait => true }.merge(opts))
21
+ }
22
+ self
23
+ end
24
+
25
+ def subscribe opts = {}, &blk
26
+ @on_msg = blk
27
+ @mq.callback{
28
+ @mq.send Protocol::Basic::Consume.new({ :queue => name,
29
+ :consumer_tag => name,
30
+ :no_ack => true,
31
+ :nowait => true }.merge(opts))
32
+ }
33
+ self
34
+ end
35
+
36
+ def publish data, opts = {}
37
+ exchange.publish(data, opts)
38
+ end
39
+
40
+ def receive headers, body
41
+ if @on_msg
42
+ @on_msg.call *(@on_msg.arity == 1 ? [body] : [headers, body])
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def exchange
49
+ @exchange ||= Exchange.new(@mq, :direct, '', :key => name)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,51 @@
1
+ require 'emfork'
2
+
3
+ unless defined?(BlankSlate)
4
+ class BlankSlate < BasicObject; end if defined?(BasicObject)
5
+
6
+ class BlankSlate
7
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
8
+ end
9
+ end
10
+
11
+ class MQ
12
+ class RPC < BlankSlate
13
+ def initialize mq, queue, obj = nil
14
+ @mq = mq
15
+
16
+ if obj
17
+ @obj = case obj
18
+ when ::Class
19
+ obj.new
20
+ when ::Module
21
+ (::Class.new do include(obj) end).new
22
+ else
23
+ obj
24
+ end
25
+
26
+ @mq.queue(queue).subscribe{ |info, request|
27
+ method, *args = ::Marshal.load(request)
28
+ ret = @obj.__send__(method, *args)
29
+
30
+ if info.reply_to
31
+ @mq.queue(info.reply_to).publish(::Marshal.dump(ret), :key => info.reply_to, :message_id => info.message_id)
32
+ end
33
+ }
34
+ else
35
+ @callbacks ||= {}
36
+ @queue = @mq.queue(@name = 'some random identifier for me').subscribe{|info, msg|
37
+ if blk = @callbacks.delete(info.message_id)
38
+ blk.call ::Marshal.load(msg)
39
+ end
40
+ }
41
+ @remote = @mq.queue(queue)
42
+ end
43
+ end
44
+
45
+ def method_missing meth, *args, &blk
46
+ message_id = "random message id #{::Kernel.rand(999_999_999_999)}"
47
+ @callbacks[message_id] = blk if blk
48
+ @remote.publish(::Marshal.dump([meth, *args]), :reply_to => blk ? @name : nil, :message_id => message_id)
49
+ end
50
+ end
51
+ end
@@ -1,7 +1,9 @@
1
1
  require 'rubygems'
2
2
  require 'json'
3
3
 
4
- s = JSON.parse(File.read(File.dirname(__FILE__)+'/amqp-0.8.json'))
4
+ name = 'amqp-0.8.json'
5
+ path = File.dirname(__FILE__)+'/'+name
6
+ s = JSON.parse(File.read(path))
5
7
 
6
8
  # require 'pp'
7
9
  # pp(s)
@@ -10,6 +12,11 @@ s = JSON.parse(File.read(File.dirname(__FILE__)+'/amqp-0.8.json'))
10
12
  require 'erb'
11
13
 
12
14
  puts ERB.new(%q[
15
+ # this file was autogenerated on <%= Time.now.to_s %>
16
+ # using <%= name.ljust(16) %> (mtime: <%= File.mtime(path) %>)
17
+ #
18
+ # DO NOT EDIT! (edit protocol/codegen.rb instead, and run `rake codegen`)
19
+
13
20
  module AMQP
14
21
  HEADER = <%= s['name'].dump %>.freeze
15
22
  VERSION_MAJOR = <%= s['major-version'] %>
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.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aman Gupta
@@ -9,10 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-07-17 00:00:00 -07:00
12
+ date: 2008-07-21 00:00:00 -07:00
13
13
  default_executable:
14
- dependencies: []
15
-
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: eventmachine
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.12.1
24
+ version:
16
25
  description: AMQP client implementation in Ruby/EventMachine
17
26
  email: amqp@tmm1.net
18
27
  executables: []
@@ -22,9 +31,15 @@ extensions: []
22
31
  extra_rdoc_files: []
23
32
 
24
33
  files:
34
+ - README
25
35
  - examples/clock.rb
26
36
  - examples/hashtable.rb
27
37
  - examples/pingpong.rb
38
+ - examples/primes-forked.rb
39
+ - examples/primes-processes.rb
40
+ - examples/primes-simple.rb
41
+ - examples/primes-threaded.rb
42
+ - examples/primes.rb
28
43
  - examples/simple.rb
29
44
  - examples/stocks.rb
30
45
  - lib/amqp/buffer.rb
@@ -33,12 +48,15 @@ files:
33
48
  - lib/amqp/protocol.rb
34
49
  - lib/amqp/spec.rb
35
50
  - lib/amqp.rb
51
+ - lib/emfork.rb
52
+ - lib/mq/exchange.rb
53
+ - lib/mq/queue.rb
54
+ - lib/mq/rpc.rb
36
55
  - lib/mq.rb
37
56
  - protocol/amqp-0.8.json
38
57
  - protocol/codegen.rb
39
58
  - protocol/doc.txt
40
59
  - protocol/amqp-0.8.xml
41
- - README
42
60
  has_rdoc: false
43
61
  homepage: http://amqp.rubyforge.org/
44
62
  post_install_message: