rbczmq 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|