zmqmachine 0.3.1 → 0.3.2
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/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
|