ffi-rxs 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.travis.yml +10 -0
- data/.yardopts +9 -0
- data/CHANGELOG +11 -0
- data/LICENSE +20 -0
- data/README.textile +86 -0
- data/examples/LICENSE +18 -0
- data/examples/README +2 -0
- data/examples/durable_pub.rb +34 -0
- data/examples/durable_sub.rb +29 -0
- data/examples/latency_measurement.rb +140 -0
- data/examples/reply.rb +25 -0
- data/examples/req_rep_poll.rb +71 -0
- data/examples/request.rb +24 -0
- data/examples/task_sink.rb +31 -0
- data/examples/task_vent.rb +34 -0
- data/examples/task_worker.rb +36 -0
- data/examples/throughput_measurement.rb +169 -0
- data/examples/weather_upd_client.rb +47 -0
- data/examples/weather_upd_server.rb +35 -0
- data/examples/xreq_xrep_poll.rb +101 -0
- data/ffi-rxs.gemspec +3 -5
- data/lib/ffi-rxs/constants.rb +15 -11
- data/lib/ffi-rxs/context.rb +54 -60
- data/lib/ffi-rxs/exceptions.rb +7 -5
- data/lib/ffi-rxs/libc.rb +1 -0
- data/lib/ffi-rxs/libxs.rb +2 -0
- data/lib/ffi-rxs/message.rb +79 -50
- data/lib/ffi-rxs/poll.rb +54 -12
- data/lib/ffi-rxs/poll_items.rb +2 -1
- data/lib/ffi-rxs/socket.rb +243 -184
- data/lib/ffi-rxs/util.rb +25 -16
- data/lib/ffi-rxs/version.rb +3 -1
- data/lib/ffi-rxs.rb +8 -14
- data/spec/socket_spec.rb +63 -0
- metadata +31 -19
- data/README.rdoc +0 -86
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/CHANGELOG
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
= Version 1.0.1
|
2
|
+
|
3
|
+
* Re-working of some ZeroMQ examples for use with Crossroads I/O
|
4
|
+
* Addition of missing socket option XS::IPV4ONLY
|
5
|
+
* Addition of new socket option XS::KEEPALIVE
|
6
|
+
* Editing of inline comments for Yard documentation in Textile
|
7
|
+
* Amended .gemspec to exclude ext/libxs.so from build to accommodate Travis CI in development
|
8
|
+
|
9
|
+
= Version 1.0.0
|
10
|
+
|
11
|
+
* Initial release
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 - 2012 Chuck Remes, Chris Duncan and contributors
|
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 NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
h1. About ffi-rxs
|
2
|
+
|
3
|
+
This gem wraps the "Crossroads I/O":http://crossroads.io networking library using the Ruby FFI (foreign function interface). It's a pure Ruby wrapper so this gem can be loaded and run by any Ruby runtime that supports FFI. That's all of them: MRI 1.9.x, Rubinius and JRuby.
|
4
|
+
|
5
|
+
Crossroads I/O is a fork of ZeroMQ. This gem is a re-working of the ffi-rzmq gem created by Chuck Remes to provide bindings for the Crossroads I/O libxs C library instead of the ZeroMQ libzmq library. The gem auto-configures itself to expose the API conforming to the loaded C library.
|
6
|
+
|
7
|
+
h2. Features/Problems
|
8
|
+
|
9
|
+
This gem needs to be tested in the wild. Please kick its tyres and give it a good thrashing. It is inevitable that bugs will be discovered, so please open issues for them here or fork this project, fix them, and send me a pull request.
|
10
|
+
|
11
|
+
The 'ffi' gem has dropped support for MRI 1.8.x. Since this project relies on that gem to load and run this code, then this project does not support MRI 1.8.x. I recommend JRuby for the best performance and stability.
|
12
|
+
|
13
|
+
h2. Requirements
|
14
|
+
|
15
|
+
* Crossroads I/O version 1.0.0 or later.
|
16
|
+
* ffi (>= 1.0.0)
|
17
|
+
|
18
|
+
The Crossroads I/O library must be installed on your system in a well-known location like /usr/local/lib. This is the default for new Crossroads I/O installs. Future releases may include the library as a C extension built at time of installation.
|
19
|
+
|
20
|
+
Do *not* run this gem under MRI with an old 'ffi' gem. It will crash randomly and you will be sad.
|
21
|
+
|
22
|
+
h2. Installation
|
23
|
+
|
24
|
+
A full gem has been released to Rubygems.org as of release 1.0.0. Make sure the Crossroads I/O library is already installed on your system.
|
25
|
+
|
26
|
+
* gem install ffi-rxs # should grab the latest release
|
27
|
+
|
28
|
+
|
29
|
+
To build from git master:
|
30
|
+
|
31
|
+
* git clone git://github.com/celldee/ffi-rxs
|
32
|
+
* cd ffi-rxs
|
33
|
+
* gem build ffi-rxs.gemspec
|
34
|
+
* gem install ffi-rxs-*.gem
|
35
|
+
|
36
|
+
|
37
|
+
*NOTE* for Windows users!
|
38
|
+
In order for this gem to find the libxs.dll, it *must* be on the Windows PATH. Google for "modify windows path" for instructions on how to do that if you are unfamiliar with that activity.
|
39
|
+
|
40
|
+
h2. Examples
|
41
|
+
|
42
|
+
h3. Using Request (REQ) socket -
|
43
|
+
|
44
|
+
<pre>
|
45
|
+
require 'ffi-rxs'
|
46
|
+
|
47
|
+
ctx = XS::Context.create()
|
48
|
+
socket = ctx.socket(XS::REQ)
|
49
|
+
socket.connect("tcp://127.0.0.1:5000")
|
50
|
+
|
51
|
+
for i in 1..10
|
52
|
+
msg = "msg #{i.to_s}"
|
53
|
+
socket.send_string(msg)
|
54
|
+
puts "Sending: " + msg
|
55
|
+
socket.recv_string(msg_in = '')
|
56
|
+
puts "Received: " + msg_in
|
57
|
+
end
|
58
|
+
</pre>
|
59
|
+
|
60
|
+
h3. Using Reply (REP) socket -
|
61
|
+
|
62
|
+
<pre>
|
63
|
+
require 'ffi-rxs'
|
64
|
+
|
65
|
+
ctx = XS::Context.create()
|
66
|
+
socket = ctx.socket(XS::REP)
|
67
|
+
socket.bind("tcp://127.0.0.1:5000")
|
68
|
+
|
69
|
+
while true do
|
70
|
+
socket.recv_string(msg = '')
|
71
|
+
puts "Got: " + msg
|
72
|
+
socket.send_string(msg)
|
73
|
+
end
|
74
|
+
</pre>
|
75
|
+
|
76
|
+
The above examples and additional ones are available in the repository examples folder.
|
77
|
+
|
78
|
+
h2. Links
|
79
|
+
|
80
|
+
* "Source code":http://github.com/celldee/ffi-rxs
|
81
|
+
* "Crossroads I/O website":http://crossroads.io
|
82
|
+
* "Crossroads I/O Discussion Group":http://groups.crossroads.io/
|
83
|
+
|
84
|
+
h2. License
|
85
|
+
|
86
|
+
This project is licensed under the MIT license. Please see the LICENSE file in the repository.
|
data/examples/LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2010-2012 iMatix Corporation, Chris Duncan and Contributors
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/examples/README
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Durable publisher to be used in conjunction with durable_sub.rb
|
4
|
+
# Justin Case <justin@playelite.com>
|
5
|
+
|
6
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
7
|
+
|
8
|
+
context = XS::Context.create()
|
9
|
+
|
10
|
+
# Subscriber tells us when it's ready here
|
11
|
+
sync = context.socket(XS::PULL)
|
12
|
+
sync.bind("tcp://127.0.0.1:5564")
|
13
|
+
|
14
|
+
# We send updates via this socket
|
15
|
+
publisher = context.socket(XS::PUB)
|
16
|
+
publisher.bind("tcp://127.0.0.1:5565")
|
17
|
+
|
18
|
+
# Wait for synchronization request
|
19
|
+
sync.recv_string(sync_request = '')
|
20
|
+
|
21
|
+
# Now broadcast exactly 10 updates with pause
|
22
|
+
10.times do |update_number|
|
23
|
+
message = sprintf("Update %d", update_number)
|
24
|
+
puts "Sending: " + message
|
25
|
+
publisher.send_string(message)
|
26
|
+
sleep(1)
|
27
|
+
end
|
28
|
+
|
29
|
+
publisher.send_string("END")
|
30
|
+
|
31
|
+
sync.close
|
32
|
+
publisher.close
|
33
|
+
context.terminate
|
34
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Durable subscriber to be used in conjunction with durable_pub.rb
|
4
|
+
# Justin Case <justin@playelite.com>
|
5
|
+
|
6
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
7
|
+
|
8
|
+
context = XS::Context.create()
|
9
|
+
|
10
|
+
# Connect our subscriber socket
|
11
|
+
subscriber = context.socket(XS::SUB)
|
12
|
+
subscriber.setsockopt(XS::IDENTITY, "Hello")
|
13
|
+
subscriber.setsockopt(XS::SUBSCRIBE, "")
|
14
|
+
subscriber.connect("tcp://127.0.0.1:5565")
|
15
|
+
|
16
|
+
# Synchronize with publisher
|
17
|
+
sync = context.socket(XS::PUSH)
|
18
|
+
sync.connect("tcp://127.0.0.1:5564")
|
19
|
+
sync.send_string("")
|
20
|
+
|
21
|
+
# Get updates, exit when told to do so
|
22
|
+
loop do
|
23
|
+
subscriber.recv_string(message = '')
|
24
|
+
puts "Received: " + message
|
25
|
+
if message == "END"
|
26
|
+
break
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# latency_measurement.rb
|
4
|
+
|
5
|
+
# Within a single process, we start up two threads. One thread has a REQ (request)
|
6
|
+
# socket and the second thread has a REP (reply) socket. We measure the
|
7
|
+
# *round-trip* latency between these sockets. Only *one* message is in flight at
|
8
|
+
# any given moment.
|
9
|
+
#
|
10
|
+
# This example also illustrates how a single context can be shared amongst several
|
11
|
+
# threads. Sharing a single context also allows a user to specify the "inproc"
|
12
|
+
# transport in addition to "tcp" and "ipc".
|
13
|
+
#
|
14
|
+
# % ruby latency_measurement.rb tcp://127.0.0.1:5555 1024 1_000_000
|
15
|
+
#
|
16
|
+
# % ruby latency_measurement.rb inproc://lm_sock 1024 1_000_000
|
17
|
+
|
18
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
19
|
+
|
20
|
+
if ARGV.length < 3
|
21
|
+
puts "usage: ruby latency_measurement.rb <connect-to> <message-size> <roundtrip-count>"
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
|
25
|
+
link = ARGV[0]
|
26
|
+
message_size = ARGV[1].to_i
|
27
|
+
roundtrip_count = ARGV[2].to_i
|
28
|
+
|
29
|
+
def assert(rc)
|
30
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
master_context = XS::Context.new
|
35
|
+
rescue ContextError => e
|
36
|
+
STDERR.puts "Failed to allocate context or socket!"
|
37
|
+
raise
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
class Receiver
|
42
|
+
def initialize context, link, size, count
|
43
|
+
@context = context
|
44
|
+
@link = link
|
45
|
+
@size = size
|
46
|
+
@count = count
|
47
|
+
|
48
|
+
begin
|
49
|
+
@socket = @context.socket(XS::REP)
|
50
|
+
rescue ContextError => e
|
51
|
+
STDERR.puts "Failed to allocate REP socket!"
|
52
|
+
raise
|
53
|
+
end
|
54
|
+
|
55
|
+
assert(@socket.setsockopt(XS::LINGER, 100))
|
56
|
+
assert(@socket.setsockopt(XS::RCVHWM, 100))
|
57
|
+
assert(@socket.setsockopt(XS::SNDHWM, 100))
|
58
|
+
|
59
|
+
assert(@socket.bind(@link))
|
60
|
+
end
|
61
|
+
|
62
|
+
def run
|
63
|
+
@count.times do
|
64
|
+
string = ''
|
65
|
+
assert(@socket.recv_string(string, 0))
|
66
|
+
|
67
|
+
raise "Message size doesn't match, expected [#{@size}] but received [#{string.size}]" if @size != string.size
|
68
|
+
|
69
|
+
assert(@socket.send_string(string, 0))
|
70
|
+
end
|
71
|
+
|
72
|
+
assert(@socket.close)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Transmitter
|
77
|
+
def initialize context, link, size, count
|
78
|
+
@context = context
|
79
|
+
@link = link
|
80
|
+
@size = size
|
81
|
+
@count = count
|
82
|
+
|
83
|
+
begin
|
84
|
+
@socket = @context.socket(XS::REQ)
|
85
|
+
rescue ContextError => e
|
86
|
+
STDERR.puts "Failed to allocate REP socket!"
|
87
|
+
raise
|
88
|
+
end
|
89
|
+
|
90
|
+
assert(@socket.setsockopt(XS::LINGER, 100))
|
91
|
+
assert(@socket.setsockopt(XS::RCVHWM, 100))
|
92
|
+
assert(@socket.setsockopt(XS::SNDHWM, 100))
|
93
|
+
|
94
|
+
assert(@socket.connect(@link))
|
95
|
+
end
|
96
|
+
|
97
|
+
def run
|
98
|
+
msg = "#{ '3' * @size }"
|
99
|
+
|
100
|
+
elapsed = elapsed_microseconds do
|
101
|
+
@count.times do
|
102
|
+
assert(@socket.send_string(msg, 0))
|
103
|
+
assert(@socket.recv_string(msg, 0))
|
104
|
+
|
105
|
+
raise "Message size doesn't match, expected [#{@size}] but received [#{msg.size}]" if @size != msg.size
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
latency = elapsed / @count / 2
|
110
|
+
|
111
|
+
puts "message size: %i [B]" % @size
|
112
|
+
puts "roundtrip count: %i" % @count
|
113
|
+
puts "throughput (msgs/s): %i" % (@count / (elapsed / 1_000_000))
|
114
|
+
puts "mean latency: %.3f [us]" % latency
|
115
|
+
assert(@socket.close)
|
116
|
+
end
|
117
|
+
|
118
|
+
def elapsed_microseconds(&blk)
|
119
|
+
start = Time.now
|
120
|
+
yield
|
121
|
+
value = ((Time.now - start) * 1_000_000)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
threads = []
|
126
|
+
threads << Thread.new do
|
127
|
+
receiver = Receiver.new(master_context, link, message_size, roundtrip_count)
|
128
|
+
receiver.run
|
129
|
+
end
|
130
|
+
|
131
|
+
sleep 1
|
132
|
+
|
133
|
+
threads << Thread.new do
|
134
|
+
transmitter = Transmitter.new(master_context, link, message_size, roundtrip_count)
|
135
|
+
transmitter.run
|
136
|
+
end
|
137
|
+
|
138
|
+
threads.each {|t| t.join}
|
139
|
+
|
140
|
+
master_context.terminate
|
data/examples/reply.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# reply.rb
|
4
|
+
|
5
|
+
# This example is used in conjunction with request.rb. Run this program in
|
6
|
+
# a terminal/console window and then run request.rb in another terminal/console
|
7
|
+
# window and observe the output.
|
8
|
+
#
|
9
|
+
# Usage: ruby reply.rb
|
10
|
+
#
|
11
|
+
# To stop the program terminate the process with Ctrl-C or another
|
12
|
+
# method of your choice.
|
13
|
+
#
|
14
|
+
|
15
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
16
|
+
|
17
|
+
ctx = XS::Context.create()
|
18
|
+
socket = ctx.socket(XS::REP)
|
19
|
+
socket.bind("tcp://127.0.0.1:5000")
|
20
|
+
|
21
|
+
while true do
|
22
|
+
socket.recv_string(msg = '')
|
23
|
+
puts "Got: " + msg
|
24
|
+
socket.send_string(msg)
|
25
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# req_rep_poll.rb
|
4
|
+
#
|
5
|
+
# It illustrates the use of xs_poll(), as wrapped by the Ruby library,
|
6
|
+
# for detecting and responding to read and write events recorded on sockets.
|
7
|
+
# It also shows how to use XS::NO_BLOCK/XS::DONTWAIT for non-blocking send
|
8
|
+
# and receive.
|
9
|
+
|
10
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
11
|
+
|
12
|
+
|
13
|
+
def assert(rc)
|
14
|
+
raise "Last API call failed at #{caller(1)}" unless rc >= 0
|
15
|
+
end
|
16
|
+
|
17
|
+
link = "tcp://127.0.0.1:5554"
|
18
|
+
|
19
|
+
begin
|
20
|
+
ctx = XS::Context.create()
|
21
|
+
s1 = ctx.socket(XS::REQ)
|
22
|
+
s2 = ctx.socket(XS::REP)
|
23
|
+
rescue ContextError => e
|
24
|
+
STDERR.puts "Failed to allocate context or socket!"
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
|
28
|
+
assert(s1.setsockopt(XS::LINGER, 100))
|
29
|
+
assert(s2.setsockopt(XS::LINGER, 100))
|
30
|
+
|
31
|
+
assert(s1.connect(link))
|
32
|
+
assert(s2.bind(link))
|
33
|
+
|
34
|
+
poller = XS::Poller.new
|
35
|
+
poller.register_readable(s2)
|
36
|
+
poller.register_writable(s1)
|
37
|
+
|
38
|
+
start_time = Time.now
|
39
|
+
@unsent = true
|
40
|
+
|
41
|
+
until @done do
|
42
|
+
assert(poller.poll_nonblock)
|
43
|
+
|
44
|
+
# send the message after 5 seconds
|
45
|
+
if Time.now - start_time > 5 && @unsent
|
46
|
+
payload = "#{ '3' * 1024 }"
|
47
|
+
|
48
|
+
puts "sending payload nonblocking"
|
49
|
+
assert(s1.send_string(payload, XS::NonBlocking))
|
50
|
+
@unsent = false
|
51
|
+
end
|
52
|
+
|
53
|
+
# check for messages after 1 second
|
54
|
+
if Time.now - start_time > 1
|
55
|
+
poller.readables.each do |sock|
|
56
|
+
received_msg = ''
|
57
|
+
assert(sock.recv_string(received_msg, XS::NonBlocking))
|
58
|
+
|
59
|
+
puts "message received [#{received_msg}]"
|
60
|
+
@done = true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
puts "executed in [#{Time.now - start_time}] seconds"
|
66
|
+
|
67
|
+
assert(s1.close)
|
68
|
+
assert(s2.close)
|
69
|
+
|
70
|
+
ctx.terminate
|
71
|
+
|
data/examples/request.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# request.rb
|
4
|
+
|
5
|
+
# This example is used in conjunction with reply.rb. Run this program in
|
6
|
+
# a terminal/console window and then run reply.rb in another terminal/console
|
7
|
+
# window and observe the output.
|
8
|
+
#
|
9
|
+
# Usage: ruby request.rb
|
10
|
+
#
|
11
|
+
|
12
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
13
|
+
|
14
|
+
ctx = XS::Context.create()
|
15
|
+
socket = ctx.socket(XS::REQ)
|
16
|
+
socket.connect("tcp://127.0.0.1:5000")
|
17
|
+
|
18
|
+
for i in 1..10
|
19
|
+
msg = "msg #{i.to_s}"
|
20
|
+
socket.send_string(msg)
|
21
|
+
puts "Sending: " + msg
|
22
|
+
socket.recv_string(msg_in = '')
|
23
|
+
puts "Received: " + msg_in
|
24
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Task sink to be used in conjunction with task_vent.rb
|
4
|
+
# and task_worker.rb
|
5
|
+
# Binds PULL socket to tcp://localhost:5558
|
6
|
+
# Collects results from task_workers via that socket
|
7
|
+
|
8
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
9
|
+
|
10
|
+
# Prepare our context and socket
|
11
|
+
context = XS::Context.create()
|
12
|
+
receiver = context.socket(XS::PULL)
|
13
|
+
receiver.bind("tcp://*:5558")
|
14
|
+
|
15
|
+
# Wait for start of batch
|
16
|
+
receiver.recv_string('')
|
17
|
+
puts 'Sink started'
|
18
|
+
tstart = Time.now
|
19
|
+
|
20
|
+
# Process 100 confirmations
|
21
|
+
100.times do |task_nbr|
|
22
|
+
receiver.recv_string('')
|
23
|
+
$stdout << ((task_nbr % 10 == 0) ? ':' : '.')
|
24
|
+
$stdout.flush
|
25
|
+
end
|
26
|
+
|
27
|
+
# Calculate and report duration of batch
|
28
|
+
tend = Time.now
|
29
|
+
total_msec = (tend-tstart) * 1000
|
30
|
+
puts "Total elapsed time: #{total_msec} msec"
|
31
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Task vent to be used in conjunction with task_worker.rb
|
4
|
+
# and task_sink.rb
|
5
|
+
# Binds PUSH socket to tcp://localhost:5557
|
6
|
+
# Sends batch of tasks to task_workers via that socket
|
7
|
+
|
8
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
9
|
+
|
10
|
+
context = XS::Context.create()
|
11
|
+
|
12
|
+
# Socket to send messages on
|
13
|
+
sender = context.socket(XS::PUSH)
|
14
|
+
sender.bind("tcp://*:5557")
|
15
|
+
|
16
|
+
puts "Press enter when the workers are ready..."
|
17
|
+
$stdin.read(1)
|
18
|
+
puts "Sending tasks to workers..."
|
19
|
+
|
20
|
+
# The first message is "0" and signals start of batch
|
21
|
+
sender.send_string('0')
|
22
|
+
|
23
|
+
# Send 100 tasks
|
24
|
+
total_msec = 0 # Total expected cost in msecs
|
25
|
+
100.times do
|
26
|
+
workload = rand(100) + 1
|
27
|
+
total_msec += workload
|
28
|
+
$stdout << "#{workload}."
|
29
|
+
sender.send_string(workload.to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
puts "Total expected cost: #{total_msec} msec"
|
33
|
+
Kernel.sleep(1) # Give Crossroads time to deliver
|
34
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Task worker to be used in conjunction with task_vent.rb
|
4
|
+
# and task_sink.rb
|
5
|
+
# Connects PULL socket to tcp://localhost:5557
|
6
|
+
# Collects workloads from task_vent via that socket
|
7
|
+
# Connects PUSH socket to tcp://localhost:5558
|
8
|
+
# Sends results to sink via that socket
|
9
|
+
|
10
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'ffi-rxs')
|
11
|
+
|
12
|
+
context = XS::Context.create()
|
13
|
+
|
14
|
+
# Socket to receive messages on
|
15
|
+
receiver = context.socket(XS::PULL)
|
16
|
+
receiver.connect("tcp://localhost:5557")
|
17
|
+
|
18
|
+
# Socket to send messages to
|
19
|
+
sender = context.socket(XS::PUSH)
|
20
|
+
sender.connect("tcp://localhost:5558")
|
21
|
+
|
22
|
+
# Process tasks forever
|
23
|
+
while true
|
24
|
+
|
25
|
+
receiver.recv_string(msec = '')
|
26
|
+
# Simple progress indicator for the viewer
|
27
|
+
$stdout << "#{msec}."
|
28
|
+
$stdout.flush
|
29
|
+
|
30
|
+
# Do the work
|
31
|
+
sleep(msec.to_f / 1000)
|
32
|
+
|
33
|
+
# Send results to sink
|
34
|
+
sender.send_string("")
|
35
|
+
end
|
36
|
+
|