rbczmq 0.1
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/.gitignore +23 -0
- data/.travis.yml +19 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +19 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +247 -0
- data/Rakefile +67 -0
- data/examples/loop.rb +109 -0
- data/examples/poller.rb +37 -0
- data/examples/pub_sub.rb +101 -0
- data/examples/push_pull.rb +104 -0
- data/examples/req_rep.rb +100 -0
- data/ext/czmq.tar.gz +0 -0
- data/ext/rbczmq/context.c +280 -0
- data/ext/rbczmq/context.h +26 -0
- data/ext/rbczmq/extconf.rb +138 -0
- data/ext/rbczmq/frame.c +401 -0
- data/ext/rbczmq/frame.h +24 -0
- data/ext/rbczmq/jruby.h +22 -0
- data/ext/rbczmq/loop.c +413 -0
- data/ext/rbczmq/loop.h +24 -0
- data/ext/rbczmq/message.c +620 -0
- data/ext/rbczmq/message.h +24 -0
- data/ext/rbczmq/poller.c +308 -0
- data/ext/rbczmq/poller.h +29 -0
- data/ext/rbczmq/pollitem.c +251 -0
- data/ext/rbczmq/pollitem.h +25 -0
- data/ext/rbczmq/rbczmq_ext.c +198 -0
- data/ext/rbczmq/rbczmq_ext.h +94 -0
- data/ext/rbczmq/rbczmq_prelude.h +22 -0
- data/ext/rbczmq/rubinius.h +24 -0
- data/ext/rbczmq/ruby18.h +43 -0
- data/ext/rbczmq/ruby19.h +15 -0
- data/ext/rbczmq/socket.c +1570 -0
- data/ext/rbczmq/socket.h +136 -0
- data/ext/rbczmq/timer.c +110 -0
- data/ext/rbczmq/timer.h +23 -0
- data/ext/zeromq.tar.gz +0 -0
- data/lib/rbczmq.rb +3 -0
- data/lib/zmq.rb +77 -0
- data/lib/zmq/context.rb +50 -0
- data/lib/zmq/default_handler.rb +16 -0
- data/lib/zmq/frame.rb +11 -0
- data/lib/zmq/handler.rb +76 -0
- data/lib/zmq/loop.rb +131 -0
- data/lib/zmq/message.rb +9 -0
- data/lib/zmq/poller.rb +22 -0
- data/lib/zmq/pollitem.rb +31 -0
- data/lib/zmq/socket.rb +125 -0
- data/lib/zmq/socket/dealer.rb +33 -0
- data/lib/zmq/socket/pair.rb +39 -0
- data/lib/zmq/socket/pub.rb +30 -0
- data/lib/zmq/socket/pull.rb +29 -0
- data/lib/zmq/socket/push.rb +32 -0
- data/lib/zmq/socket/rep.rb +37 -0
- data/lib/zmq/socket/req.rb +37 -0
- data/lib/zmq/socket/router.rb +38 -0
- data/lib/zmq/socket/sub.rb +27 -0
- data/lib/zmq/timer.rb +12 -0
- data/lib/zmq/version.rb +5 -0
- data/perf/pair.rb +7 -0
- data/perf/pair/local.rb +22 -0
- data/perf/pair/remote.rb +25 -0
- data/perf/pub_sub.rb +7 -0
- data/perf/pub_sub/local.rb +22 -0
- data/perf/pub_sub/remote.rb +25 -0
- data/perf/push_pull.rb +7 -0
- data/perf/push_pull/local.rb +21 -0
- data/perf/push_pull/remote.rb +25 -0
- data/perf/req_rep.rb +7 -0
- data/perf/req_rep/local.rb +35 -0
- data/perf/req_rep/remote.rb +28 -0
- data/perf/runner.rb +142 -0
- data/rbczmq.gemspec +22 -0
- data/test/helper.rb +21 -0
- data/test/socket/test_dealer_socket.rb +14 -0
- data/test/socket/test_pair_socket.rb +24 -0
- data/test/socket/test_pair_sockets.rb +74 -0
- data/test/socket/test_pub_socket.rb +17 -0
- data/test/socket/test_pub_sub_sockets.rb +87 -0
- data/test/socket/test_pull_socket.rb +17 -0
- data/test/socket/test_push_pull_sockets.rb +81 -0
- data/test/socket/test_push_socket.rb +17 -0
- data/test/socket/test_rep_socket.rb +25 -0
- data/test/socket/test_req_rep_sockets.rb +42 -0
- data/test/socket/test_req_socket.rb +27 -0
- data/test/socket/test_router_socket.rb +14 -0
- data/test/socket/test_routing.rb +66 -0
- data/test/socket/test_sub_socket.rb +17 -0
- data/test/test_context.rb +86 -0
- data/test/test_frame.rb +78 -0
- data/test/test_handler.rb +28 -0
- data/test/test_loop.rb +252 -0
- data/test/test_message.rb +201 -0
- data/test/test_poller.rb +154 -0
- data/test/test_pollitem.rb +78 -0
- data/test/test_socket.rb +403 -0
- data/test/test_threading.rb +34 -0
- data/test/test_timer.rb +37 -0
- data/test/test_zmq.rb +62 -0
- metadata +208 -0
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
.libs/*
|
2
|
+
.rbx/*
|
3
|
+
*.rbc
|
4
|
+
*.lo
|
5
|
+
*.la
|
6
|
+
*.lai
|
7
|
+
*.dylib.dSYM
|
8
|
+
*.dylib
|
9
|
+
*.o
|
10
|
+
*.a
|
11
|
+
*.log
|
12
|
+
tmp/*
|
13
|
+
true/*
|
14
|
+
*.bundle
|
15
|
+
*.gem
|
16
|
+
doc/*
|
17
|
+
.DS_Store
|
18
|
+
ext/zeromq
|
19
|
+
ext/czmq
|
20
|
+
pkg
|
21
|
+
scratch
|
22
|
+
ext/rbczmq/dst
|
23
|
+
ext/rbczmq/Makefile
|
data/.travis.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- rbx-18mode
|
4
|
+
- rbx-19mode
|
5
|
+
- ree
|
6
|
+
- 1.8.7
|
7
|
+
- 1.9.2
|
8
|
+
- 1.9.3
|
9
|
+
- ruby-head
|
10
|
+
script: "bundle exec rake"
|
11
|
+
before_install: "sudo apt-get install uuid-dev"
|
12
|
+
gemfile:
|
13
|
+
- Gemfile
|
14
|
+
notifications:
|
15
|
+
recipients:
|
16
|
+
- lourens@methodmissing.com
|
17
|
+
branches:
|
18
|
+
only:
|
19
|
+
- master
|
data/Gemfile.lock
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Lourens Naudé - http://github.com/methodmissing
|
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,247 @@
|
|
1
|
+
= rbczmq - binding for the high level ZeroMQ C API {<img src="https://secure.travis-ci.org/methodmissing/rbczmq.png" alt="Build Status" />}[http://travis-ci.org/methodmissing/rbczmq]
|
2
|
+
|
3
|
+
(c) 2011 Lourens Naudé (methodmissing), James Tucker (raggi) with API guidance from the czmq (http://czmq.zeromq.org/) project.
|
4
|
+
|
5
|
+
http://github.com/methodmissing/rbczmq
|
6
|
+
|
7
|
+
==== NOT YET PRODUCTION READY, BUT ALMOST
|
8
|
+
|
9
|
+
== About ZeroMQ
|
10
|
+
|
11
|
+
In a nutshell, ZeroMQ is a hybrid networking library / concurrency framework. I quote the ØMQ Guide (http://zguide.zeromq.org/page:all) :
|
12
|
+
|
13
|
+
"ØMQ (ZeroMQ, 0MQ, zmq) looks like an embeddable networking library but acts like a concurrency framework. It gives you sockets that carry whole messages across various transports like in-process, inter-process, TCP, and multicast. You can connect sockets N-to-N with patterns like fanout, pub-sub, task distribution, and request-reply. It's fast enough to be the fabric for clustered products. Its asynchronous I/O model gives you scalable multicore applications, built as asynchronous message-processing tasks. It has a score of language APIs and runs on most operating systems. ØMQ is from iMatix and is LGPL open source."
|
14
|
+
|
15
|
+
== Another ZeroMQ extension ?
|
16
|
+
|
17
|
+
This extension bundles both ZeroMQ (libzmq, http://www.zeromq.org/) and CZMQ (libczmq, http://czmq.zeromq.org/) and as such have no third party dependencies other than a Ruby distribution and a C compiler. My goals for this project were :
|
18
|
+
|
19
|
+
* Access to a powerful messaging technology without having to install a bunch of dependencies
|
20
|
+
* A stable and mostly version agnostic (2.x and 3.x series) API
|
21
|
+
* Leverage and build upon a mature and maintained client (CZMQ)
|
22
|
+
* Target Ruby distributions with a stable and comprehensive C API (MRI, Rubinius, JRuby is work in progress)
|
23
|
+
* Support for running sockets in Threads - both green and native threads should be supported and preempt properly
|
24
|
+
with edge-triggered multiplexing from libzmq.
|
25
|
+
* Integrate with the Garbage Collector in a predictable way. CZMQ and the ZeroMQ framework is very fast and can allocate
|
26
|
+
an enormous amount of objects in no time when using the Frame, Message and String wrappers. Resources such as socket
|
27
|
+
connections should be cleaned up when objects are finalized as well.
|
28
|
+
* Expose Message envelopes and Frames to developers as well to allow for higher level protocols and constructs.
|
29
|
+
* Enforce well known best practices such as restricting socket interactions to within the thread the socket was created in etc.
|
30
|
+
|
31
|
+
== Performance
|
32
|
+
|
33
|
+
ZeroMQ can have higher throughput than TCP in most cases by using a message batching technique. Please have a look through the Performance section in the ZeroMQ FAQ (http://www.zeromq.org/area:faq#toc2) for further implementation details.
|
34
|
+
|
35
|
+
Some notes about these benchmarks :
|
36
|
+
|
37
|
+
* Messages go through the full network stack on localhost (TCP/IP transport)
|
38
|
+
* The sender and receiver endpoints are Ruby processes which coerce transferred data to native String objects
|
39
|
+
on both ends.
|
40
|
+
* There's thus a definite method dispatch cost in addition to intermittent pauses from the Garbage Collector
|
41
|
+
* It's still plenty fast for most soft real-time applications and we're able to push in excess of 1 gigabits/s with the 1024 byte payloads. A language with automatic memory management cannot easily comply to hard real-time guarantees anyways.
|
42
|
+
|
43
|
+
=== TCP/IP loopback, 100k messages, 100 byte payloads
|
44
|
+
|
45
|
+
Lourenss-MacBook-Air:rbczmq lourens$ MSG_COUNT=100000 MSG_SIZE=100 ruby perf/pair.rb
|
46
|
+
Local pids: 2042
|
47
|
+
Remote pid: 2043
|
48
|
+
Sent 100000 messages in 0.3933s ...
|
49
|
+
[2043] Memory used before: 1128kb
|
50
|
+
[2043] Memory used after: 3012kb
|
51
|
+
[2042] Memory used before: 1120kb
|
52
|
+
====== [2042] transfer stats ======
|
53
|
+
message encoding: string
|
54
|
+
message size: 100 [B]
|
55
|
+
message count: 100000
|
56
|
+
mean throughput: 227978 [msg/s]
|
57
|
+
mean throughput: 182.383 [Mb/s]
|
58
|
+
[2042] Memory used after: 22432kb
|
59
|
+
|
60
|
+
=== TCP/IP loopback, 100k messages, 1024 byte payloads
|
61
|
+
|
62
|
+
Lourenss-MacBook-Air:rbczmq lourens$ MSG_COUNT=100000 MSG_SIZE=1024 ruby perf/pair.rb
|
63
|
+
Local pids: 2027
|
64
|
+
Remote pid: 2028
|
65
|
+
Sent 100000 messages in 0.641198s ...
|
66
|
+
[2028] Memory used before: 1120kb
|
67
|
+
[2028] Memory used after: 12776kb
|
68
|
+
[2027] Memory used before: 1144kb
|
69
|
+
====== [2027] transfer stats ======
|
70
|
+
message encoding: string
|
71
|
+
message size: 1024 [B]
|
72
|
+
message count: 100000
|
73
|
+
mean throughput: 160756 [msg/s]
|
74
|
+
mean throughput: 1316.919 [Mb/s]
|
75
|
+
[2027] Memory used after: 189004kb
|
76
|
+
|
77
|
+
=== TCP/IP loopback, 100k messages, 2048 byte payloads
|
78
|
+
|
79
|
+
Lourenss-MacBook-Air:rbczmq lourens$ MSG_COUNT=100000 MSG_SIZE=2048 ruby perf/pair.rb
|
80
|
+
Local pids: 2034
|
81
|
+
Remote pid: 2035
|
82
|
+
Sent 100000 messages in 0.94703s ...
|
83
|
+
[2035] Memory used before: 1140kb
|
84
|
+
[2035] Memory used after: 7212kb
|
85
|
+
[2034] Memory used before: 1128kb
|
86
|
+
====== [2034] transfer stats ======
|
87
|
+
message encoding: string
|
88
|
+
message size: 2048 [B]
|
89
|
+
message count: 100000
|
90
|
+
mean throughput: 123506 [msg/s]
|
91
|
+
mean throughput: 2023.528 [Mb/s]
|
92
|
+
[2034] Memory used after: 277712kb
|
93
|
+
|
94
|
+
Have a play around with the performance runner and other socket pairs as well - https://github.com/methodmissing/rbczmq/tree/master/perf
|
95
|
+
|
96
|
+
== Usage
|
97
|
+
|
98
|
+
As a first step I'd highly recommend you read (and reread) through the zguide (http://zguide.zeromq.org/page:all) as understanding the supported messaging patterns and topologies is fundamental to getting the most from this binding.
|
99
|
+
Here's a few basic examples. Please refer to documentation (http://methodmissing.github.com/rbczmq/) and test cases (https://github.com/methodmissing/rbczmq/tree/master/test) for detailed usage information.
|
100
|
+
|
101
|
+
=== Basic send / receive, in process transport
|
102
|
+
|
103
|
+
ctx = ZMQ::Context.new
|
104
|
+
rep = ctx.socket(:PAIR)
|
105
|
+
port = rep.bind("inproc://send.receive")
|
106
|
+
req = ctx.socket(:PAIR)
|
107
|
+
req.connect("inproc://send.receive")
|
108
|
+
req.send("ping") # true
|
109
|
+
rep.recv # "ping"
|
110
|
+
|
111
|
+
ctx.destroy
|
112
|
+
|
113
|
+
=== Fair-queued work distribution to a set of worker threads
|
114
|
+
|
115
|
+
ctx = ZMQ::Context.new
|
116
|
+
push = ctx.bind(:PUSH, "inproc://push-pull-distribution.test")
|
117
|
+
threads = []
|
118
|
+
5.times do
|
119
|
+
threads << Thread.new do
|
120
|
+
pull = ctx.connect(:PULL, "inproc://push-pull-distribution.test")
|
121
|
+
msg = pull.recv
|
122
|
+
pull.close
|
123
|
+
msg
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
sleep 0.5 # avoid "slow joiner" syndrome
|
128
|
+
messages = %w(a b c d e f)
|
129
|
+
messages.each do |m|
|
130
|
+
push.send m
|
131
|
+
end
|
132
|
+
|
133
|
+
threads.each{|t| t.join }
|
134
|
+
threads.all?{|t| messages.include?(t.value) } # true
|
135
|
+
|
136
|
+
ctx.destroy
|
137
|
+
|
138
|
+
=== Async request / reply routing
|
139
|
+
|
140
|
+
ctx = ZMQ::Context.new
|
141
|
+
router = ctx.bind(:ROUTER, "inproc://routing-flow.test")
|
142
|
+
dealer = ctx.socket(:DEALER)
|
143
|
+
dealer.identity = "xyz"
|
144
|
+
dealer.connect("inproc://routing-flow.test")
|
145
|
+
|
146
|
+
router.sendm("xyz")
|
147
|
+
router.send("request")
|
148
|
+
dealer.recv # "request"
|
149
|
+
|
150
|
+
dealer.send("reply")
|
151
|
+
router.recv # "xyz"
|
152
|
+
router.recv # "reply"
|
153
|
+
|
154
|
+
ctx.destroy
|
155
|
+
|
156
|
+
=== Send / receive frames
|
157
|
+
|
158
|
+
ctx = ZMQ::Context.new
|
159
|
+
rep = ctx.socket(:PAIR)
|
160
|
+
rep.bind("inproc://frames.test")
|
161
|
+
req = ctx.socket(:PAIR)
|
162
|
+
req.connect("inproc://frames.test")
|
163
|
+
ping = ZMQ::Frame("ping")
|
164
|
+
req.send_frame(ping) # true
|
165
|
+
rep.recv_frame # ZMQ::Frame("ping")
|
166
|
+
rep.send_frame(ZMQ::Frame("pong")) # true
|
167
|
+
req.recv_frame # ZMQ::Frame("pong")
|
168
|
+
rep.send_frame(ZMQ::Frame("pong")) # true
|
169
|
+
req.recv_frame_nonblock # nil
|
170
|
+
sleep 0.3
|
171
|
+
req.recv_frame_nonblock # ZMQ::Frame("pong")
|
172
|
+
|
173
|
+
ctx.destroy
|
174
|
+
|
175
|
+
=== Send / receive messages
|
176
|
+
|
177
|
+
ctx = ZMQ::Context.new
|
178
|
+
rep = ctx.socket(:PAIR)
|
179
|
+
rep.bind("inproc://messages.test")
|
180
|
+
req = ctx.socket(:PAIR)
|
181
|
+
req.connect("inproc://messages.test")
|
182
|
+
|
183
|
+
msg = ZMQ::Message.new
|
184
|
+
msg.push ZMQ::Frame("header")
|
185
|
+
msg.push ZMQ::Frame("body")
|
186
|
+
|
187
|
+
req.send_message(msg) # nil
|
188
|
+
|
189
|
+
recvd_msg = rep.recv_message
|
190
|
+
recvd_msg.class # ZMQ::Message
|
191
|
+
recvd_msg.pop # ZMQ::Frame("header")
|
192
|
+
recvd_msg.pop # ZMQ::Frame("body")
|
193
|
+
|
194
|
+
ctx.destroy
|
195
|
+
|
196
|
+
== Resources
|
197
|
+
|
198
|
+
* ZeroMQ - http://www.zeromq.org/community
|
199
|
+
* The ØMQ Reference Manual - http://api.zeromq.org/
|
200
|
+
* The ØMQ FAQ - http://www.zeromq.org/area:faq
|
201
|
+
* Whitepapers - http://www.zeromq.org/area:whitepapers
|
202
|
+
* The ØMQ Guide - http://zguide.zeromq.org/page:all
|
203
|
+
* CZMQ - http://czmq.zeromq.org/
|
204
|
+
* Recent presentation on data transfer by the author of this binding - http://www.slideshare.net/methodmissing/sapo-codebits-2011
|
205
|
+
|
206
|
+
== Requirements
|
207
|
+
|
208
|
+
* A POSIX compliant OS, known to work well on Linux, BSD variants and Mac OS X
|
209
|
+
* Ruby MRI 1.8, 1.9 or Rubinius (JRuby capi support forthcoming)
|
210
|
+
* A C compiler
|
211
|
+
|
212
|
+
== Installation
|
213
|
+
|
214
|
+
Rubygems installation
|
215
|
+
|
216
|
+
gem install rbczmq
|
217
|
+
|
218
|
+
Building from source
|
219
|
+
|
220
|
+
git clone git@github.com:methodmissing/rbczmq.git
|
221
|
+
rake
|
222
|
+
|
223
|
+
Running tests
|
224
|
+
|
225
|
+
rake test
|
226
|
+
|
227
|
+
== TODO
|
228
|
+
|
229
|
+
* ZMQ::Message#save && ZMQ::Message.load
|
230
|
+
* Optimize zloop handler callbacks (perftools)
|
231
|
+
* OS X leaks utility - http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/leaks.1.html
|
232
|
+
* Handle GC issue with timers in loop callbacks
|
233
|
+
* czmq send methods aren't non-blocking by default
|
234
|
+
* Enforce socket timeouts
|
235
|
+
* Revisit the ZMQ::Loop API
|
236
|
+
* Push gem out to rubygems.org
|
237
|
+
* RDOC fail on mixed C and Ruby source files that document that same constants
|
238
|
+
* GC guards to prevent recycling objects being sent / received.
|
239
|
+
* Sockets can bind && connect to multiple endpoints - account for that
|
240
|
+
* Watch out for further cases where REQ / REP pairs could raise EFSM
|
241
|
+
* Do not clobber local scope from macros (James's commit in master)
|
242
|
+
* Support installation without vendor'ed libs as well
|
243
|
+
* Incorporate examples into CI as well
|
244
|
+
|
245
|
+
== Contact, feedback and bugs
|
246
|
+
|
247
|
+
This project is still work in progress and I'm looking for guidance on API design, use cases and any outlier experiences. Please log bugs and suggestions at https://github.com/methodmissing/rbczmq/issues
|
data/Rakefile
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems' unless defined?(Gem)
|
4
|
+
require 'rake' unless defined?(Rake)
|
5
|
+
|
6
|
+
# Prefer compiled Rubinius bytecode in .rbx/
|
7
|
+
ENV["RBXOPT"] = "-Xrbc.db"
|
8
|
+
|
9
|
+
require 'rake/extensiontask'
|
10
|
+
require 'rake/testtask'
|
11
|
+
begin
|
12
|
+
require 'rdoc/task'
|
13
|
+
rescue LoadError # fallback to older 1.8.7 rubies
|
14
|
+
require 'rake/rdoctask'
|
15
|
+
end
|
16
|
+
|
17
|
+
gemspec = eval(IO.read('rbczmq.gemspec'))
|
18
|
+
|
19
|
+
Gem::PackageTask.new(gemspec) do |pkg|
|
20
|
+
end
|
21
|
+
|
22
|
+
Rake::ExtensionTask.new('rbczmq', gemspec) do |ext|
|
23
|
+
ext.name = 'rbczmq_ext'
|
24
|
+
ext.ext_dir = 'ext/rbczmq'
|
25
|
+
|
26
|
+
CLEAN.include 'ext/rbczmq/dst'
|
27
|
+
CLEAN.include 'ext/zeromq'
|
28
|
+
CLEAN.include 'ext/czmq'
|
29
|
+
CLEAN.include 'lib/**/rbczmq_ext.*'
|
30
|
+
end
|
31
|
+
|
32
|
+
Rake::RDocTask.new do |rd|
|
33
|
+
files = FileList["README.rdoc", "lib/**/*.rb", "ext/rbczmq/*.c"]
|
34
|
+
rd.title = "rbczmq - binding for the high level ZeroMQ C API"
|
35
|
+
rd.main = "README.rdoc"
|
36
|
+
rd.rdoc_dir = "doc"
|
37
|
+
rd.options << "--promiscuous"
|
38
|
+
rd.rdoc_files.include(files)
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'Run rbczmq tests'
|
42
|
+
Rake::TestTask.new(:test) do |t|
|
43
|
+
t.test_files = Dir.glob("test/**/test_*.rb")
|
44
|
+
t.verbose = true
|
45
|
+
t.warning = true
|
46
|
+
end
|
47
|
+
|
48
|
+
namespace :debug do
|
49
|
+
desc "Run the test suite under gdb"
|
50
|
+
task :gdb do
|
51
|
+
system "gdb --args ruby rake"
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "Run the test suite under valgrind"
|
55
|
+
task :valgrind do
|
56
|
+
valgrind_opts = "--num-callers=5 --error-limit=no --partial-loads-ok=yes --undef-value-errors=no --leak-check=full"
|
57
|
+
system %[valgrind #{valgrind_opts} ruby -w -I"lib" -I"/Users/lourens/.rvm/gems/ruby-1.8.7-p352/gems/rake-0.9.2/lib" "/Users/lourens/.rvm/gems/ruby-1.8.7-p352/gems/rake-0.9.2/lib/rake/rake_test_loader.rb" "test/test_context.rb" "test/test_frame.rb" "test/test_loop.rb" "test/test_message.rb" "test/test_socket.rb" "test/test_threading.rb" "test/test_timer.rb" "test/test_zmq.rb"]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
desc 'Clobber Rubinius .rbc files'
|
62
|
+
task :clobber_rbc do
|
63
|
+
sh 'find . -name *.rbc -print0 | xargs -0 rm'
|
64
|
+
end
|
65
|
+
|
66
|
+
task :test => :compile
|
67
|
+
task :default => :test
|
data/examples/loop.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$:.unshift('.')
|
4
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__)) + '/../lib')
|
5
|
+
require 'zmq'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
class ConsumerHandler < ZMQ::Handler
|
9
|
+
def initialize(pollable, consumer)
|
10
|
+
super
|
11
|
+
@consumer = consumer
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_readable
|
15
|
+
@consumer.perform recv
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ProducerHandler < ZMQ::Handler
|
20
|
+
def initialize(pollable, producer)
|
21
|
+
super
|
22
|
+
@producer = producer
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_writable
|
26
|
+
@producer.work
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Consumer
|
31
|
+
attr_reader :thread
|
32
|
+
def initialize(ctx, endpoint, topic = "")
|
33
|
+
@socket = ctx.socket(:SUB)
|
34
|
+
@socket.subscribe("")
|
35
|
+
@socket.connect(endpoint)
|
36
|
+
# verbose output
|
37
|
+
@socket.verbose = true
|
38
|
+
@socket.subscribe(topic)
|
39
|
+
@jobs, @working = 0, 0.0
|
40
|
+
end
|
41
|
+
|
42
|
+
def start
|
43
|
+
ZL.register_readable(@socket, ConsumerHandler, self)
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def stop
|
48
|
+
ZL.remove(@socket)
|
49
|
+
stats
|
50
|
+
end
|
51
|
+
|
52
|
+
def perform(work)
|
53
|
+
# Random hot loop to simulate CPU intensive work
|
54
|
+
start = Time.now
|
55
|
+
work.to_i.times{}
|
56
|
+
@jobs += 1
|
57
|
+
@working += (Time.now - start).to_f
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def stats
|
62
|
+
puts "Processed #{@jobs} jobs in %.4f seconds" % @working
|
63
|
+
$stdout.flush
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Producer
|
68
|
+
def initialize(ctx, endpoint, topic = "")
|
69
|
+
@ctx, @endpoint, @topic, @consumers = ctx, endpoint, topic, []
|
70
|
+
@socket = ctx.socket(:PUB)
|
71
|
+
# verbose output
|
72
|
+
@socket.verbose = true
|
73
|
+
@socket.bind(endpoint)
|
74
|
+
@socket.linger = 1
|
75
|
+
@interrupted = false
|
76
|
+
end
|
77
|
+
|
78
|
+
def spawn_consumers(count = 10)
|
79
|
+
count.times do
|
80
|
+
@consumers << Consumer.new(@ctx, @endpoint, @topic).start
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def start
|
85
|
+
ZL.register_writable(@socket, ProducerHandler, self)
|
86
|
+
end
|
87
|
+
|
88
|
+
def stop
|
89
|
+
@consumers.each{|c| c.stop }
|
90
|
+
ZL.remove(@socket)
|
91
|
+
ZL.stop
|
92
|
+
@ctx.destroy
|
93
|
+
end
|
94
|
+
|
95
|
+
def work
|
96
|
+
work = "#{@topic}#{rand(100_000).to_s}"
|
97
|
+
@socket.send(work)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
ZL.run do
|
102
|
+
ctx = ZMQ::Context.new
|
103
|
+
producer = Producer.new(ctx, 'inproc://example.loop')
|
104
|
+
producer.spawn_consumers
|
105
|
+
trap(:INT) do
|
106
|
+
producer.stop
|
107
|
+
end
|
108
|
+
producer.start
|
109
|
+
end
|