tmm1-amqp 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,77 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'amqp'
3
+
4
+ module SimpleClient
5
+ def process_frame frame
6
+ case frame
7
+ when Frame::Body
8
+ EM.stop_event_loop
9
+
10
+ when Frame::Method
11
+ case method = frame.payload
12
+ when Protocol::Connection::Start
13
+ send Protocol::Connection::StartOk.new({:platform => 'Ruby/EventMachine',
14
+ :product => 'AMQP',
15
+ :information => 'http://github.com/tmm1/amqp',
16
+ :version => '0.1.0'},
17
+ 'AMQPLAIN',
18
+ {:LOGIN => 'guest',
19
+ :PASSWORD => 'guest'},
20
+ 'en_US')
21
+
22
+ when Protocol::Connection::Tune
23
+ send Protocol::Connection::TuneOk.new(:channel_max => 0,
24
+ :frame_max => 131072,
25
+ :heartbeat => 0)
26
+
27
+ send Protocol::Connection::Open.new(:virtual_host => '/',
28
+ :capabilities => '',
29
+ :insist => false)
30
+
31
+ when Protocol::Connection::OpenOk
32
+ send Protocol::Channel::Open.new, :channel => 1
33
+
34
+ when Protocol::Channel::OpenOk
35
+ send Protocol::Access::Request.new(:realm => '/data',
36
+ :read => true,
37
+ :write => true,
38
+ :active => true), :channel => 1
39
+
40
+ when Protocol::Access::RequestOk
41
+ @ticket = method.ticket
42
+ send Protocol::Queue::Declare.new(:ticket => @ticket,
43
+ :queue => '',
44
+ :exclusive => false,
45
+ :auto_delete => true), :channel => 1
46
+
47
+ when Protocol::Queue::DeclareOk
48
+ @queue = method.queue
49
+ send Protocol::Queue::Bind.new(:ticket => @ticket,
50
+ :queue => @queue,
51
+ :exchange => '',
52
+ :routing_key => 'test_route'), :channel => 1
53
+
54
+ when Protocol::Queue::BindOk
55
+ send Protocol::Basic::Consume.new(:ticket => @ticket,
56
+ :queue => @queue,
57
+ :no_local => false,
58
+ :no_ack => true), :channel => 1
59
+
60
+ when Protocol::Basic::ConsumeOk
61
+ data = "this is a test!"
62
+
63
+ send Protocol::Basic::Publish.new(:ticket => @ticket,
64
+ :exchange => '',
65
+ :routing_key => 'test_route'), :channel => 1
66
+ send Protocol::Header.new(Protocol::Basic, data.length, :content_type => 'application/octet-stream',
67
+ :delivery_mode => 1,
68
+ :priority => 0), :channel => 1
69
+ send Frame::Body.new(data), :channel => 1
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ AMQP.logging = true
76
+ AMQP.client = SimpleClient
77
+ AMQP.start
@@ -0,0 +1,56 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'mq'
3
+
4
+ EM.run{
5
+
6
+ def log *args
7
+ p [ Time.now, *args ]
8
+ end
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
+ }
21
+
22
+ Thread.new{
23
+ amq = MQ.new
24
+ amq.queue('apple stock').bind(amq.topic, :key => 'stock.usd.appl').subscribe{ |price|
25
+ log 'apple stock', price
26
+ }
27
+ }
28
+
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
+ }
34
+ }
35
+
36
+ }
37
+
38
+ __END__
39
+
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"]
data/lib/amqp.rb ADDED
@@ -0,0 +1,14 @@
1
+ module AMQP
2
+ DIR = File.expand_path(File.dirname(File.expand_path(__FILE__)))
3
+
4
+ $:.unshift DIR
5
+
6
+ %w[ buffer spec protocol frame client ].each do |file|
7
+ require "amqp/#{file}"
8
+ end
9
+
10
+ class << self
11
+ @logging = false
12
+ attr_accessor :logging
13
+ end
14
+ end
@@ -0,0 +1,395 @@
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
10
+
11
+ module AMQP
12
+ class Buffer
13
+ class Overflow < Exception; end
14
+ class InvalidType < Exception; end
15
+
16
+ def initialize data = ''
17
+ @data = data
18
+ @pos = 0
19
+ end
20
+
21
+ attr_reader :pos
22
+
23
+ def data
24
+ @data.clone
25
+ end
26
+ alias :contents :data
27
+ alias :to_s :data
28
+
29
+ def << data
30
+ @data << data.to_s
31
+ self
32
+ end
33
+
34
+ def length
35
+ @data.length
36
+ end
37
+
38
+ def empty?
39
+ pos == length
40
+ end
41
+
42
+ def rewind
43
+ @pos = 0
44
+ end
45
+
46
+ def read_properties *types
47
+ types.shift if types.first == :properties
48
+
49
+ i = 0
50
+ values = []
51
+
52
+ while props = read(:short)
53
+ (0..14).each do |n|
54
+ # no more property types
55
+ break unless types[i]
56
+
57
+ # if flag is set
58
+ if props & (1<<(15-n)) != 0
59
+ if types[i] == :bit
60
+ # bit values exist in flags only
61
+ values << true
62
+ else
63
+ # save type name for later reading
64
+ values << types[i]
65
+ end
66
+ else
67
+ # property not set or is false bit
68
+ values << (types[i] == :bit ? false : nil)
69
+ end
70
+
71
+ i+=1
72
+ end
73
+
74
+ # bit(0) == 0 means no more property flags
75
+ break unless props & 1 == 1
76
+ end
77
+
78
+ values.map do |value|
79
+ value.is_a?(Symbol) ? read(value) : value
80
+ end
81
+ end
82
+
83
+ def read *types
84
+ if types.first == :properties
85
+ return read_properties(*types)
86
+ end
87
+
88
+ values = types.map do |type|
89
+ case type
90
+ when :octet
91
+ _read(1, 'C')
92
+ when :short
93
+ _read(2, 'n')
94
+ when :long
95
+ _read(4, 'N')
96
+ when :longlong
97
+ upper, lower = _read(8, 'NN')
98
+ upper << 32 | lower
99
+ when :shortstr
100
+ _read read(:octet)
101
+ when :longstr
102
+ _read read(:long)
103
+ when :timestamp
104
+ Time.at read(:longlong)
105
+ when :table
106
+ t = Hash.new
107
+
108
+ table = Buffer.new(read(:longstr))
109
+ until table.empty?
110
+ key, type = table.read(:shortstr, :octet)
111
+ key = key.intern
112
+ t[key] ||= case type
113
+ when 83 # 'S'
114
+ table.read(:longstr)
115
+ when 73 # 'I'
116
+ table.read(:long)
117
+ when 68 # 'D'
118
+ exp = table.read(:octet)
119
+ num = table.read(:long)
120
+ num / 10.0**exp
121
+ when 84 # 'T'
122
+ table.read(:timestamp)
123
+ when 70 # 'F'
124
+ table.read(:table)
125
+ end
126
+ end
127
+
128
+ t
129
+ when :bit
130
+ if (@bits ||= []).empty?
131
+ val = read(:octet)
132
+ @bits = (0..7).map{|i| (val & 1<<i) != 0 }
133
+ end
134
+
135
+ @bits.shift
136
+ else
137
+ raise InvalidType, "Cannot read data of type #{type}"
138
+ end
139
+ end
140
+
141
+ types.size == 1 ? values.first : values
142
+ end
143
+
144
+ def write type, data
145
+ case type
146
+ when :octet
147
+ _write(data, 'C')
148
+ when :short
149
+ _write(data, 'n')
150
+ when :long
151
+ _write(data, 'N')
152
+ when :longlong
153
+ lower = data & 0xffffffff
154
+ upper = (data & ~0xffffffff) >> 32
155
+ _write([upper, lower], 'NN')
156
+ when :shortstr
157
+ data = (data || '').to_s
158
+ _write([data.length, data], 'Ca*')
159
+ when :longstr
160
+ if data.is_a? Hash
161
+ write(:table, data)
162
+ else
163
+ data = (data || '').to_s
164
+ _write([data.length, data], 'Na*')
165
+ end
166
+ when :timestamp
167
+ write(:longlong, data.to_i)
168
+ when :table
169
+ data ||= {}
170
+ write :longstr, (data.inject(Buffer.new) do |table, (key, value)|
171
+ table.write(:shortstr, key.to_s)
172
+
173
+ case value
174
+ when String
175
+ table.write(:octet, 83) # 'S'
176
+ table.write(:longstr, value.to_s)
177
+ when Fixnum
178
+ table.write(:octet, 73) # 'I'
179
+ table.write(:long, value)
180
+ when Float
181
+ table.write(:octet, 68) # 'D'
182
+ # XXX there's gotta be a better way to do this..
183
+ exp = value.to_s.split('.').last.length
184
+ num = value * 10**exp
185
+ table.write(:octet, exp)
186
+ table.write(:long, num)
187
+ when Time
188
+ table.write(:octet, 84) # 'T'
189
+ table.write(:timestamp, value)
190
+ when Hash
191
+ table.write(:octet, 70) # 'F'
192
+ table.write(:table, value)
193
+ end
194
+
195
+ table
196
+ end)
197
+ when :bit
198
+ [*data].to_enum(:each_slice, 8).each{|bits|
199
+ write(:octet, bits.enum_with_index.inject(0){ |byte, (bit, i)|
200
+ byte |= 1<<i if bit
201
+ byte
202
+ })
203
+ }
204
+ when :properties
205
+ values = []
206
+ data.enum_with_index.inject(0) do |short, ((type, value), i)|
207
+ n = i % 15
208
+ last = i+1 == data.size
209
+
210
+ if (n == 0 and i != 0) or last
211
+ if data.size > i+1
212
+ short |= 1<<0
213
+ elsif last and value
214
+ values << [type,value]
215
+ short |= 1<<(15-n)
216
+ end
217
+
218
+ write(:short, short)
219
+ short = 0
220
+ end
221
+
222
+ if value and !last
223
+ values << [type,value]
224
+ short |= 1<<(15-n)
225
+ end
226
+
227
+ short
228
+ end
229
+
230
+ values.each do |type, value|
231
+ write(type, value) unless type == :bit
232
+ end
233
+ else
234
+ raise InvalidType, "Cannot write data of type #{type}"
235
+ end
236
+
237
+ self
238
+ end
239
+
240
+ def extract
241
+ begin
242
+ cur_data, cur_pos = @data.clone, @pos
243
+ yield self
244
+ rescue Overflow
245
+ @data, @pos = cur_data, cur_pos
246
+ nil
247
+ end
248
+ end
249
+
250
+ def _read size, pack = nil
251
+ if @pos + size > length
252
+ raise Overflow
253
+ else
254
+ data = @data[@pos,size]
255
+ @data[@pos,size] = ''
256
+ if pack
257
+ data = data.unpack(pack)
258
+ data = data.pop if data.size == 1
259
+ end
260
+ data
261
+ end
262
+ end
263
+
264
+ def _write data, pack = nil
265
+ data = [*data].pack(pack) if pack
266
+ @data[@pos,0] = data
267
+ @pos += data.length
268
+ end
269
+ end
270
+ end
271
+
272
+ if $0 =~ /bacon/ or $0 == __FILE__
273
+ require 'bacon'
274
+ include AMQP
275
+
276
+ describe Buffer do
277
+ before do
278
+ @buf = Buffer.new
279
+ end
280
+
281
+ should 'have contents' do
282
+ @buf.contents.should == ''
283
+ end
284
+
285
+ should 'initialize with data' do
286
+ @buf = Buffer.new('abc')
287
+ @buf.contents.should == 'abc'
288
+ end
289
+
290
+ should 'append raw data' do
291
+ @buf << 'abc'
292
+ @buf << 'def'
293
+ @buf.contents.should == 'abcdef'
294
+ end
295
+
296
+ should 'append other buffers' do
297
+ @buf << Buffer.new('abc')
298
+ @buf.data.should == 'abc'
299
+ end
300
+
301
+ should 'have a position' do
302
+ @buf.pos.should == 0
303
+ end
304
+
305
+ should 'have a length' do
306
+ @buf.length.should == 0
307
+ @buf << 'abc'
308
+ @buf.length.should == 3
309
+ end
310
+
311
+ should 'know the end' do
312
+ @buf.empty?.should == true
313
+ end
314
+
315
+ should 'read and write data' do
316
+ @buf._write('abc')
317
+ @buf.rewind
318
+ @buf._read(2).should == 'ab'
319
+ @buf._read(1).should == 'c'
320
+ end
321
+
322
+ should 'raise on overflow' do
323
+ lambda{ @buf._read(1) }.should.raise Buffer::Overflow
324
+ end
325
+
326
+ should 'raise on invalid types' do
327
+ lambda{ @buf.read(:junk) }.should.raise Buffer::InvalidType
328
+ lambda{ @buf.write(:junk, 1) }.should.raise Buffer::InvalidType
329
+ end
330
+
331
+ { :octet => 0b10101010,
332
+ :short => 100,
333
+ :long => 100_000_000,
334
+ :longlong => 666_555_444_333_222_111,
335
+ :shortstr => 'hello',
336
+ :longstr => 'bye'*500,
337
+ :timestamp => time = Time.at(Time.now.to_i),
338
+ :table => { :this => 'is', :a => 'hash', :with => {:nested => 123, :and => time, :also => 123.456} },
339
+ :bit => true
340
+ }.each do |type, value|
341
+
342
+ should "read and write a #{type}" do
343
+ @buf.write(type, value)
344
+ @buf.rewind
345
+ @buf.read(type).should == value
346
+ @buf.should.be.empty
347
+ end
348
+
349
+ end
350
+
351
+ should 'read and write multiple bits' do
352
+ bits = [true, false, false, true, true, false, false, true, true, false]
353
+ @buf.write(:bit, bits)
354
+ @buf.write(:octet, 100)
355
+
356
+ @buf.rewind
357
+
358
+ bits.map do
359
+ @buf.read(:bit)
360
+ end.should == bits
361
+ @buf.read(:octet).should == 100
362
+ end
363
+
364
+ should 'read and write properties' do
365
+ properties = ([
366
+ [:octet, 1],
367
+ [:shortstr, 'abc'],
368
+ [:bit, true],
369
+ [:bit, false],
370
+ [:shortstr, nil],
371
+ [:timestamp, nil],
372
+ [:table, { :a => 'hash' }],
373
+ ]*5).sort_by{rand}
374
+
375
+ @buf.write(:properties, properties)
376
+ @buf.rewind
377
+ @buf.read(:properties, *properties.map{|type,_| type }).should == properties.map{|_,value| value }
378
+ @buf.should.be.empty
379
+ end
380
+
381
+ should 'do transactional reads with #extract' do
382
+ @buf.write :octet, 8
383
+ orig = @buf.to_s
384
+
385
+ @buf.rewind
386
+ @buf.extract do |b|
387
+ b.read :octet
388
+ b.read :short
389
+ end
390
+
391
+ @buf.pos.should == 0
392
+ @buf.data.should == orig
393
+ end
394
+ end
395
+ end