amqp 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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: