zmqmachine 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +14 -2
- data/examples/one_handed_ping_pong.rb +2 -2
- data/examples/ping_pong.rb +4 -3
- data/examples/pubsub_forwarder.rb +141 -0
- data/lib/zm/address.rb +24 -8
- data/lib/zm/devices/forwarder.rb +119 -0
- data/lib/zm/devices/queue.rb +122 -0
- data/lib/zm/devices.rb +4 -0
- data/lib/zm/reactor.rb +1 -1
- data/lib/zm/sockets/base.rb +5 -5
- data/lib/zm/sockets/sub.rb +10 -6
- data/lib/zm/timers.rb +26 -19
- data/lib/zmqmachine.rb +1 -1
- data/version.txt +1 -1
- data/zmqmachine.gemspec +5 -5
- metadata +7 -4
- data/.gitignore +0 -14
data/History.txt
CHANGED
@@ -1,7 +1,19 @@
|
|
1
|
+
== 0.3.2 / 2010-08-25
|
2
|
+
* Fixed a bug in Timers where the timers were never getting removed
|
3
|
+
after they fired. Bug was caused by a Ruby bug with #delete_if. While
|
4
|
+
deleting each fired timer, the delete_if loop was exited early via
|
5
|
+
a call to #break as an optimization. The #break caused all deletes to
|
6
|
+
be ignored.
|
7
|
+
See bug http://redmine.ruby-lang.org/issues/show/2545
|
8
|
+
* Minor fixes to the ping-pong examples. Was missing some calls to
|
9
|
+
register/deregister for read/write events. The socket semantics have
|
10
|
+
changed since earlier revisions; those changes didn't get caught until
|
11
|
+
now.
|
12
|
+
|
1
13
|
== 0.3.1 / 2010-08-16
|
2
14
|
* Forgot to load the xreq/xrep files during startup
|
3
15
|
* Added support for resetting a timer's firing schedule. Useful for when
|
4
|
-
the time source has been overridden; let's existing timers
|
16
|
+
the time source has been overridden; let's existing timers
|
5
17
|
reschedule themselves using the new time source.
|
6
18
|
* Fixed a logic bug with delivery of multi-part messages
|
7
19
|
|
@@ -19,7 +31,7 @@
|
|
19
31
|
== 0.2.0 / 2010-06-06
|
20
32
|
* Updated internals to conform to the 0mq api as of
|
21
33
|
release 2.0.7.
|
22
|
-
|
34
|
+
|
23
35
|
* Minor api enhancements.
|
24
36
|
|
25
37
|
== 0.1.0 / 2010-06-02
|
@@ -35,7 +35,7 @@ class PingPongHandler
|
|
35
35
|
def on_writable socket
|
36
36
|
rc = socket.send_message_string "#{'a' * 2048}"
|
37
37
|
@sent_count += 1
|
38
|
-
|
38
|
+
|
39
39
|
# after sending the first message, deregister for future write events
|
40
40
|
@context.deregister_writable socket
|
41
41
|
end
|
@@ -71,7 +71,7 @@ ctx1 = ZM::Reactor.new(:test).run do |context|
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
ctx1.join
|
74
|
+
ctx1.join 15_000
|
75
75
|
#puts "Started at [#{Time.now}]"
|
76
76
|
#puts "main thread will sleep [#{sleep_time}] seconds before aborting the context threads"
|
77
77
|
#sleep sleep_time
|
data/examples/ping_pong.rb
CHANGED
@@ -3,7 +3,7 @@ require 'rubygems'
|
|
3
3
|
require 'ffi-rzmq'
|
4
4
|
require '../lib/zmqmachine'
|
5
5
|
|
6
|
-
# This example illustrates how to write a simple set of
|
6
|
+
# This example illustrates how to write a simple set of
|
7
7
|
# handlers for providing message ping-pong using
|
8
8
|
# a REQ/REP socket pair. All activity is asynchronous and
|
9
9
|
# relies on non-blocking I/O.
|
@@ -30,7 +30,7 @@ class PongHandler
|
|
30
30
|
@received_count += 1
|
31
31
|
socket.send_message messages.first
|
32
32
|
@sent_count += 1
|
33
|
-
|
33
|
+
|
34
34
|
@context.next_tick { @context.stop } if @sent_count == Allowed_pongs
|
35
35
|
end
|
36
36
|
end
|
@@ -45,6 +45,7 @@ class PingHandler
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def on_attach socket
|
48
|
+
@context.register_readable socket
|
48
49
|
address = ZM::Address.new '127.0.0.1', 5555, :tcp
|
49
50
|
rc = socket.connect address
|
50
51
|
rc = socket.send_message_string "#{'a' * 2048}"
|
@@ -81,6 +82,6 @@ end
|
|
81
82
|
#end
|
82
83
|
|
83
84
|
|
84
|
-
ctx1.join
|
85
|
+
ctx1.join 15_000
|
85
86
|
#ctx2.join
|
86
87
|
puts "received [#{@pong_handler.received_count}], sent [#{@pong_handler.sent_count}]"
|
@@ -0,0 +1,141 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'ffi-rzmq'
|
4
|
+
require '../lib/zmqmachine'
|
5
|
+
|
6
|
+
|
7
|
+
# Shows how to publish from multiple PUB sockets to the same
|
8
|
+
# "bus" via a forwarder device.
|
9
|
+
#
|
10
|
+
|
11
|
+
class PublisherHandler
|
12
|
+
attr_reader :sent_count
|
13
|
+
|
14
|
+
def initialize context, port, topics
|
15
|
+
@context = context
|
16
|
+
@port = port
|
17
|
+
@topics = topics
|
18
|
+
@sent_count = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_attach socket
|
22
|
+
address = ZM::Address.new '127.0.0.1', @port, :tcp
|
23
|
+
rc = socket.connect address
|
24
|
+
# puts "publisher on_attach rc [#{rc}]"
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_writable socket
|
28
|
+
# puts "publisher writing..."
|
29
|
+
topic = @topics[rand(@topics.size)]
|
30
|
+
symbol = topic.split('.').first
|
31
|
+
|
32
|
+
if 'es' == symbol
|
33
|
+
payload = "#{topic}|#{rand(1200) + 1}|#{rand(4400)}"
|
34
|
+
else
|
35
|
+
payload = "#{topic}|#{rand(300) + 1}|#{rand(8000)}"
|
36
|
+
end
|
37
|
+
|
38
|
+
message = ZMQ::Message.new payload
|
39
|
+
socket.send_message message
|
40
|
+
@sent_count += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class SubscriberHandler
|
45
|
+
attr_reader :received_count, :topics
|
46
|
+
|
47
|
+
def initialize context, ports, topic = nil, sleep = false
|
48
|
+
@context = context
|
49
|
+
@received_count = 0
|
50
|
+
@ports = ports
|
51
|
+
(@topics ||= []) << topic.to_s
|
52
|
+
@sleep = sleep
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_attach socket
|
56
|
+
@ports.each do |port|
|
57
|
+
address = ZM::Address.new '127.0.0.1', port, :tcp
|
58
|
+
rc = socket.connect address
|
59
|
+
# puts "subscriber on_attach rc [#{rc}]"
|
60
|
+
|
61
|
+
@topics.each do |topic|
|
62
|
+
puts "subscribe to [#{topic}]"
|
63
|
+
socket.subscribe topic
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def on_readable socket, messages
|
69
|
+
@received_count += 1
|
70
|
+
sleep 0.01 if @sleep
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
sleep_time = 5
|
75
|
+
|
76
|
+
|
77
|
+
# Run the forwarder device in a separate context. *Could* be run from
|
78
|
+
# the same context as the publishers and subscribers too.
|
79
|
+
#
|
80
|
+
ctx1 = ZM::Reactor.new(:A).run do |context|
|
81
|
+
incoming = ZM::Address.new '127.0.0.1', 5555, :tcp
|
82
|
+
outgoing = "tcp://127.0.0.1:5556"
|
83
|
+
|
84
|
+
forwarder = ZM::Device::Forwarder.new context, incoming, outgoing
|
85
|
+
puts "forwarder started"
|
86
|
+
|
87
|
+
# context.oneshot_timer(3000) do
|
88
|
+
# puts "reactor one shutting down..."
|
89
|
+
# context.stop
|
90
|
+
# puts "reactor one shut down"
|
91
|
+
# end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Or, run each handler in separate contexts each with its
|
95
|
+
# own thread.
|
96
|
+
ctx2 = ZM::Reactor.new(:B).run do |context|
|
97
|
+
# start the publishers and subscribers after a 1 sec delay; give time
|
98
|
+
# to the forwarder device to start up and get ready
|
99
|
+
context.oneshot_timer(1000) do
|
100
|
+
@pub1_handler = PublisherHandler.new context, 5555, ['futures.us.es.m.10', 'futures.us.es.u.10']
|
101
|
+
context.pub_socket @pub1_handler
|
102
|
+
|
103
|
+
@pub2_handler = PublisherHandler.new context, 5555, ['futures.us.nq.m.10', 'futures.us.nq.u.10']
|
104
|
+
context.pub_socket @pub2_handler
|
105
|
+
|
106
|
+
@sub1_handler = SubscriberHandler.new context, [5556]
|
107
|
+
context.sub_socket @sub1_handler
|
108
|
+
|
109
|
+
@sub2_handler = SubscriberHandler.new context, [5556], 'futures.us.es.m'
|
110
|
+
context.sub_socket @sub2_handler
|
111
|
+
|
112
|
+
@sub3_handler = SubscriberHandler.new context, [5556], 'futures.us.es.u'
|
113
|
+
context.sub_socket @sub3_handler
|
114
|
+
|
115
|
+
@sub4_handler = SubscriberHandler.new context, [5556], 'futures.us.nq.m'
|
116
|
+
context.sub_socket @sub4_handler
|
117
|
+
|
118
|
+
@sub5_handler = SubscriberHandler.new context, [5556], 'futures.us.'
|
119
|
+
context.sub_socket @sub5_handler
|
120
|
+
end
|
121
|
+
|
122
|
+
# context.oneshot_timer(sleep_time * 1000) do
|
123
|
+
# puts "reactor two shutting down..."
|
124
|
+
# context.stop
|
125
|
+
# puts "reactor two shut down"
|
126
|
+
# end
|
127
|
+
end
|
128
|
+
|
129
|
+
# let's see how many messages we can publish in this many seconds
|
130
|
+
puts "Started at [#{Time.now}]"
|
131
|
+
puts "main thread will sleep [#{sleep_time}] seconds before aborting the reactor context threads"
|
132
|
+
sleep sleep_time
|
133
|
+
|
134
|
+
puts "done sleeping"
|
135
|
+
puts "sent [#{@pub1_handler.sent_count}]"
|
136
|
+
puts "sent [#{@pub2_handler.sent_count}]"
|
137
|
+
puts "* [#{@sub1_handler.received_count}]"
|
138
|
+
puts "futures.us.ep.m [#{@sub2_handler.received_count}]"
|
139
|
+
puts "futures.us.ep.u [#{@sub3_handler.received_count}]"
|
140
|
+
puts "futures.us.nq.m [#{@sub4_handler.received_count}]"
|
141
|
+
puts "futures.us.nq [#{@sub5_handler.received_count}]"
|
data/lib/zm/address.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
# Author:: Chuck Remes
|
4
4
|
# Homepage:: http://github.com/chuckremes/zmqmachine
|
5
5
|
# Date:: 20100602
|
6
|
-
#
|
6
|
+
#
|
7
7
|
#----------------------------------------------------------------------------
|
8
8
|
#
|
9
9
|
# Copyright (C) 2010 by Chuck Remes. All Rights Reserved.
|
10
10
|
# Email: cremes at mac dot com
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# (The MIT License)
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# Permission is hereby granted, free of charge, to any person obtaining
|
15
15
|
# a copy of this software and associated documentation files (the
|
16
16
|
# 'Software'), to deal in the Software without restriction, including
|
@@ -18,10 +18,10 @@
|
|
18
18
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
19
19
|
# permit persons to whom the Software is furnished to do so, subject to
|
20
20
|
# the following conditions:
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# The above copyright notice and this permission notice shall be
|
23
23
|
# included in all copies or substantial portions of the Software.
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
26
26
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
27
27
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
@@ -32,7 +32,7 @@
|
|
32
32
|
#
|
33
33
|
#---------------------------------------------------------------------------
|
34
34
|
#
|
35
|
-
#
|
35
|
+
#
|
36
36
|
|
37
37
|
module ZMQMachine
|
38
38
|
|
@@ -41,18 +41,34 @@ module ZMQMachine
|
|
41
41
|
#
|
42
42
|
class Address
|
43
43
|
attr_reader :host, :port, :transport
|
44
|
-
|
44
|
+
|
45
45
|
# +type+ : :tcp, :pgm or :inprocess
|
46
46
|
def initialize host, port, type = :tcp
|
47
47
|
@host = host
|
48
48
|
@port = port
|
49
49
|
@transport = determine_type type
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
def to_s
|
53
53
|
"#{@transport}://#{@host}:#{@port}"
|
54
54
|
end
|
55
55
|
|
56
|
+
|
57
|
+
# Converts strings with the format "type://host:port" into
|
58
|
+
# an Address instance.
|
59
|
+
#
|
60
|
+
def self.from_string string
|
61
|
+
#FIXME: needs error checking and ability to handle inproc/ipc types
|
62
|
+
#
|
63
|
+
# should also return nil or some other error indication when parsing fails
|
64
|
+
split = string.split(':')
|
65
|
+
type = split[0]
|
66
|
+
port = split[2]
|
67
|
+
host = split[1].sub('//', '')
|
68
|
+
|
69
|
+
Address.new host, port, type.to_sym
|
70
|
+
end
|
71
|
+
|
56
72
|
private
|
57
73
|
|
58
74
|
def determine_type type
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Author:: Chuck Remes
|
4
|
+
# Homepage:: http://github.com/chuckremes/zmqmachine
|
5
|
+
# Date:: 20100823
|
6
|
+
#
|
7
|
+
#----------------------------------------------------------------------------
|
8
|
+
#
|
9
|
+
# Copyright (C) 2010 by Chuck Remes. All Rights Reserved.
|
10
|
+
# Email: cremes at mac dot com
|
11
|
+
#
|
12
|
+
# (The MIT License)
|
13
|
+
#
|
14
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
15
|
+
# a copy of this software and associated documentation files (the
|
16
|
+
# 'Software'), to deal in the Software without restriction, including
|
17
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
18
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
19
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
20
|
+
# the following conditions:
|
21
|
+
#
|
22
|
+
# The above copyright notice and this permission notice shall be
|
23
|
+
# included in all copies or substantial portions of the Software.
|
24
|
+
#
|
25
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
26
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
27
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
28
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
29
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
30
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
31
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
32
|
+
#
|
33
|
+
#---------------------------------------------------------------------------
|
34
|
+
#
|
35
|
+
#
|
36
|
+
|
37
|
+
module ZMQMachine
|
38
|
+
|
39
|
+
module Device
|
40
|
+
|
41
|
+
|
42
|
+
# Used in conjunction with PUB/SUB sockets to allow multiple publishers to
|
43
|
+
# all publish to the same "bus."
|
44
|
+
#
|
45
|
+
# The basic mechanics are that the program contains 1 (or more) publishers that
|
46
|
+
# broadcast to the same bus. Connecting to
|
47
|
+
# an intermediate queue device allows for the publishers to have all of their
|
48
|
+
# traffic aggregated to a single port.
|
49
|
+
#
|
50
|
+
# Example:
|
51
|
+
#
|
52
|
+
# # the queue creates sockets and binds to both given addresses; all messages get
|
53
|
+
# # republished from +incoming+ to +outgoing+
|
54
|
+
# forwarder = ZM::Device::Forwarder.new reactor, "tcp://192.168.0.100:5050", "tcp://192.168.0.100:5051"
|
55
|
+
#
|
56
|
+
# # the +pub_handler+ internally calls "connect" to the incoming address given above
|
57
|
+
# pub1 = reactor.pub_socket pub_handler
|
58
|
+
# pub2 = reactor.pub_socket pub_handler
|
59
|
+
#
|
60
|
+
# # the +sub_handler+ internally calls "connect" to the outgoing address given above
|
61
|
+
# subscriber = reactor.sub_socket sub_handler
|
62
|
+
#
|
63
|
+
class Forwarder
|
64
|
+
|
65
|
+
class Handler
|
66
|
+
attr_accessor :socket_out
|
67
|
+
|
68
|
+
def initialize reactor, address, debug = false
|
69
|
+
@reactor = reactor
|
70
|
+
@address = address
|
71
|
+
@debug = debug
|
72
|
+
end
|
73
|
+
|
74
|
+
def on_attach socket
|
75
|
+
socket.identity = "forwarder.#{Kernel.rand(999_999_999)}"
|
76
|
+
rc = socket.bind @address
|
77
|
+
#FIXME: error handling!
|
78
|
+
|
79
|
+
socket.subscribe_all if :sub == socket.kind
|
80
|
+
end
|
81
|
+
|
82
|
+
def on_writable socket
|
83
|
+
@reactor.deregister_writable socket
|
84
|
+
end
|
85
|
+
|
86
|
+
def on_readable socket, messages
|
87
|
+
messages.each { |msg| p msg.copy_out_string } if @debug
|
88
|
+
|
89
|
+
socket_out.send_messages messages if @socket_out
|
90
|
+
end
|
91
|
+
end # class Handler
|
92
|
+
|
93
|
+
|
94
|
+
# Takes either a properly formatted string that can be converted into a ZM::Address
|
95
|
+
# or takes a ZM::Address directly.
|
96
|
+
#
|
97
|
+
# Forwards all messages received by the +incoming+ address to the +outgoing+ address.
|
98
|
+
#
|
99
|
+
def initialize reactor, incoming, outgoing, debug = false
|
100
|
+
incoming = Address.from_string incoming if incoming.kind_of? String
|
101
|
+
outgoing = Address.from_string outgoing if outgoing.kind_of? String
|
102
|
+
|
103
|
+
# setup the handlers for processing messages
|
104
|
+
@handler_in = Handler.new reactor, incoming, debug
|
105
|
+
@handler_out = Handler.new reactor, outgoing, debug
|
106
|
+
|
107
|
+
# create each socket and pass in the appropriate handler
|
108
|
+
@incoming = reactor.sub_socket @handler_in
|
109
|
+
@outgoing = reactor.pub_socket @handler_out
|
110
|
+
|
111
|
+
# set each handler's outgoing socket
|
112
|
+
@handler_in.socket_out = @outgoing
|
113
|
+
@handler_out.socket_out = @incoming
|
114
|
+
end
|
115
|
+
end # class Forwarder
|
116
|
+
|
117
|
+
end # module Device
|
118
|
+
|
119
|
+
end # module ZMQMachine
|
@@ -0,0 +1,122 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Author:: Chuck Remes
|
4
|
+
# Homepage:: http://github.com/chuckremes/zmqmachine
|
5
|
+
# Date:: 20100823
|
6
|
+
#
|
7
|
+
#----------------------------------------------------------------------------
|
8
|
+
#
|
9
|
+
# Copyright (C) 2010 by Chuck Remes. All Rights Reserved.
|
10
|
+
# Email: cremes at mac dot com
|
11
|
+
#
|
12
|
+
# (The MIT License)
|
13
|
+
#
|
14
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
15
|
+
# a copy of this software and associated documentation files (the
|
16
|
+
# 'Software'), to deal in the Software without restriction, including
|
17
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
18
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
19
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
20
|
+
# the following conditions:
|
21
|
+
#
|
22
|
+
# The above copyright notice and this permission notice shall be
|
23
|
+
# included in all copies or substantial portions of the Software.
|
24
|
+
#
|
25
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
26
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
27
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
28
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
29
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
30
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
31
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
32
|
+
#
|
33
|
+
#---------------------------------------------------------------------------
|
34
|
+
#
|
35
|
+
#
|
36
|
+
|
37
|
+
module ZMQMachine
|
38
|
+
|
39
|
+
module Device
|
40
|
+
|
41
|
+
|
42
|
+
# Used in conjunction with REQ/REP sockets to load balance the requests and
|
43
|
+
# replies over (potentially) multiple backends.
|
44
|
+
#
|
45
|
+
# The basic mechanics are that the program contains 1 (or more) clients that
|
46
|
+
# talk to 1 (or more) backends that all perform the same work. Connecting to
|
47
|
+
# an intermediate queue device allows for the client requests to be fair-balanced
|
48
|
+
# among the available backend servers. The hidden identities passed along by
|
49
|
+
# REQ/REP sockets are used by the queue device's internal XREQ/XREP sockets to
|
50
|
+
# route the messages back to the appropriate client.
|
51
|
+
#
|
52
|
+
# Example:
|
53
|
+
#
|
54
|
+
# # the queue creates sockets and binds to both given addresses; all messages get
|
55
|
+
# # routed between the two
|
56
|
+
# queue = ZM::Device::Queue.new reactor, "tcp://192.168.0.100:5050", "tcp://192.168.0.100:5051"
|
57
|
+
#
|
58
|
+
# # the +client_handler+ internally calls "connect" to the incoming address given above
|
59
|
+
# client = reactor.req_socket client_handler
|
60
|
+
# client2 = reactor.req_socket client_handler
|
61
|
+
#
|
62
|
+
# # the +server_handler+ internally calls "connect" to the outgoing address given above
|
63
|
+
# server = reactor.rep_socket server_handler
|
64
|
+
#
|
65
|
+
class Queue
|
66
|
+
|
67
|
+
class Handler
|
68
|
+
attr_accessor :socket_out
|
69
|
+
|
70
|
+
def initialize reactor, address, debug = false, dir = 0
|
71
|
+
@reactor = reactor
|
72
|
+
@address = address
|
73
|
+
@debug = debug
|
74
|
+
@dir = dir
|
75
|
+
end
|
76
|
+
|
77
|
+
def on_attach socket
|
78
|
+
socket.identity = "queue.#{Kernel.rand(999_999_999)}"
|
79
|
+
rc = socket.bind @address
|
80
|
+
#FIXME: error handling!
|
81
|
+
end
|
82
|
+
|
83
|
+
def on_writable socket
|
84
|
+
@reactor.deregister_writable socket
|
85
|
+
end
|
86
|
+
|
87
|
+
def on_readable socket, messages
|
88
|
+
messages.each { |msg| puts "[#{@dir}] [#{msg.copy_out_string}]" } if @debug
|
89
|
+
|
90
|
+
if @socket_out
|
91
|
+
socket_out.send_messages messages
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end # class Handler
|
95
|
+
|
96
|
+
|
97
|
+
# Takes either a properly formatted string that can be converted into a ZM::Address
|
98
|
+
# or takes a ZM::Address directly.
|
99
|
+
#
|
100
|
+
# Routes all messages received by either address to the other address.
|
101
|
+
#
|
102
|
+
def initialize reactor, incoming, outgoing, debug = false
|
103
|
+
incoming = Address.from_string incoming if incoming.kind_of? String
|
104
|
+
outgoing = Address.from_string outgoing if outgoing.kind_of? String
|
105
|
+
|
106
|
+
# setup the handlers for processing messages
|
107
|
+
@handler_in = Handler.new reactor, incoming, debug, :in
|
108
|
+
@handler_out = Handler.new reactor, outgoing, debug, :out
|
109
|
+
|
110
|
+
# create each socket and pass in the appropriate handler
|
111
|
+
@incoming = reactor.xrep_socket @handler_in
|
112
|
+
@outgoing = reactor.xreq_socket @handler_out
|
113
|
+
|
114
|
+
# set each handler's outgoing socket
|
115
|
+
@handler_in.socket_out = @outgoing
|
116
|
+
@handler_out.socket_out = @incoming
|
117
|
+
end
|
118
|
+
end # class Queue
|
119
|
+
|
120
|
+
end # module Device
|
121
|
+
|
122
|
+
end # module ZMQMachine
|
data/lib/zm/devices.rb
ADDED
data/lib/zm/reactor.rb
CHANGED
@@ -332,7 +332,7 @@ module ZMQMachine
|
|
332
332
|
def cancel_timer timer
|
333
333
|
@timers.cancel timer
|
334
334
|
end
|
335
|
-
|
335
|
+
|
336
336
|
# Asks all timers to reschedule themselves starting from Timers.now.
|
337
337
|
# Typically called when the underlying time source for the ZM::Timers
|
338
338
|
# class has been replaced; existing timers may not fire as expected, so
|
data/lib/zm/sockets/base.rb
CHANGED
@@ -117,9 +117,9 @@ module ZMQMachine
|
|
117
117
|
queued = @raw_socket.send_string message, ZMQ::NOBLOCK | (multipart ? ZMQ::SNDMORE : 0)
|
118
118
|
queued
|
119
119
|
end
|
120
|
-
|
120
|
+
|
121
121
|
# Convenience method for sending a multi-part message. The
|
122
|
-
# +messages+ argument must respond to :size and :
|
122
|
+
# +messages+ argument must respond to :size, :at and :last (like
|
123
123
|
# an Array).
|
124
124
|
#
|
125
125
|
# May raise a ZMQ::SocketError.
|
@@ -128,16 +128,16 @@ module ZMQMachine
|
|
128
128
|
rc = false
|
129
129
|
i = 0
|
130
130
|
size = messages.size
|
131
|
-
|
131
|
+
|
132
132
|
# loop through all messages but the last
|
133
133
|
while size > 1 && i < size - 1 do
|
134
134
|
rc = send_message messages.at(i), true
|
135
135
|
i += 1
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
# send the last message without the multipart arg to flush
|
139
139
|
# the message to the 0mq queue
|
140
|
-
rc =
|
140
|
+
rc = send_message messages.last if size > 0
|
141
141
|
rc
|
142
142
|
end
|
143
143
|
|
data/lib/zm/sockets/sub.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
# Author:: Chuck Remes
|
4
4
|
# Homepage:: http://github.com/chuckremes/zmqmachine
|
5
5
|
# Date:: 20100602
|
6
|
-
#
|
6
|
+
#
|
7
7
|
#----------------------------------------------------------------------------
|
8
8
|
#
|
9
9
|
# Copyright (C) 2010 by Chuck Remes. All Rights Reserved.
|
10
10
|
# Email: cremes at mac dot com
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# (The MIT License)
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# Permission is hereby granted, free of charge, to any person obtaining
|
15
15
|
# a copy of this software and associated documentation files (the
|
16
16
|
# 'Software'), to deal in the Software without restriction, including
|
@@ -18,10 +18,10 @@
|
|
18
18
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
19
19
|
# permit persons to whom the Software is furnished to do so, subject to
|
20
20
|
# the following conditions:
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# The above copyright notice and this permission notice shall be
|
23
23
|
# included in all copies or substantial portions of the Software.
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
26
26
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
27
27
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
@@ -32,7 +32,7 @@
|
|
32
32
|
#
|
33
33
|
#---------------------------------------------------------------------------
|
34
34
|
#
|
35
|
-
#
|
35
|
+
#
|
36
36
|
|
37
37
|
module ZMQMachine
|
38
38
|
|
@@ -71,6 +71,10 @@ module ZMQMachine
|
|
71
71
|
@raw_socket.setsockopt ZMQ::SUBSCRIBE, topic
|
72
72
|
end
|
73
73
|
|
74
|
+
def subscribe_all
|
75
|
+
subscribe ''
|
76
|
+
end
|
77
|
+
|
74
78
|
private
|
75
79
|
|
76
80
|
def allocate_socket context
|
data/lib/zm/timers.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
# Author:: Chuck Remes
|
4
4
|
# Homepage:: http://github.com/chuckremes/zmqmachine
|
5
5
|
# Date:: 20100602
|
6
|
-
#
|
6
|
+
#
|
7
7
|
#----------------------------------------------------------------------------
|
8
8
|
#
|
9
9
|
# Copyright (C) 2010 by Chuck Remes. All Rights Reserved.
|
10
10
|
# Email: cremes at mac dot com
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# (The MIT License)
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# Permission is hereby granted, free of charge, to any person obtaining
|
15
15
|
# a copy of this software and associated documentation files (the
|
16
16
|
# 'Software'), to deal in the Software without restriction, including
|
@@ -18,10 +18,10 @@
|
|
18
18
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
19
19
|
# permit persons to whom the Software is furnished to do so, subject to
|
20
20
|
# the following conditions:
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# The above copyright notice and this permission notice shall be
|
23
23
|
# included in all copies or substantial portions of the Software.
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
26
26
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
27
27
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
@@ -32,12 +32,12 @@
|
|
32
32
|
#
|
33
33
|
#---------------------------------------------------------------------------
|
34
34
|
#
|
35
|
-
#
|
35
|
+
#
|
36
36
|
|
37
37
|
module ZMQMachine
|
38
38
|
|
39
39
|
# Manages the addition and cancellation of all timers. Each #Reactor
|
40
|
-
# maintains its own set of timers; the timer belongs to the
|
40
|
+
# maintains its own set of timers; the timer belongs to the
|
41
41
|
# reactor context.
|
42
42
|
#
|
43
43
|
# This should never be instantiated directly
|
@@ -83,7 +83,7 @@ module ZMQMachine
|
|
83
83
|
timer
|
84
84
|
end
|
85
85
|
|
86
|
-
# Cancel the +timer+.
|
86
|
+
# Cancel the +timer+.
|
87
87
|
#
|
88
88
|
# Returns +true+ when cancellation succeeds.
|
89
89
|
# Returns +false+ when it fails to find the
|
@@ -94,7 +94,7 @@ module ZMQMachine
|
|
94
94
|
end
|
95
95
|
|
96
96
|
# A convenience method that loops through all known timers
|
97
|
-
# and fires all of the expired timers.
|
97
|
+
# and fires all of the expired timers.
|
98
98
|
#
|
99
99
|
#--
|
100
100
|
# Internally the list is sorted whenever a timer is added or
|
@@ -106,35 +106,42 @@ module ZMQMachine
|
|
106
106
|
# all time is expected as milliseconds
|
107
107
|
now = Timers.now
|
108
108
|
save = []
|
109
|
+
delete = []
|
109
110
|
|
110
111
|
# timers should be sorted by expiration time
|
111
|
-
|
112
|
+
# NOTE: was using #delete_if here, but it does *not* delete any
|
113
|
+
# items when the break executes before iterating through the entire
|
114
|
+
# set; that's unacceptable so I save each timer for deletion and
|
115
|
+
# do that in a separate loop
|
116
|
+
@timers.each do |timer|
|
112
117
|
break unless timer.expired?(now)
|
113
118
|
timer.fire
|
114
119
|
save << timer if timer.periodical?
|
115
|
-
|
120
|
+
delete << timer
|
116
121
|
end
|
117
122
|
|
123
|
+
delete.each { |timer| @timers.delete timer }
|
124
|
+
|
118
125
|
# reinstate the periodicals; necessary to do in two steps
|
119
126
|
# since changing the timer.fire_time inside the loop would
|
120
127
|
# not retain proper ordering in the sorted set; re-adding it
|
121
|
-
# ensures the
|
128
|
+
# ensures the timers are in sorted order
|
122
129
|
save.each { |timer| @timers.add timer }
|
123
130
|
end
|
124
|
-
|
131
|
+
|
125
132
|
# Runs through all timers and asks each one to reschedule itself
|
126
133
|
# from Timers.now + whatever delay was originally recorded.
|
127
134
|
#
|
128
135
|
def reschedule
|
129
136
|
timers = @timers.dup
|
130
137
|
@timers.clear
|
131
|
-
|
138
|
+
|
132
139
|
timers.each do |timer|
|
133
140
|
timer.reschedule
|
134
141
|
@timers.add timer
|
135
142
|
end
|
136
143
|
end
|
137
|
-
|
144
|
+
|
138
145
|
# Returns the current time using the following algo:
|
139
146
|
#
|
140
147
|
# (Time.now.to_f * 1000).to_i
|
@@ -147,7 +154,7 @@ module ZMQMachine
|
|
147
154
|
def self.now
|
148
155
|
(Time.now.to_f * 1000).to_i
|
149
156
|
end
|
150
|
-
|
157
|
+
|
151
158
|
# Convert Timers.now to a number usable by the Time class.
|
152
159
|
#
|
153
160
|
def self.now_converted
|
@@ -157,7 +164,7 @@ module ZMQMachine
|
|
157
164
|
|
158
165
|
|
159
166
|
# Used to track the specific expiration time and execution
|
160
|
-
# code for each timer.
|
167
|
+
# code for each timer.
|
161
168
|
#
|
162
169
|
# This should never be instantiated directly
|
163
170
|
# by user code. A timer must be known to the #Reactor in which it
|
@@ -214,7 +221,7 @@ module ZMQMachine
|
|
214
221
|
def periodical?
|
215
222
|
@periodical
|
216
223
|
end
|
217
|
-
|
224
|
+
|
218
225
|
def reschedule
|
219
226
|
schedule_firing_time
|
220
227
|
end
|
@@ -234,7 +241,7 @@ module ZMQMachine
|
|
234
241
|
# had it not been late, it would fire at 20
|
235
242
|
def schedule_firing_time
|
236
243
|
@initiated = Timers.now
|
237
|
-
|
244
|
+
|
238
245
|
@fire_time = @initiated + @delay
|
239
246
|
end
|
240
247
|
|
data/lib/zmqmachine.rb
CHANGED
@@ -66,7 +66,7 @@ require 'set'
|
|
66
66
|
require 'ffi-rzmq'
|
67
67
|
|
68
68
|
# the order of files is important
|
69
|
-
%w(address exceptions timers deferrable reactor message sockets).each do |file|
|
69
|
+
%w(address exceptions timers deferrable reactor message sockets devices).each do |file|
|
70
70
|
require ZMQMachine.libpath(['zm', file])
|
71
71
|
end
|
72
72
|
|
data/version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.2
|
data/zmqmachine.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{zmqmachine}
|
5
|
-
s.version = "0.3.
|
5
|
+
s.version = "0.3.2"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Chuck Remes"]
|
9
|
-
s.date = %q{2010-08-
|
9
|
+
s.date = %q{2010-08-25}
|
10
10
|
s.description = %q{ZMQMachine is another Ruby implementation of the reactor pattern but this
|
11
11
|
time using 0mq sockets rather than POSIX sockets.
|
12
12
|
|
@@ -22,19 +22,19 @@ It is possible to extend the 0mq library to "poll" normal file
|
|
22
22
|
descriptors. This isn't on my roadmap but patches are accepted.}
|
23
23
|
s.email = %q{cremes@mac.com}
|
24
24
|
s.extra_rdoc_files = ["History.txt", "README.rdoc", "version.txt"]
|
25
|
-
s.files = [".bnsignore", "
|
25
|
+
s.files = [".bnsignore", "History.txt", "README.rdoc", "Rakefile", "examples/fake_ftp.rb", "examples/one_handed_ping_pong.rb", "examples/ping_pong.rb", "examples/pub_sub.rb", "examples/pubsub_forwarder.rb", "examples/throttled_ping_pong.rb", "lib/zm/address.rb", "lib/zm/deferrable.rb", "lib/zm/devices.rb", "lib/zm/devices/forwarder.rb", "lib/zm/devices/queue.rb", "lib/zm/exceptions.rb", "lib/zm/message.rb", "lib/zm/reactor.rb", "lib/zm/sockets.rb", "lib/zm/sockets/base.rb", "lib/zm/sockets/pair.rb", "lib/zm/sockets/pub.rb", "lib/zm/sockets/rep.rb", "lib/zm/sockets/req.rb", "lib/zm/sockets/sub.rb", "lib/zm/sockets/xrep.rb", "lib/zm/sockets/xreq.rb", "lib/zm/timers.rb", "lib/zmqmachine.rb", "spec/spec_helper.rb", "spec/zmqmachine_spec.rb", "version.txt", "zmqmachine.gemspec"]
|
26
26
|
s.homepage = %q{http://github.com/chuckremes/zmqmachine}
|
27
27
|
s.rdoc_options = ["--main", "README.rdoc"]
|
28
28
|
s.require_paths = ["lib"]
|
29
29
|
s.rubyforge_project = %q{zmqmachine}
|
30
|
-
s.rubygems_version = %q{1.3.
|
30
|
+
s.rubygems_version = %q{1.3.7}
|
31
31
|
s.summary = %q{ZMQMachine is another Ruby implementation of the reactor pattern but this time using 0mq sockets rather than POSIX sockets}
|
32
32
|
|
33
33
|
if s.respond_to? :specification_version then
|
34
34
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
35
35
|
s.specification_version = 3
|
36
36
|
|
37
|
-
if Gem::Version.new(Gem::
|
37
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
38
38
|
s.add_runtime_dependency(%q<ffi-rzmq>, [">= 0.5.0"])
|
39
39
|
s.add_development_dependency(%q<bones>, [">= 3.4.7"])
|
40
40
|
else
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 3
|
8
|
-
-
|
9
|
-
version: 0.3.
|
8
|
+
- 2
|
9
|
+
version: 0.3.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Chuck Remes
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-08-
|
17
|
+
date: 2010-08-25 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -70,7 +70,6 @@ extra_rdoc_files:
|
|
70
70
|
- version.txt
|
71
71
|
files:
|
72
72
|
- .bnsignore
|
73
|
-
- .gitignore
|
74
73
|
- History.txt
|
75
74
|
- README.rdoc
|
76
75
|
- Rakefile
|
@@ -78,9 +77,13 @@ files:
|
|
78
77
|
- examples/one_handed_ping_pong.rb
|
79
78
|
- examples/ping_pong.rb
|
80
79
|
- examples/pub_sub.rb
|
80
|
+
- examples/pubsub_forwarder.rb
|
81
81
|
- examples/throttled_ping_pong.rb
|
82
82
|
- lib/zm/address.rb
|
83
83
|
- lib/zm/deferrable.rb
|
84
|
+
- lib/zm/devices.rb
|
85
|
+
- lib/zm/devices/forwarder.rb
|
86
|
+
- lib/zm/devices/queue.rb
|
84
87
|
- lib/zm/exceptions.rb
|
85
88
|
- lib/zm/message.rb
|
86
89
|
- lib/zm/reactor.rb
|