brb 0.2.0
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/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
|
+
|