brb 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +49 -0
- data/Rakefile +23 -0
- data/examples/simple_client.rb +37 -0
- data/examples/simple_core.rb +31 -0
- data/init.rb +2 -0
- data/lib/brb.rb +48 -0
- data/lib/brb/event_machine.rb +44 -0
- data/lib/brb/exception.rb +0 -0
- data/lib/brb/request.rb +81 -0
- data/lib/brb/tunnel.rb +81 -0
- data/lib/brb/tunnel/shared.rb +109 -0
- data/spec/brb/brb_massive_usage_spec.rb +72 -0
- data/spec/brb/brb_service_spec.rb +29 -0
- data/spec/brb/brb_tunnel_spec.rb +124 -0
- data/spec/spec_helper.rb +68 -0
- metadata +91 -0
data/CHANGELOG.rdoc
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Guillaume Luccisano - g-mai|: guillaume.luccisano
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
= BrB - Easy and Fast distributed ruby
|
2
|
+
|
3
|
+
BrB is a simple and extremely fast interface for doing simple distributed ruby.
|
4
|
+
The main power of the architecture is provided by EventMachine (Fast and reliable IO event library).
|
5
|
+
|
6
|
+
BrB was build in order to achieve these 4 main goals :
|
7
|
+
* Simple and fast message passing between distant ruby process.
|
8
|
+
* Message passing with handling of return value when needed.
|
9
|
+
* Being extremely fast in order to handle more than a few thousand messages per seconds.
|
10
|
+
* Being completely transparent for developer.
|
11
|
+
|
12
|
+
The principle is simple and inspired from Drb (Original distributed ruby library) :
|
13
|
+
A process want to expose an object on the network, and want other ruby processes calling directly method on it.
|
14
|
+
BrB handle all that part, it's fully transparent in the ruby code.
|
15
|
+
|
16
|
+
BrB only support message passing with Marshable dumpable object : String, symbol, Array, hash, Number, Object etc...
|
17
|
+
That mean you can not send file descriptor, Thread or another funky things like that :)
|
18
|
+
|
19
|
+
== Main Functionalities
|
20
|
+
|
21
|
+
* Unlimited objects exposed
|
22
|
+
* Processes can expose object and be client to exposed object too at the same time
|
23
|
+
* Do not wait for return by default : just do simple message passing
|
24
|
+
* Wait for return if needed by simply adding <em>_block</em> at the end of the function name
|
25
|
+
* Transmission of Exception when blocking call
|
26
|
+
* Thread safe if used correctly with Event Machine
|
27
|
+
|
28
|
+
== How it works
|
29
|
+
|
30
|
+
First of all, a process declare himself as a sever and expose any object on a given address and port.
|
31
|
+
Then, any number of distant processes can create a <em>Tunnel</em> with that server and can expose an object in exchange too.
|
32
|
+
After connection are ready, just call method on the tunnel. It will just act like normal method calling on the exposed object !
|
33
|
+
|
34
|
+
== What BrB is usable for ?
|
35
|
+
|
36
|
+
* Doing Simple message passing between ruby process.
|
37
|
+
* Connecting hundred of ruby process transparently.
|
38
|
+
* Building a real-time scalable (game) server
|
39
|
+
* Taking important load on a server easily just by distributing the load on multiple BrB instance.
|
40
|
+
* Taking advantage of multi-core and multi-threaded systems.
|
41
|
+
|
42
|
+
== TODO
|
43
|
+
* Better README and Wiki
|
44
|
+
* Writing more examples
|
45
|
+
* Publish Benchmarks VS drb
|
46
|
+
* Improve logging mechanism
|
47
|
+
* Clean up
|
48
|
+
|
49
|
+
Copyright (c) 2009-2010 Guillaume Luccisano - g-mai|: guillaume.luccisano, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
|
5
|
+
spec_files = Rake::FileList["spec/**/*_spec.rb"]
|
6
|
+
|
7
|
+
desc "Run specs for current Rails version"
|
8
|
+
Spec::Rake::SpecTask.new do |t|
|
9
|
+
t.spec_files = spec_files
|
10
|
+
t.spec_opts = ["-c"]
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :spec
|
14
|
+
|
15
|
+
desc "Run simple core"
|
16
|
+
task :simple_core_example do
|
17
|
+
require 'examples/simple_core'
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Run simple client (call simple_core_before)"
|
21
|
+
task :simple_client_example do
|
22
|
+
require 'examples/simple_client'
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'lib/brb'
|
2
|
+
|
3
|
+
port = 5555
|
4
|
+
host = 'localhost'
|
5
|
+
|
6
|
+
EM::run do
|
7
|
+
EM::set_quantum(20)
|
8
|
+
# Connecting to the core server, retrieving its interface object : core
|
9
|
+
# We do not want to expose an object, so the first parameter is nil
|
10
|
+
core = BrB::Tunnel.create(nil, "brb://#{host}:#{port}", :silent => false)
|
11
|
+
|
12
|
+
# Create thread here, as you can not block the EventMachine::run block
|
13
|
+
Thread.new do
|
14
|
+
|
15
|
+
# Calling 10 times an non blocking method on the distant core server
|
16
|
+
10.times do
|
17
|
+
core.simple_api_method # Do not wait for response
|
18
|
+
end
|
19
|
+
|
20
|
+
# Calling 10 times again long treatment time distant methods
|
21
|
+
10.times do
|
22
|
+
core.simple_long_api_method # Do not wait for response
|
23
|
+
end
|
24
|
+
|
25
|
+
# Calling a blocking method with _block on the distant core server :
|
26
|
+
puts " >> Calling 1s call, and wait for response..."
|
27
|
+
r = core.simple_api_method_block
|
28
|
+
puts " > Api response : #{r}"
|
29
|
+
|
30
|
+
puts " >> Calling long call, and wait for response..."
|
31
|
+
r = core.simple_long_api_method_block
|
32
|
+
puts " > Api long response : #{r}"
|
33
|
+
|
34
|
+
# Our job is over, close
|
35
|
+
EM.stop
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'lib/brb'
|
2
|
+
|
3
|
+
class ExposedCoreObject
|
4
|
+
|
5
|
+
def simple_api_method
|
6
|
+
puts "#{Thread.current} > In simple api method, now sleeping"
|
7
|
+
sleep 1
|
8
|
+
puts "#{Thread.current} > Done sleeping in simple api method, return"
|
9
|
+
return 'OK'
|
10
|
+
end
|
11
|
+
|
12
|
+
def simple_long_api_method
|
13
|
+
puts "#{Thread.current} > In simple long api method, now sleeping"
|
14
|
+
sleep 10
|
15
|
+
puts "#{Thread.current} > Done sleeping in long api method, return"
|
16
|
+
return 'OK LONG'
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
Thread.abort_on_exception = true
|
22
|
+
|
23
|
+
port = 5555
|
24
|
+
host = 'localhost'
|
25
|
+
|
26
|
+
EM::run do
|
27
|
+
EM::set_quantum(20)
|
28
|
+
|
29
|
+
puts " > Starting the core on brb://#{host}:#{port}"
|
30
|
+
BrB::Service.instance.start_service(:object => ExposedCoreObject.new, :silent => false, :host => host, :port => port)
|
31
|
+
end
|
data/init.rb
ADDED
data/lib/brb.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'brb', 'exception.rb')
|
2
|
+
require File.join(File.dirname(__FILE__), 'brb', 'event_machine.rb')
|
3
|
+
require File.join(File.dirname(__FILE__), 'brb', 'tunnel.rb')
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Brb Main class used to do basic distributed ruby, Simple but fast
|
8
|
+
# Use two distinct canal, one for the command reception, and the other one for send return value
|
9
|
+
#
|
10
|
+
module BrB
|
11
|
+
class Service
|
12
|
+
attr_reader :silent
|
13
|
+
attr_reader :uri
|
14
|
+
|
15
|
+
include Singleton
|
16
|
+
|
17
|
+
# Start a server hosted on the object given,
|
18
|
+
# If an uri is given, automatcilay connect to the distant brb object
|
19
|
+
def start_service(opts = {}, &block)
|
20
|
+
return if @em_signature
|
21
|
+
|
22
|
+
@silent = opts[:silent]
|
23
|
+
|
24
|
+
addr = "brb://#{opts[:host] || 'localhost'}:#{opts[:port] || 6200}"
|
25
|
+
|
26
|
+
tputs " [BrB] Start service on #{addr} ..."
|
27
|
+
@uri, @em_signature = BrBProtocol.open_server(addr, BrB::Tunnel::Handler, opts.merge(:block => block))
|
28
|
+
tputs " [BrB] Service started on #{@uri}"
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def tputs(s)
|
33
|
+
puts s if !@silent
|
34
|
+
end
|
35
|
+
|
36
|
+
public
|
37
|
+
# Stop the Brb Service
|
38
|
+
def stop_service
|
39
|
+
return if !@em_signature
|
40
|
+
|
41
|
+
tputs ' [BrB] Stop service'
|
42
|
+
EM::stop_server(@em_signature)
|
43
|
+
@em_signature = nil
|
44
|
+
@uri = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
# Define a BrBProtocol using event machine
|
4
|
+
class BrBEventMachine
|
5
|
+
|
6
|
+
def self.parse_uri(uri)
|
7
|
+
if /^brb:\/\/(.+):([0-9]+)$/ =~ uri
|
8
|
+
[$1, $2.to_i]
|
9
|
+
else
|
10
|
+
raise "Bad tcp BrB url: '#{uri}'"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.open(uri, klass, opts = {})
|
15
|
+
host, port = parse_uri(uri)
|
16
|
+
begin
|
17
|
+
socket = EventMachine::connect host, port, klass, opts.merge(:uri => "brb://#{host}:#{port}")
|
18
|
+
|
19
|
+
rescue Exception => e
|
20
|
+
puts e.backtrace.join("\n")
|
21
|
+
raise "#{e} - #{uri}"
|
22
|
+
end
|
23
|
+
return socket
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.open_server(uri, klass, opts = {})
|
27
|
+
host, port = parse_uri(uri)
|
28
|
+
max = 80
|
29
|
+
begin
|
30
|
+
#EventMachine::epoll
|
31
|
+
uri = "brb://#{host}:#{port}"
|
32
|
+
return uri, EventMachine::start_server(host, port, klass, opts.merge(:uri => uri))
|
33
|
+
rescue Exception => e
|
34
|
+
max -= 1
|
35
|
+
port += 1
|
36
|
+
retry if max > 0
|
37
|
+
puts e.backtrace.join("\n")
|
38
|
+
raise "#{e} - BrB Tcp Event machine Can not bind on #{host}:#{port}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class BrBProtocol < BrBEventMachine
|
44
|
+
end
|
File without changes
|
data/lib/brb/request.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
module BrB
|
2
|
+
module Request
|
3
|
+
|
4
|
+
def is_brb_request_blocking?(meth)
|
5
|
+
if m = meth.to_s and m.rindex('_block') == (m.size - 6)
|
6
|
+
return true
|
7
|
+
end
|
8
|
+
nil
|
9
|
+
end
|
10
|
+
|
11
|
+
# Execute a request on a distant object
|
12
|
+
def new_brb_out_request(meth, *args)
|
13
|
+
Thread.current[:brb_nb_out] ||= 0
|
14
|
+
Thread.current[:brb_nb_out] += 1
|
15
|
+
|
16
|
+
block = is_brb_request_blocking?(meth) ? Thread.current.to_s.to_sym : nil
|
17
|
+
if block
|
18
|
+
args << block
|
19
|
+
args << Thread.current[:brb_nb_out]
|
20
|
+
end
|
21
|
+
|
22
|
+
args.size > 0 ? brb_send([:s, meth, args]) : brb_send([:s, meth])
|
23
|
+
|
24
|
+
# Block jusqu'au retour de la requete
|
25
|
+
if block
|
26
|
+
#TimeMonitor.instance.watch_thread!(@timeout_rcv_value || 45)
|
27
|
+
begin
|
28
|
+
r = recv(block, Thread.current[:brb_nb_out])
|
29
|
+
rescue Exception => e
|
30
|
+
#@object.log_error(e, "Error sending out request #{meth}(#{args.inspect})")
|
31
|
+
raise e
|
32
|
+
ensure
|
33
|
+
#TimeMonitor.instance.remove_thread!
|
34
|
+
end
|
35
|
+
if r.kind_of? Exception
|
36
|
+
raise r
|
37
|
+
end
|
38
|
+
return r
|
39
|
+
end
|
40
|
+
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
# Execute a request on the local object
|
45
|
+
def new_brb_in_request(meth, *args)
|
46
|
+
|
47
|
+
if is_brb_request_blocking?(meth)
|
48
|
+
|
49
|
+
m = meth.to_s
|
50
|
+
m = m[0, m.size - 6].to_sym
|
51
|
+
|
52
|
+
idrequest = args.pop
|
53
|
+
thread = args.pop
|
54
|
+
begin
|
55
|
+
#TimeMonitor.instance.watch_thread!(25)
|
56
|
+
r = ((args.size > 0) ? @object.send(m, *args) : @object.send(m))
|
57
|
+
brb_send([:r, r, thread, idrequest])
|
58
|
+
rescue Exception => e
|
59
|
+
brb_send([:r, e, thread, idrequest])
|
60
|
+
tputs e.to_s
|
61
|
+
tputs e.backtrace.join("\n")
|
62
|
+
#raise e
|
63
|
+
ensure
|
64
|
+
#TimeMonitor.instance.remove_thread!
|
65
|
+
end
|
66
|
+
else
|
67
|
+
|
68
|
+
begin
|
69
|
+
(args.size > 0) ? @object.send(meth, *args) : @object.send(meth)
|
70
|
+
rescue Exception => e
|
71
|
+
tputs "#{e.to_s} => By calling #{meth} on #{@object.class} with args : #{args.inspect}"
|
72
|
+
tputs e.backtrace.join("\n")
|
73
|
+
raise e
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/lib/brb/tunnel.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'eventmachine'
|
3
|
+
require File.join(File.dirname(__FILE__), 'request.rb')
|
4
|
+
require File.join(File.dirname(__FILE__), 'tunnel', 'shared.rb')
|
5
|
+
|
6
|
+
module BrB
|
7
|
+
module Tunnel
|
8
|
+
|
9
|
+
def self.create(object, uri = nil, opts = {}, &block)
|
10
|
+
BrBProtocol.open(uri, BrB::Tunnel::Handler, opts.merge(:object => object, :block => block))
|
11
|
+
end
|
12
|
+
|
13
|
+
# Brb interface Handler for Tunnel over Event machine
|
14
|
+
class Handler < EventMachine::Connection
|
15
|
+
attr_reader :uri
|
16
|
+
|
17
|
+
include BrB::Request
|
18
|
+
include BrB::Tunnel::Shared
|
19
|
+
|
20
|
+
def initialize(opts = {})
|
21
|
+
super
|
22
|
+
@object = opts[:object]
|
23
|
+
@silent = opts[:silent]
|
24
|
+
@timeout_rcv_value = opts[:timeout] || 30
|
25
|
+
@close_after_timeout = opts[:close_after_timeout] || false
|
26
|
+
@uri = opts[:uri]
|
27
|
+
@closed = nil
|
28
|
+
@replock = Mutex.new
|
29
|
+
@responses = {}
|
30
|
+
@block = opts[:block]
|
31
|
+
@mu = Mutex.new
|
32
|
+
|
33
|
+
@queue = Queue.new
|
34
|
+
@buffer = ''
|
35
|
+
end
|
36
|
+
|
37
|
+
def post_init
|
38
|
+
tputs " [BrB] Tunnel initialized on #{@uri}"
|
39
|
+
@active = true
|
40
|
+
if @block
|
41
|
+
EventMachine.defer do
|
42
|
+
@block.call(:register, self)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def close_connection(after_writing = false)
|
48
|
+
@active = false
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
def unbind
|
53
|
+
tputs ' [BrB] Tunnel service closed'
|
54
|
+
@active = false
|
55
|
+
if @block
|
56
|
+
EventMachine.defer do
|
57
|
+
@block.call(:unregister, self)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def stop_service
|
63
|
+
tputs ' [BrB] Stop Tunnel service'
|
64
|
+
@active = false
|
65
|
+
EM.schedule do
|
66
|
+
close_connection
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def active?
|
71
|
+
@active
|
72
|
+
end
|
73
|
+
|
74
|
+
def method_missing(meth, *args)
|
75
|
+
return nil if !@active
|
76
|
+
new_brb_out_request(meth, *args)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module BrB
|
4
|
+
module Tunnel
|
5
|
+
module Shared
|
6
|
+
def tputs(s)
|
7
|
+
puts s if !@silent
|
8
|
+
end
|
9
|
+
|
10
|
+
def make_proxy(r)
|
11
|
+
if r.is_a?(Array)
|
12
|
+
t = []
|
13
|
+
r.each do |obj|
|
14
|
+
t << if obj.is_a? Array
|
15
|
+
make_proxy(obj)
|
16
|
+
elsif !obj.is_a?(Symbol) and !obj.is_a?(String) and obj and !(Marshal::dump(obj) rescue nil)
|
17
|
+
#puts " - > Make proxy for : #{obj.class}"
|
18
|
+
obj.to_s.to_sym
|
19
|
+
else
|
20
|
+
obj
|
21
|
+
end
|
22
|
+
end
|
23
|
+
return t
|
24
|
+
else
|
25
|
+
return r.to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def brb_send(r)
|
30
|
+
return nil if !@active
|
31
|
+
s = Marshal::dump(r) rescue Marshal::dump(make_proxy(r))
|
32
|
+
|
33
|
+
s = [s.size].pack('N') + s
|
34
|
+
EM.schedule do
|
35
|
+
send_data s
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
SizeOfPackedInt = [1].pack('N').size
|
40
|
+
|
41
|
+
def load_request
|
42
|
+
return nil if @buffer.size < SizeOfPackedInt
|
43
|
+
len = @buffer.unpack('N').first + SizeOfPackedInt
|
44
|
+
if @buffer.size < len
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
|
48
|
+
obj = Marshal::load(@buffer[SizeOfPackedInt, len])
|
49
|
+
@buffer.slice!(0,len)
|
50
|
+
return obj
|
51
|
+
end
|
52
|
+
|
53
|
+
def receive_data(data)
|
54
|
+
@buffer << data
|
55
|
+
|
56
|
+
while obj = load_request
|
57
|
+
if obj[0] == :r
|
58
|
+
@replock.lock
|
59
|
+
@responses[obj[2]] ||= Queue.new
|
60
|
+
@replock.unlock
|
61
|
+
@responses[obj[2]] << [obj[1], obj[3]]
|
62
|
+
else
|
63
|
+
|
64
|
+
@queue << obj
|
65
|
+
|
66
|
+
EventMachine.defer do
|
67
|
+
treat_request(@queue.pop)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def treat_request(obj)
|
75
|
+
if obj.size == 2
|
76
|
+
new_brb_in_request(obj[1])
|
77
|
+
else
|
78
|
+
new_brb_in_request(obj[1], *(obj.last))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def recv(key, nb_out)
|
83
|
+
begin
|
84
|
+
@replock.lock
|
85
|
+
r = @responses[key] ||= Queue.new
|
86
|
+
@replock.unlock
|
87
|
+
while rep = r.pop
|
88
|
+
if rep[1] == nb_out # On check ke c'est bien la réponse que l'on attend
|
89
|
+
return rep[0]
|
90
|
+
end
|
91
|
+
#Error.create(:backtrace => "Error in Brb receiving in thread #{Thread.current}. Try to get #{nb_out} request id, but get #{rep[1]}", :type => 'BrBRecvBadId', :url => "#{@object.class}-#{@uri}")
|
92
|
+
if rep[1] > nb_out
|
93
|
+
return nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
rescue Exception => e
|
97
|
+
if @close_after_timeout == true
|
98
|
+
#Error.create(:backtrace => "Error in Rcv in thread #{Thread.current}. Try to get #{nb_out} => Stop service #{e.class.to_s}\n\n#{e.backtrace.join("\n")}", :type => e.to_s, :url => "#{@object.class}-#{@uri}")
|
99
|
+
stop_service
|
100
|
+
sleep 1
|
101
|
+
raise e
|
102
|
+
else
|
103
|
+
raise e
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe :brb_service do
|
4
|
+
before(:all) do
|
5
|
+
@brb = BrB::Service.instance
|
6
|
+
@brb.stop_service
|
7
|
+
@brb_test = BrBTest.new
|
8
|
+
open_service(@brb_test)
|
9
|
+
@clients = []
|
10
|
+
20.times do
|
11
|
+
@clients << connect_to_the_service(self, @brb.uri) do |type, tunnel|
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Start the service
|
17
|
+
it "should the service be started" do
|
18
|
+
@clients.each do |cl|
|
19
|
+
cl.should_not be_nil
|
20
|
+
cl.active?.should be_true
|
21
|
+
cl.uri.should == @brb.uri
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should works with massive simple messaging" do
|
26
|
+
nb_call_before = @brb_test.nb_call || 0
|
27
|
+
nb_call_to_do = 500
|
28
|
+
|
29
|
+
@clients.each do |cl|
|
30
|
+
nb_call_to_do.times do
|
31
|
+
cl.noarg
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
sleep 6
|
36
|
+
# Wait a little in order to be sure all the stack is processed
|
37
|
+
@brb_test.last_call.should == :noarg
|
38
|
+
@brb_test.nb_call.should == (nb_call_to_do * @clients.size) + nb_call_before
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should works with massive simple messaging including blocking messaging" do
|
42
|
+
nb_call_before = @brb_test.nb_call || 0
|
43
|
+
nb_call_to_do = 500
|
44
|
+
nb_call_blocking_to_do = 50
|
45
|
+
|
46
|
+
t = Thread.new do
|
47
|
+
@clients.each do |cl|
|
48
|
+
nb_call_blocking_to_do.times do
|
49
|
+
val = Time.now.to_f
|
50
|
+
cl.return_same_value_block(val).should == val
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
@clients.each do |cl|
|
56
|
+
nb_call_to_do.times do
|
57
|
+
cl.noarg
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
sleep 6
|
62
|
+
t.join
|
63
|
+
# Wait a little in order to be sure all the stack is processed
|
64
|
+
@brb_test.nb_call.should == (nb_call_to_do * @clients.size + nb_call_blocking_to_do * @clients.size) + nb_call_before
|
65
|
+
end
|
66
|
+
|
67
|
+
# Finally, stop the service
|
68
|
+
it "should stop the service after usage" do
|
69
|
+
@brb.stop_service
|
70
|
+
@brb.uri.should be_nil
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe :brb_service do
|
4
|
+
before(:all) do
|
5
|
+
@brb = BrB::Service.instance
|
6
|
+
@brb.stop_service
|
7
|
+
@brb_test = BrBTest.new
|
8
|
+
open_service(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Start the service
|
12
|
+
it "should open a service on localhost:6200" do
|
13
|
+
@brb.uri.should_not be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
# Finally, stop the service
|
17
|
+
it "should stop the service" do
|
18
|
+
@brb.stop_service
|
19
|
+
@brb.uri.should be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should start again the service after a stop" do
|
23
|
+
open_service(self)
|
24
|
+
@brb.stop_service
|
25
|
+
open_service(self)
|
26
|
+
@brb.uri.should_not be_nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
$last_unregistered = nil
|
4
|
+
$last_registered = nil
|
5
|
+
|
6
|
+
describe :brb_service do
|
7
|
+
before(:all) do
|
8
|
+
@brb = BrB::Service.instance
|
9
|
+
@brb.stop_service
|
10
|
+
@brb_test = BrBTest.new
|
11
|
+
open_service(@brb_test)
|
12
|
+
@client = connect_to_the_service(self, @brb.uri) do |type, tunnel|
|
13
|
+
if type == :unregister
|
14
|
+
$last_unregistered = tunnel
|
15
|
+
elsif type == :register
|
16
|
+
$last_registered = tunnel
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Start the service
|
22
|
+
it "should the service be started" do
|
23
|
+
@client.should_not be_nil
|
24
|
+
@client.active?.should be_true
|
25
|
+
@client.uri.should == @brb.uri
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have get the register message" do
|
29
|
+
sleep 0.2 # Be sure to get the message
|
30
|
+
$last_registered.class.should == @client.class
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should correctly call simple distant method without args and without return" do
|
34
|
+
@client.noarg
|
35
|
+
sleep 0.2
|
36
|
+
# Wait a little in order to be sure the method is called
|
37
|
+
@brb_test.last_call.should == :noarg
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should correctly call simple distant method without args and without return multipe times" do
|
41
|
+
nb_call_before = @brb_test.nb_call
|
42
|
+
nb_call_to_do = 50
|
43
|
+
|
44
|
+
nb_call_to_do.times do
|
45
|
+
@client.noarg
|
46
|
+
end
|
47
|
+
sleep 0.2
|
48
|
+
# Wait a little in order to be sure the method is called
|
49
|
+
@brb_test.last_call.should == :noarg
|
50
|
+
@brb_test.nb_call.should == nb_call_to_do + nb_call_before
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should correctly call distant method with one argument" do
|
54
|
+
@client.very_long(:hello)
|
55
|
+
sleep 0.2
|
56
|
+
# Wait a little in order to be sure the method is called
|
57
|
+
@brb_test.last_args.should == [:hello]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should correctly call distant method with multiple arguments" do
|
61
|
+
args = [:one, :two, 3, "four"]
|
62
|
+
@client.fourargs(*args)
|
63
|
+
sleep 0.2
|
64
|
+
# Wait a little in order to be sure the method is called
|
65
|
+
@brb_test.last_args.should == args
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should correctly return arguments symbol value" do
|
69
|
+
@client.one_arg_with_return_block(:hello).should == :hello
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should correctly return arguments string value" do
|
73
|
+
@client.one_arg_with_return_block('hello').should == 'hello'
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should correctly return arguments Fixnum value" do
|
77
|
+
@client.one_arg_with_return_block(42).should == 42
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should correctly return arguments Float value" do
|
81
|
+
@client.one_arg_with_return_block(42.42).should == 42.42
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should correctly return arguments Table value" do
|
85
|
+
@client.one_arg_with_return_block([:one, :two, 3, "four"]).should == [:one, :two, 3, "four"]
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should correctly return arguments Hash value" do
|
89
|
+
h = {:yoyo => :titi, "salut" => 45}
|
90
|
+
@client.one_arg_with_return_block(h).should == h
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should dump to symbol undumpable value by using the proxy" do
|
94
|
+
@client.return_same_value_block(Thread.current).class.should == Symbol
|
95
|
+
@client.return_same_value_block(Thread.current).should == Thread.current.to_s.to_sym
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should transmit with success exception when blocking" do
|
99
|
+
e = nil
|
100
|
+
begin
|
101
|
+
@client.notavalidmeth_block
|
102
|
+
rescue Exception => e
|
103
|
+
end
|
104
|
+
e.should be_a NameError
|
105
|
+
end
|
106
|
+
|
107
|
+
# Finally, stop the service
|
108
|
+
it "should stop the service after usage" do
|
109
|
+
@brb.stop_service
|
110
|
+
@brb.uri.should be_nil
|
111
|
+
end
|
112
|
+
|
113
|
+
# Finally, stop the service
|
114
|
+
it "should stop the tunnel after usage" do
|
115
|
+
@client.stop_service
|
116
|
+
@client.active?.should_not be_true
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should have get the unregister message" do
|
120
|
+
sleep 0.2 # Be sure to get the message
|
121
|
+
$last_unregistered.class.should == @client.class
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
|
4
|
+
Thread.abort_on_exception = true
|
5
|
+
|
6
|
+
require File.dirname(__FILE__) + '/../init.rb'
|
7
|
+
|
8
|
+
def open_service(object, host = 'localhost', port = 6200)
|
9
|
+
if !EM::reactor_running?
|
10
|
+
# Launch event machine reactor
|
11
|
+
Thread.new do
|
12
|
+
EM::run do
|
13
|
+
EM::set_quantum(20)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
sleep 0.05
|
17
|
+
end
|
18
|
+
r = nil
|
19
|
+
EM.schedule do
|
20
|
+
r = BrB::Service.instance.start_service(:object => object, :silent => true, :host => host, :port => port)
|
21
|
+
end
|
22
|
+
sleep 0.1
|
23
|
+
return r
|
24
|
+
end
|
25
|
+
|
26
|
+
def connect_to_the_service(object_exposed, uri, &block)
|
27
|
+
r = nil
|
28
|
+
EM.schedule do
|
29
|
+
r = BrB::Tunnel.create(object_exposed, uri, :silent => true, &block)
|
30
|
+
end
|
31
|
+
sleep 0.1
|
32
|
+
return r
|
33
|
+
end
|
34
|
+
|
35
|
+
class BrBTest
|
36
|
+
attr_reader :last_call
|
37
|
+
attr_reader :last_args
|
38
|
+
attr_reader :nb_call
|
39
|
+
|
40
|
+
def increment_nb_call(call_name, *args)
|
41
|
+
@last_call = call_name
|
42
|
+
@last_args = args
|
43
|
+
@nb_call ||= 0
|
44
|
+
@nb_call += 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def very_long(ar)
|
48
|
+
increment_nb_call(:very_long, ar)
|
49
|
+
end
|
50
|
+
|
51
|
+
def fourargs(arg1, arg2, arg3, arg4)
|
52
|
+
increment_nb_call(:fourargs, arg1, arg2, arg3, arg4)
|
53
|
+
end
|
54
|
+
|
55
|
+
def noarg
|
56
|
+
increment_nb_call(:noarg)
|
57
|
+
end
|
58
|
+
|
59
|
+
def one_arg_with_return(ar)
|
60
|
+
increment_nb_call(:one_arg_with_return)
|
61
|
+
return ar
|
62
|
+
end
|
63
|
+
|
64
|
+
def return_same_value(val)
|
65
|
+
increment_nb_call(:return_same_value)
|
66
|
+
return val
|
67
|
+
end
|
68
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: brb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Guillaume Luccisano
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-04-16 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: eventmachine
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
description: BrB is a simple and extremely fast interface for doing simple distributed ruby and message passing
|
33
|
+
email: guillaume.luccisano@gmail.com
|
34
|
+
executables: []
|
35
|
+
|
36
|
+
extensions: []
|
37
|
+
|
38
|
+
extra_rdoc_files: []
|
39
|
+
|
40
|
+
files:
|
41
|
+
- examples/simple_client.rb
|
42
|
+
- examples/simple_core.rb
|
43
|
+
- lib/brb/event_machine.rb
|
44
|
+
- lib/brb/exception.rb
|
45
|
+
- lib/brb/request.rb
|
46
|
+
- lib/brb/tunnel/shared.rb
|
47
|
+
- lib/brb/tunnel.rb
|
48
|
+
- lib/brb.rb
|
49
|
+
- spec/brb/brb_massive_usage_spec.rb
|
50
|
+
- spec/brb/brb_service_spec.rb
|
51
|
+
- spec/brb/brb_tunnel_spec.rb
|
52
|
+
- spec/spec_helper.rb
|
53
|
+
- CHANGELOG.rdoc
|
54
|
+
- MIT-LICENSE
|
55
|
+
- Rakefile
|
56
|
+
- README.rdoc
|
57
|
+
- init.rb
|
58
|
+
has_rdoc: true
|
59
|
+
homepage: http://github.com/kwi/BrB
|
60
|
+
licenses: []
|
61
|
+
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
segments:
|
79
|
+
- 1
|
80
|
+
- 3
|
81
|
+
- 4
|
82
|
+
version: 1.3.4
|
83
|
+
requirements:
|
84
|
+
- eventmachine
|
85
|
+
rubyforge_project: brb
|
86
|
+
rubygems_version: 1.3.6
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: BrB is a simple and extremely fast interface for doing simple distributed ruby
|
90
|
+
test_files: []
|
91
|
+
|