ma-zmq 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +26 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +39 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/benchmark/bench.rb +57 -0
- data/benchmark/results.txt +23 -0
- data/lib/ma-zmq.rb +28 -0
- data/lib/ma-zmq/connection_handler.rb +73 -0
- data/lib/ma-zmq/proxy.rb +56 -0
- data/lib/ma-zmq/proxy/backend.rb +70 -0
- data/lib/ma-zmq/proxy/balancer.rb +43 -0
- data/lib/ma-zmq/pull.rb +49 -0
- data/lib/ma-zmq/push.rb +10 -0
- data/lib/ma-zmq/reply.rb +34 -0
- data/lib/ma-zmq/request.rb +66 -0
- data/lib/ma-zmq/socket_handler.rb +137 -0
- data/ma-zmq.gemspec +76 -0
- data/test/helper.rb +18 -0
- data/test/test_ma-zmq.rb +7 -0
- metadata +178 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
#gem "em-zeromq", "= 0.2.2"
|
6
|
+
|
7
|
+
gem "eventmachine", "= 0.12.10"
|
8
|
+
gem "ffi-rzmq", "= 0.9.3"
|
9
|
+
|
10
|
+
# Add dependencies to develop your gem here.
|
11
|
+
# Include everything needed to run rake, tests, features, etc.
|
12
|
+
group :development do
|
13
|
+
gem "shoulda", ">= 0"
|
14
|
+
gem "bundler", "~> 1.0.0"
|
15
|
+
gem "jeweler", "~> 1.6.4"
|
16
|
+
gem "rcov", ">= 0"
|
17
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
eventmachine (0.12.10)
|
5
|
+
ffi (1.0.11)
|
6
|
+
ffi-rzmq (0.9.3)
|
7
|
+
ffi
|
8
|
+
git (1.2.5)
|
9
|
+
jeweler (1.6.4)
|
10
|
+
bundler (~> 1.0)
|
11
|
+
git (>= 1.2.5)
|
12
|
+
rake
|
13
|
+
rake (0.9.2)
|
14
|
+
rcov (0.9.10)
|
15
|
+
shoulda (2.11.3)
|
16
|
+
|
17
|
+
PLATFORMS
|
18
|
+
ruby
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
bundler (~> 1.0.0)
|
22
|
+
eventmachine (= 0.12.10)
|
23
|
+
ffi-rzmq (= 0.9.3)
|
24
|
+
jeweler (~> 1.6.4)
|
25
|
+
rcov
|
26
|
+
shoulda
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Fernando Alonso
|
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,39 @@
|
|
1
|
+
= ma-zmq
|
2
|
+
|
3
|
+
MaZMQ was born as an extension from EMZeroMQ, now aims to add more features than its inspiration. Its first purpose was to have a RoundRobin handler to work with ZMQ sockets.
|
4
|
+
Which by this moment works, but is very restricted, and harcoded too. Then I started adding some sugar, studying both EM and ZMQ and turned out to be independent gem.
|
5
|
+
As a piece of another project, MaZMQ will be updated in the following.
|
6
|
+
|
7
|
+
Hope you enjoy it !
|
8
|
+
|
9
|
+
= Using MaZMQ
|
10
|
+
|
11
|
+
EM.run do
|
12
|
+
request = MaZMQ::Request.new
|
13
|
+
request.timeout(5)
|
14
|
+
request.connect(:tcp, '127.0.0.1', 3200)
|
15
|
+
|
16
|
+
request.on_timeout {
|
17
|
+
puts "Server has no response!"
|
18
|
+
}
|
19
|
+
request.on_read { |msg|
|
20
|
+
puts "Received: #{msg}"
|
21
|
+
}
|
22
|
+
request.send_string('ping!')
|
23
|
+
end
|
24
|
+
|
25
|
+
== Contributing to ma-zmq
|
26
|
+
|
27
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
28
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
29
|
+
* Fork the project
|
30
|
+
* Start a feature/bugfix branch
|
31
|
+
* Commit and push until you are happy with your contribution
|
32
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
33
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
34
|
+
|
35
|
+
== Copyright
|
36
|
+
|
37
|
+
Copyright (c) 2011 Fernando Alonso. See LICENSE.txt for
|
38
|
+
further details.
|
39
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "ma-zmq"
|
18
|
+
gem.homepage = "http://github.com/al-nattahnam/ma-zmq"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{RoundRobin handler for ZMQ over EventedMachine}
|
21
|
+
gem.description = %Q{RoundRobin handler for ZMQ over EventedMachine}
|
22
|
+
gem.email = "krakatoa1987@gmail.com"
|
23
|
+
gem.authors = ["Fernando Alonso"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
Rcov::RcovTask.new do |test|
|
37
|
+
test.libs << 'test'
|
38
|
+
test.pattern = 'test/**/test_*.rb'
|
39
|
+
test.verbose = true
|
40
|
+
test.rcov_opts << '--exclude "gems/*"'
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "ma-zmq #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/benchmark/bench.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ma-zmq'
|
3
|
+
require 'benchmark'
|
4
|
+
#require 'ruby-prof'
|
5
|
+
|
6
|
+
requests = 10000
|
7
|
+
concurrency = 5
|
8
|
+
per_fork = requests / concurrency
|
9
|
+
|
10
|
+
@@sent = 0
|
11
|
+
@@tries = 0
|
12
|
+
@@read = 0
|
13
|
+
|
14
|
+
concurrency.times {
|
15
|
+
fork do
|
16
|
+
puts Benchmark.realtime {
|
17
|
+
#result = RubyProf.profile {
|
18
|
+
EM.run do
|
19
|
+
req = MaZMQ::Proxy.new
|
20
|
+
req.connect :tcp, '127.0.0.1', 3340
|
21
|
+
req.connect :tcp, '127.0.0.1', 3341
|
22
|
+
req.connect :tcp, '127.0.0.1', 3342
|
23
|
+
req.connect :tcp, '127.0.0.1', 3343
|
24
|
+
req.connect :tcp, '127.0.0.1', 3343
|
25
|
+
|
26
|
+
#req.connect :tcp, '127.0.0.1', 3344
|
27
|
+
#req.connect :tcp, '127.0.0.1', 3345
|
28
|
+
#req.connect :tcp, '127.0.0.1', 3346
|
29
|
+
#req.connect :tcp, '127.0.0.1', 3347
|
30
|
+
req.timeout(0.5)
|
31
|
+
req.on_read { |m|
|
32
|
+
@@read += 1
|
33
|
+
}
|
34
|
+
req.on_timeout {
|
35
|
+
puts 'timeout!'
|
36
|
+
}
|
37
|
+
EM.add_periodic_timer(0.000001) do
|
38
|
+
@@tries += 1
|
39
|
+
if @@sent < per_fork
|
40
|
+
if req.send_string('test')
|
41
|
+
@@sent += 1
|
42
|
+
end
|
43
|
+
else
|
44
|
+
if @@read >= per_fork
|
45
|
+
EM.stop
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
#}
|
51
|
+
}
|
52
|
+
#printer = RubyProf::FlatPrinter.new(result)
|
53
|
+
#printer.print(STDOUT, 0)
|
54
|
+
puts @@read
|
55
|
+
puts "Tries: #{@@tries}"
|
56
|
+
end
|
57
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
10000 requests (@ 0.00001)
|
2
|
+
|
3
|
+
c1
|
4
|
+
--
|
5
|
+
2x MaZMQ::Reply = 35 secs (285.71 req/s)
|
6
|
+
3x MaZMQ::Reply = 54 secs (185.18 req/s)
|
7
|
+
4x MaZMQ::Reply = 63 secs (158.73 req/s) Mid performance
|
8
|
+
|
9
|
+
c5
|
10
|
+
--
|
11
|
+
4x MaZMQ::Reply = 29 secs (344.82 req/s) Best performance
|
12
|
+
3x MaZMQ::Reply = 35 secs (285.71 req/s)
|
13
|
+
2x MaZMQ::Reply = 39 secs (256.41 req/s)
|
14
|
+
|
15
|
+
c10
|
16
|
+
---
|
17
|
+
4x MaZMQ::Reply = 39 secs (256.41 req/s)
|
18
|
+
3x MaZMQ::Reply = 54 secs (185.18 req/s)
|
19
|
+
2x MaZMQ::Reply = 78 secs (128.20 req/s) Worst performance
|
20
|
+
|
21
|
+
c20 (r1.9.2)
|
22
|
+
---
|
23
|
+
8x MaZMQ::Reply = 7.8 secs (1282 req/s)
|
data/lib/ma-zmq.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'ffi-rzmq'
|
3
|
+
|
4
|
+
require 'ma-zmq/connection_handler'
|
5
|
+
require 'ma-zmq/socket_handler'
|
6
|
+
|
7
|
+
require 'ma-zmq/request'
|
8
|
+
require 'ma-zmq/reply'
|
9
|
+
|
10
|
+
require 'ma-zmq/push'
|
11
|
+
require 'ma-zmq/pull'
|
12
|
+
|
13
|
+
require 'ma-zmq/proxy/backend'
|
14
|
+
require 'ma-zmq/proxy/balancer'
|
15
|
+
require 'ma-zmq/proxy'
|
16
|
+
|
17
|
+
module MaZMQ
|
18
|
+
@@context = nil
|
19
|
+
def self.context
|
20
|
+
# Como MaZMQ estaria funcionando siempre en EM, el proceso en el cual corre seria siempre unico, y por esa razon (repasando http://zguide.zeromq.org/page:all#Getting-the-Context-Right), usamos un unico Contexto en toda la aplicacion. Y el usuario no tiene que instanciar uno.
|
21
|
+
@@context ||= ZMQ::Context.new
|
22
|
+
@@context
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.terminate
|
26
|
+
@@context.terminate if @@context
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module MaZMQ
|
2
|
+
class ConnectionHandler < EM::Connection
|
3
|
+
def initialize(socket_handler)
|
4
|
+
@socket_handler = socket_handler
|
5
|
+
@socket_type = socket_handler.socket_type
|
6
|
+
|
7
|
+
@on_read_lambda = lambda {|m|}
|
8
|
+
@on_write_lambda = lambda {|m|}
|
9
|
+
@on_timeout_lambda = lambda {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_read(block)
|
13
|
+
@on_read_lambda = block
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_write(block)
|
17
|
+
@on_write_lambda = block
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_timeout(block)
|
21
|
+
@on_timeout_lambda = block
|
22
|
+
end
|
23
|
+
|
24
|
+
def notify_readable
|
25
|
+
#if @socket_handler.socket_type == ZMQ::REP
|
26
|
+
# if @socket_handler.state == :idle
|
27
|
+
# msg = try_read
|
28
|
+
# if msg
|
29
|
+
# @on_read_lambda.call(msg)
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#end
|
33
|
+
end
|
34
|
+
|
35
|
+
def notify_writable
|
36
|
+
case @socket_type
|
37
|
+
when ZMQ::REP
|
38
|
+
if @socket_handler.state == :idle
|
39
|
+
msg = @socket_handler.recv_string
|
40
|
+
if msg and not msg.empty?
|
41
|
+
@on_read_lambda.call(msg)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
when ZMQ::REQ
|
45
|
+
if @socket_handler.state == :sending
|
46
|
+
msg = @socket_handler.recv_string
|
47
|
+
end
|
48
|
+
case @socket_handler.state
|
49
|
+
when :idle
|
50
|
+
if msg and not msg.empty?
|
51
|
+
@on_read_lambda.call(msg)
|
52
|
+
end
|
53
|
+
when :timeout
|
54
|
+
@on_timeout_lambda.call #(@socket_handler.identity)
|
55
|
+
puts "SocketHandler: #{@socket_handler.identity} timeout!"
|
56
|
+
self.detach
|
57
|
+
end
|
58
|
+
when ZMQ::PULL
|
59
|
+
msg = @socket_handler.recv_string
|
60
|
+
case @socket_handler.state
|
61
|
+
when :idle
|
62
|
+
if msg and not msg.empty?
|
63
|
+
@on_read_lambda.call(msg)
|
64
|
+
end
|
65
|
+
when :timeout
|
66
|
+
@on_timeout_lambda.call #(@socket_handler.identity)
|
67
|
+
puts "SocketHandler: #{@socket_handler.identity} timeout!"
|
68
|
+
self.detach
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/ma-zmq/proxy.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module MaZMQ
|
2
|
+
class Proxy
|
3
|
+
include MaZMQ::Proxy::Backend
|
4
|
+
|
5
|
+
#@@id = 0
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@balancer = MaZMQ::Proxy::Balancer.new
|
9
|
+
|
10
|
+
# @max_timeouts = 5 # TODO
|
11
|
+
# @max_timeouted = 1
|
12
|
+
# @max_retries
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_string(msg)
|
18
|
+
@current = current_socket
|
19
|
+
return false if @current.is_a? NilClass
|
20
|
+
case @current.state
|
21
|
+
when :idle
|
22
|
+
@current.send_string(msg)
|
23
|
+
else
|
24
|
+
return false
|
25
|
+
end
|
26
|
+
@balancer.next
|
27
|
+
end
|
28
|
+
|
29
|
+
def connect(protocol, address, port)
|
30
|
+
return false if addresses.include? "#{protocol}://#{address}:#{port.to_s}"
|
31
|
+
index = super(protocol, address, port)
|
32
|
+
#@balancer.curent refresh ! TODO
|
33
|
+
@balancer.add(index)
|
34
|
+
index
|
35
|
+
end
|
36
|
+
|
37
|
+
def disconnect(index)
|
38
|
+
@balancer.remove(index)
|
39
|
+
super(index)
|
40
|
+
end
|
41
|
+
|
42
|
+
def close
|
43
|
+
@sockets.each do |s|
|
44
|
+
s.close
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def addresses
|
49
|
+
@sockets.collect{|s| s.addresses}.flatten
|
50
|
+
end
|
51
|
+
|
52
|
+
def current_socket
|
53
|
+
@sockets[@balancer.current]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module MaZMQ
|
2
|
+
class Proxy
|
3
|
+
module Backend
|
4
|
+
def initialize
|
5
|
+
@sockets = []
|
6
|
+
|
7
|
+
@timeout = nil # TODO individual timeouts for different sockets
|
8
|
+
end
|
9
|
+
|
10
|
+
def connect(protocol, address, port)
|
11
|
+
# validate as in SocketHandler
|
12
|
+
request = MaZMQ::Request.new
|
13
|
+
request.connect(protocol, address, port)
|
14
|
+
if EM.reactor_running?
|
15
|
+
request.timeout(@timeout)
|
16
|
+
if @on_read_lambda.is_a? Proc
|
17
|
+
request.on_read { |msg|
|
18
|
+
@on_read_lambda.call(msg)
|
19
|
+
}
|
20
|
+
end
|
21
|
+
if @on_timeout_lambda.is_a? Proc
|
22
|
+
request.on_timeout {
|
23
|
+
@on_timeout_lambda.call
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#request.identity = "lb-#{@@id}"
|
29
|
+
#@@id += 1
|
30
|
+
@sockets << request
|
31
|
+
@sockets.size - 1
|
32
|
+
end
|
33
|
+
|
34
|
+
#def reconnect(index)
|
35
|
+
#end
|
36
|
+
|
37
|
+
def disconnect(index)
|
38
|
+
socket = @sockets.delete_at(index)
|
39
|
+
socket.close
|
40
|
+
end
|
41
|
+
|
42
|
+
def timeout(secs)
|
43
|
+
@timeout = secs
|
44
|
+
@sockets.each do |s|
|
45
|
+
s.timeout @timeout
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_timeout(&block)
|
50
|
+
return false if not EM.reactor_running?
|
51
|
+
@on_timeout_lambda = block
|
52
|
+
@sockets.each do |socket|
|
53
|
+
socket.on_timeout {
|
54
|
+
@on_timeout_lambda.call
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_read(&block)
|
60
|
+
return false if not EM.reactor_running?
|
61
|
+
@on_read_lambda = block
|
62
|
+
@sockets.each do |socket|
|
63
|
+
socket.on_read { |msg|
|
64
|
+
@on_read_lambda.call(msg)
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module MaZMQ
|
2
|
+
class Proxy
|
3
|
+
class Balancer
|
4
|
+
@@strategies = [
|
5
|
+
:round_robin,
|
6
|
+
:connections,
|
7
|
+
:load, # Cucub podria usar algo como 'less_jobs'
|
8
|
+
:priority,
|
9
|
+
:directed
|
10
|
+
]
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
self.strategy = :round_robin # default strategy is round_robin
|
14
|
+
@index = []
|
15
|
+
# @data = [] # connections, load
|
16
|
+
end
|
17
|
+
|
18
|
+
def strategy=(strategy)
|
19
|
+
return false if not @@strategies.include? strategy
|
20
|
+
@strategy = strategy
|
21
|
+
end
|
22
|
+
|
23
|
+
def add(index)
|
24
|
+
@index << index
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove(index)
|
28
|
+
@index.delete(index)
|
29
|
+
end
|
30
|
+
|
31
|
+
def current
|
32
|
+
@index[0]
|
33
|
+
end
|
34
|
+
|
35
|
+
def next
|
36
|
+
case @strategy
|
37
|
+
when :round_robin
|
38
|
+
@index.push(@index.shift)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/ma-zmq/pull.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module MaZMQ
|
2
|
+
class Pull < MaZMQ::SocketHandler
|
3
|
+
attr_reader :state
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@socket_type = ZMQ::PULL
|
7
|
+
|
8
|
+
@last_try = nil
|
9
|
+
|
10
|
+
@timeout = false
|
11
|
+
# @cooldown
|
12
|
+
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def recv_string
|
17
|
+
if @state == :idle
|
18
|
+
@state = :pulling
|
19
|
+
end
|
20
|
+
case @state
|
21
|
+
when :pulling
|
22
|
+
@last_try ||= Time.now if @timeout
|
23
|
+
|
24
|
+
msg = super
|
25
|
+
|
26
|
+
if msg.empty?
|
27
|
+
if @timeout and (Time.now - @last_try) > @timeout
|
28
|
+
@state = :timeout
|
29
|
+
end
|
30
|
+
else
|
31
|
+
@last_try = nil if @timeout
|
32
|
+
@state = :idle
|
33
|
+
end
|
34
|
+
return msg
|
35
|
+
when :timeout
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def timeout(secs)
|
41
|
+
@timeout = secs
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_timeout(&block)
|
45
|
+
return false if not @connection or block.arity != -1
|
46
|
+
@connection.on_timeout(block)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/ma-zmq/push.rb
ADDED
data/lib/ma-zmq/reply.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module MaZMQ
|
2
|
+
class Reply < MaZMQ::SocketHandler
|
3
|
+
attr_reader :state
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@socket_type = ZMQ::REP
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def recv_string
|
11
|
+
case @state
|
12
|
+
when :idle
|
13
|
+
msg = super
|
14
|
+
if msg and not msg.empty?
|
15
|
+
@state = :reply
|
16
|
+
end
|
17
|
+
return msg
|
18
|
+
else
|
19
|
+
return false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def send_string(msg)
|
24
|
+
case @state
|
25
|
+
when :reply
|
26
|
+
resp = super(msg)
|
27
|
+
@state = :idle
|
28
|
+
return resp
|
29
|
+
else
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module MaZMQ
|
2
|
+
class Request < MaZMQ::SocketHandler
|
3
|
+
attr_reader :state
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@socket_type = ZMQ::REQ
|
7
|
+
|
8
|
+
@last_try = nil
|
9
|
+
|
10
|
+
@timeout = false
|
11
|
+
# @cooldown
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def reconnect
|
16
|
+
close
|
17
|
+
@socket = MaZMQ::context.socket(@socket_type)
|
18
|
+
@addresses.each do |zmq_address|
|
19
|
+
@socket.connect zmq_address
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def send_string(msg)
|
24
|
+
case @state
|
25
|
+
when :idle
|
26
|
+
super(msg)
|
27
|
+
@state = :sending
|
28
|
+
return @state
|
29
|
+
else
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def recv_string
|
35
|
+
case @state
|
36
|
+
when :idle
|
37
|
+
return false
|
38
|
+
when :sending
|
39
|
+
@last_try ||= Time.now if @timeout
|
40
|
+
|
41
|
+
msg = super
|
42
|
+
|
43
|
+
if msg.empty?
|
44
|
+
if @timeout and (Time.now - @last_try) > @timeout
|
45
|
+
@state = :timeout
|
46
|
+
end
|
47
|
+
else
|
48
|
+
@last_try = nil if @timeout
|
49
|
+
@state = :idle
|
50
|
+
end
|
51
|
+
return msg
|
52
|
+
when :timeout
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def timeout(secs)
|
58
|
+
@timeout = secs
|
59
|
+
end
|
60
|
+
|
61
|
+
def on_timeout(&block)
|
62
|
+
return false if not @connection or block.arity != -1
|
63
|
+
@connection.on_timeout(block)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module MaZMQ
|
2
|
+
class SocketHandler
|
3
|
+
@@protocols = [:tcp, :inproc, :ipc] #, :pgm]
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@socket = MaZMQ::context.socket(@socket_type)
|
7
|
+
@socket.setsockopt(ZMQ::LINGER, 0)
|
8
|
+
@addresses = []
|
9
|
+
|
10
|
+
if EM.reactor_running?
|
11
|
+
build_connection
|
12
|
+
else
|
13
|
+
@connection = false
|
14
|
+
end
|
15
|
+
|
16
|
+
@state = :unavailable
|
17
|
+
end
|
18
|
+
|
19
|
+
def connect(protocol, address, port=nil)
|
20
|
+
# check multiple connects for ZMQ::REQ should RoundRobin
|
21
|
+
zmq_address = MaZMQ::SocketHandler.valid_address(protocol, address, port)
|
22
|
+
return false if not zmq_address.is_a? String
|
23
|
+
|
24
|
+
@socket.connect(zmq_address)
|
25
|
+
|
26
|
+
@addresses << zmq_address
|
27
|
+
|
28
|
+
if @state == :unavailable
|
29
|
+
@state = :idle
|
30
|
+
end
|
31
|
+
@state
|
32
|
+
end
|
33
|
+
|
34
|
+
def bind(protocol, address, port=nil)
|
35
|
+
# check once binded should not bind anymore
|
36
|
+
zmq_address = MaZMQ::SocketHandler.valid_address(protocol, address, port)
|
37
|
+
return false if not zmq_address.is_a? String
|
38
|
+
|
39
|
+
@socket.bind(zmq_address)
|
40
|
+
|
41
|
+
@addresses << zmq_address
|
42
|
+
|
43
|
+
if @state == :unavailable
|
44
|
+
@state = :idle
|
45
|
+
end
|
46
|
+
@state
|
47
|
+
end
|
48
|
+
|
49
|
+
def close
|
50
|
+
@socket.close
|
51
|
+
#if use_eventmachine
|
52
|
+
# @connection.detach
|
53
|
+
#end
|
54
|
+
end
|
55
|
+
|
56
|
+
def addresses
|
57
|
+
@addresses
|
58
|
+
end
|
59
|
+
|
60
|
+
def send_string(msg)
|
61
|
+
@socket.send_string(msg)
|
62
|
+
end
|
63
|
+
|
64
|
+
def recv_string(flags=nil)
|
65
|
+
msg = ''
|
66
|
+
flags ||= ZMQ::NOBLOCK
|
67
|
+
@socket.recv_string(msg, flags)
|
68
|
+
return msg
|
69
|
+
end
|
70
|
+
|
71
|
+
def socket_type
|
72
|
+
@socket_type
|
73
|
+
end
|
74
|
+
|
75
|
+
def on_read(&block)
|
76
|
+
return false if not @connection or block.arity != 1
|
77
|
+
@connection.on_read(block)
|
78
|
+
end
|
79
|
+
|
80
|
+
def on_write(&block)
|
81
|
+
return false if not @connection or block.arity != 1
|
82
|
+
@connection.on_write(block)
|
83
|
+
end
|
84
|
+
|
85
|
+
def identity
|
86
|
+
arr = []
|
87
|
+
@socket.getsockopt(ZMQ::IDENTITY, arr)
|
88
|
+
arr[0].to_sym rescue nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def identity=(identity)
|
92
|
+
@socket.setsockopt(ZMQ::IDENTITY, identity.to_s)
|
93
|
+
end
|
94
|
+
|
95
|
+
protected
|
96
|
+
def self.valid_address(protocol, address, port=nil)
|
97
|
+
case protocol
|
98
|
+
when :tcp
|
99
|
+
if port.is_a? Numeric
|
100
|
+
return "#{protocol}://#{address}:#{port.to_s}"
|
101
|
+
else
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
when :ipc
|
105
|
+
# Chequear socket file
|
106
|
+
if port.is_a? NilClass
|
107
|
+
return "#{protocol}://#{address}"
|
108
|
+
else
|
109
|
+
return false
|
110
|
+
end
|
111
|
+
when :inproc
|
112
|
+
if port.is_a? NilClass
|
113
|
+
return "#{protocol}://#{address}"
|
114
|
+
else
|
115
|
+
return false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.valid_protocol?(protocol)
|
121
|
+
@@protocols.include? protocol
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
def build_connection
|
126
|
+
fd = []
|
127
|
+
@socket.getsockopt(ZMQ::FD, fd)
|
128
|
+
|
129
|
+
return nil if not ZMQ::Util.resultcode_ok? fd[0]
|
130
|
+
|
131
|
+
@connection = EM.watch(fd[0], MaZMQ::ConnectionHandler, self)
|
132
|
+
#@connection.notify_readable = true
|
133
|
+
@connection.notify_writable = true
|
134
|
+
@connection
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/ma-zmq.gemspec
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "ma-zmq"
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Fernando Alonso"]
|
12
|
+
s.date = "2012-03-20"
|
13
|
+
s.description = "RoundRobin handler for ZMQ over EventedMachine"
|
14
|
+
s.email = "krakatoa1987@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"benchmark/bench.rb",
|
28
|
+
"benchmark/results.txt",
|
29
|
+
"lib/ma-zmq.rb",
|
30
|
+
"lib/ma-zmq/connection_handler.rb",
|
31
|
+
"lib/ma-zmq/proxy.rb",
|
32
|
+
"lib/ma-zmq/proxy/backend.rb",
|
33
|
+
"lib/ma-zmq/proxy/balancer.rb",
|
34
|
+
"lib/ma-zmq/pull.rb",
|
35
|
+
"lib/ma-zmq/push.rb",
|
36
|
+
"lib/ma-zmq/reply.rb",
|
37
|
+
"lib/ma-zmq/request.rb",
|
38
|
+
"lib/ma-zmq/socket_handler.rb",
|
39
|
+
"ma-zmq.gemspec",
|
40
|
+
"test/helper.rb",
|
41
|
+
"test/test_ma-zmq.rb"
|
42
|
+
]
|
43
|
+
s.homepage = "http://github.com/al-nattahnam/ma-zmq"
|
44
|
+
s.licenses = ["MIT"]
|
45
|
+
s.require_paths = ["lib"]
|
46
|
+
s.rubygems_version = "1.8.10"
|
47
|
+
s.summary = "RoundRobin handler for ZMQ over EventedMachine"
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_runtime_dependency(%q<eventmachine>, ["= 0.12.10"])
|
54
|
+
s.add_runtime_dependency(%q<ffi-rzmq>, ["= 0.9.3"])
|
55
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
56
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
57
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
58
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<eventmachine>, ["= 0.12.10"])
|
61
|
+
s.add_dependency(%q<ffi-rzmq>, ["= 0.9.3"])
|
62
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
63
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
64
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
65
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
66
|
+
end
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<eventmachine>, ["= 0.12.10"])
|
69
|
+
s.add_dependency(%q<ffi-rzmq>, ["= 0.9.3"])
|
70
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
71
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
72
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
73
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'ma-zmq'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
data/test/test_ma-zmq.rb
ADDED
metadata
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ma-zmq
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Fernando Alonso
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-03-20 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
type: :runtime
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - "="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 59
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 12
|
31
|
+
- 10
|
32
|
+
version: 0.12.10
|
33
|
+
prerelease: false
|
34
|
+
name: eventmachine
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
type: :runtime
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - "="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 61
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
- 9
|
47
|
+
- 3
|
48
|
+
version: 0.9.3
|
49
|
+
prerelease: false
|
50
|
+
name: ffi-rzmq
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
type: :development
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
prerelease: false
|
64
|
+
name: shoulda
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
type: :development
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ~>
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 23
|
74
|
+
segments:
|
75
|
+
- 1
|
76
|
+
- 0
|
77
|
+
- 0
|
78
|
+
version: 1.0.0
|
79
|
+
prerelease: false
|
80
|
+
name: bundler
|
81
|
+
version_requirements: *id004
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
type: :development
|
84
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 7
|
90
|
+
segments:
|
91
|
+
- 1
|
92
|
+
- 6
|
93
|
+
- 4
|
94
|
+
version: 1.6.4
|
95
|
+
prerelease: false
|
96
|
+
name: jeweler
|
97
|
+
version_requirements: *id005
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
type: :development
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
prerelease: false
|
110
|
+
name: rcov
|
111
|
+
version_requirements: *id006
|
112
|
+
description: RoundRobin handler for ZMQ over EventedMachine
|
113
|
+
email: krakatoa1987@gmail.com
|
114
|
+
executables: []
|
115
|
+
|
116
|
+
extensions: []
|
117
|
+
|
118
|
+
extra_rdoc_files:
|
119
|
+
- LICENSE.txt
|
120
|
+
- README.rdoc
|
121
|
+
files:
|
122
|
+
- .document
|
123
|
+
- Gemfile
|
124
|
+
- Gemfile.lock
|
125
|
+
- LICENSE.txt
|
126
|
+
- README.rdoc
|
127
|
+
- Rakefile
|
128
|
+
- VERSION
|
129
|
+
- benchmark/bench.rb
|
130
|
+
- benchmark/results.txt
|
131
|
+
- lib/ma-zmq.rb
|
132
|
+
- lib/ma-zmq/connection_handler.rb
|
133
|
+
- lib/ma-zmq/proxy.rb
|
134
|
+
- lib/ma-zmq/proxy/backend.rb
|
135
|
+
- lib/ma-zmq/proxy/balancer.rb
|
136
|
+
- lib/ma-zmq/pull.rb
|
137
|
+
- lib/ma-zmq/push.rb
|
138
|
+
- lib/ma-zmq/reply.rb
|
139
|
+
- lib/ma-zmq/request.rb
|
140
|
+
- lib/ma-zmq/socket_handler.rb
|
141
|
+
- ma-zmq.gemspec
|
142
|
+
- test/helper.rb
|
143
|
+
- test/test_ma-zmq.rb
|
144
|
+
homepage: http://github.com/al-nattahnam/ma-zmq
|
145
|
+
licenses:
|
146
|
+
- MIT
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
|
150
|
+
require_paths:
|
151
|
+
- lib
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
hash: 3
|
158
|
+
segments:
|
159
|
+
- 0
|
160
|
+
version: "0"
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
none: false
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
hash: 3
|
167
|
+
segments:
|
168
|
+
- 0
|
169
|
+
version: "0"
|
170
|
+
requirements: []
|
171
|
+
|
172
|
+
rubyforge_project:
|
173
|
+
rubygems_version: 1.8.10
|
174
|
+
signing_key:
|
175
|
+
specification_version: 3
|
176
|
+
summary: RoundRobin handler for ZMQ over EventedMachine
|
177
|
+
test_files: []
|
178
|
+
|