zmqmachine 0.6.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +15 -0
  2. data/AUTHORS.txt +3 -0
  3. data/History.txt +19 -0
  4. data/examples/fake_ftp.rb +9 -1
  5. data/examples/one_handed_ping_pong.rb +2 -1
  6. data/examples/ping_pong.rb +2 -2
  7. data/examples/pub_sub.rb +10 -4
  8. data/examples/pubsub_forwarder.rb +8 -3
  9. data/examples/throttled_ping_pong.rb +2 -2
  10. data/lib/zm/configuration.rb +129 -0
  11. data/lib/zm/device.rb +4 -0
  12. data/lib/zm/device/configuration.rb +24 -0
  13. data/lib/zm/{devices → device}/forwarder.rb +48 -46
  14. data/lib/zm/{devices → device}/queue.rb +63 -44
  15. data/lib/zm/log_client.rb +2 -2
  16. data/lib/zm/log_server.rb +68 -0
  17. data/lib/zm/reactor.rb +42 -29
  18. data/lib/zm/server.rb +4 -0
  19. data/lib/zm/server/base.rb +139 -0
  20. data/lib/zm/server/configuration.rb +53 -0
  21. data/lib/zm/server/pair.rb +23 -0
  22. data/lib/zm/server/pub.rb +23 -0
  23. data/lib/zm/server/pull.rb +24 -0
  24. data/lib/zm/server/push.rb +23 -0
  25. data/lib/zm/server/rep.rb +42 -0
  26. data/lib/zm/server/req.rb +41 -0
  27. data/lib/zm/server/routing_envelope.rb +26 -0
  28. data/lib/zm/server/sub.rb +40 -0
  29. data/lib/zm/sockets.rb +1 -1
  30. data/lib/zm/sockets/base.rb +3 -12
  31. data/lib/zm/sockets/envelope_help.rb +49 -0
  32. data/lib/zm/sockets/pair.rb +0 -1
  33. data/lib/zm/sockets/pub.rb +0 -1
  34. data/lib/zm/sockets/rep.rb +45 -0
  35. data/lib/zm/sockets/req.rb +46 -1
  36. data/lib/zm/sockets/sub.rb +0 -1
  37. data/lib/zm/timers.rb +26 -13
  38. data/lib/zmqmachine.rb +1 -1
  39. data/version.txt +1 -1
  40. data/zmqmachine.gemspec +6 -6
  41. metadata +90 -91
  42. data/lib/zm/devices.rb +0 -4
  43. data/lib/zm/sockets/xrep.rb +0 -86
  44. data/lib/zm/sockets/xreq.rb +0 -86
@@ -0,0 +1,15 @@
1
+ html
2
+ pkg
3
+ doc
4
+ *.gem
5
+ *.rbc
6
+ *.tmproj
7
+ nbproject
8
+ *.bundle
9
+ *.o
10
+ .DS_Store
11
+ .rbx
12
+ examples/work
13
+ benchmark
14
+ *~
15
+ *#*
@@ -0,0 +1,3 @@
1
+ Chuck Remes, chuckremes (github)
2
+
3
+ Tim Carey-Smith, halorgium (github)
@@ -1,3 +1,22 @@
1
+ == 0.7.1 / 2011-11-30
2
+ * Updated the Devices to use a Device::Configuration instance
3
+ for configuration rather than that long method signature.
4
+ Makes all components conform to this new configuration
5
+ mechanism consistently.
6
+
7
+ == 0.7.0 / 2011-10-28
8
+ * Created a class for holding Reactor and Server configurations.
9
+ This is much nicer than passing in individual args or a hash.
10
+
11
+ * Added the ZM::Servers module to make building servers a little
12
+ easier. The biggest improvement is for making servers that
13
+ utilize XREP/ROUTER sockets since the module handles the
14
+ splitting of the routing envelope messages from the body.
15
+
16
+ * Fixed a bug where a timer that raises an exception prevented
17
+ other periodical timers from renewing. Thanks to Tim
18
+ Carey-Smith for the bug report!
19
+
1
20
  == 0.6.0 / 2011-10-04
2
21
  * Compatibility with ffi-rzmq 0.9.0 and added some conditional
3
22
  logic to support 0mq 2.1.x and 3.x APIs.
@@ -93,6 +93,10 @@ class FTPControlClient
93
93
 
94
94
  @data_client.should_expect reply['total_chunks']
95
95
  end
96
+
97
+ def on_readable_error socket, error
98
+ puts "on_readable_error, error [#{error}], descr [#{ZMQ::Util.error_string}]"
99
+ end
96
100
 
97
101
  def get filename, &blk
98
102
  sub_address = ZM::Address.new @address.host, 5556, @address.transport
@@ -204,6 +208,10 @@ class FTPControlServer
204
208
  @pub_handler = nil
205
209
  end
206
210
  end
211
+
212
+ def on_readable_error socket, error
213
+ puts "on_readable_error, error [#{error}], descr [#{ZMQ::Util.error_string}]"
214
+ end
207
215
 
208
216
  def on_writable socket
209
217
  puts "#{self.class.name}#on_writable ERROR, should never be called"
@@ -223,7 +231,7 @@ end
223
231
 
224
232
 
225
233
  # Run both handlers within the same reactor context
226
- ctx1 = ZM::Reactor.new(:test).run do |context|
234
+ ctx1 = ZM::Reactor.new.run do |context|
227
235
 
228
236
  @request_server = FTPControlServer.new context, server_address
229
237
  context.rep_socket @request_server
@@ -29,6 +29,7 @@ class PingPongHandler
29
29
  when :request
30
30
  rc = socket.connect address
31
31
  @context.register_readable socket
32
+ @context.register_writable socket
32
33
  end
33
34
  end
34
35
 
@@ -57,7 +58,7 @@ end
57
58
 
58
59
  handler = nil
59
60
  # Run both handlers within the same reactor context
60
- ctx1 = ZM::Reactor.new(:test).run do |context|
61
+ ctx1 = ZM::Reactor.new.run do |context|
61
62
  handler = PingPongHandler.new context
62
63
 
63
64
  context.rep_socket handler
@@ -60,7 +60,7 @@ class PingHandler
60
60
  end
61
61
 
62
62
  # Run both handlers within the same reactor context
63
- ctx1 = ZM::Reactor.new(:test).run do |context|
63
+ ctx1 = ZM::Reactor.new.run do |context|
64
64
  @pong_handler = PongHandler.new context
65
65
  context.rep_socket @pong_handler
66
66
 
@@ -76,7 +76,7 @@ end
76
76
 
77
77
  # Or, run each handler in separate contexts each with its
78
78
  # own thread.
79
- #ctx2 = ZM::Reactor.new(:test).run do |context|
79
+ #ctx2 = ZM::Reactor.new.run do |context|
80
80
  # @ping_handler = PingHandler.new context
81
81
  # context.req_socket @ping_handler
82
82
  #end
@@ -79,8 +79,14 @@ end
79
79
  # handler is only getting instantiated and run in one reactor at a time. The subscriber
80
80
  # handlers can be instantiated multiple times in each reactor if you so choose.
81
81
 
82
+ configuration = ZM::Configuration.new do
83
+ name 'publisher reactor'
84
+ poll_interval 3
85
+ exception_handler Proc.new { |exc| raise(exc) }
86
+ end
87
+
82
88
  # Run all handlers within the same reactor context
83
- ctx1 = ZM::Reactor.new(:A).run do |context|
89
+ ctx1 = ZM::Reactor.new(configuration).run do |context|
84
90
  @pub1_handler = PublisherHandler.new context, 5555, ['futures.us.es.m.10', 'futures.us.es.u.10']
85
91
  context.pub_socket @pub1_handler
86
92
 
@@ -105,7 +111,7 @@ end
105
111
 
106
112
  # Or, run each handler in separate contexts each with its
107
113
  # own thread.
108
- ctx2 = ZM::Reactor.new(:B).run do |context|
114
+ ctx2 = ZM::Reactor.new.run do |context|
109
115
  # @pub1_handler = PublisherHandler.new context, 5555, ['futures.us.es.m.10', 'futures.us.es.u.10']
110
116
  # context.pub_socket @pub1_handler
111
117
 
@@ -128,7 +134,7 @@ ctx2 = ZM::Reactor.new(:B).run do |context|
128
134
  # context.sub_socket @sub5_handler
129
135
  end
130
136
 
131
- ctx3 = ZM::Reactor.new(:C).run do |context|
137
+ ctx3 = ZM::Reactor.new.run do |context|
132
138
  # @pub1_handler = PublisherHandler.new context, 5555, ['futures.us.es.m.10', 'futures.us.es.u.10']
133
139
  # context.pub_socket @pub1_handler
134
140
  #
@@ -151,7 +157,7 @@ ctx3 = ZM::Reactor.new(:C).run do |context|
151
157
  # context.sub_socket @sub5_handler
152
158
  end
153
159
 
154
- ctx4 = ZM::Reactor.new(:D).run do |context|
160
+ ctx4 = ZM::Reactor.new.run do |context|
155
161
  # @pub1_handler = PublisherHandler.new context, 5555, ['futures.us.es.m.10', 'futures.us.es.u.10']
156
162
  # context.pub_socket @pub1_handler
157
163
  #
@@ -78,17 +78,22 @@ sleep_time = 10
78
78
  # Run the forwarder device in a separate context. *Could* be run from
79
79
  # the same context as the publishers and subscribers too.
80
80
  #
81
- ctx1 = ZM::Reactor.new(:A).run do |context|
81
+ ctx1 = ZM::Reactor.new.run do |context|
82
82
  incoming = ZM::Address.new '127.0.0.1', 5555, :tcp
83
83
  outgoing = "tcp://127.0.0.1:5556"
84
+
85
+ config = ZM::Device::Configuration.new
86
+ config.reactor = context
87
+ config.incoming_endpoint = incoming
88
+ config.outgoing_endpoint = outgoing
84
89
 
85
- forwarder = ZM::Device::Forwarder.new context, incoming, outgoing
90
+ forwarder = ZM::Device::Forwarder.new(config)
86
91
  puts "forwarder started"
87
92
  end
88
93
 
89
94
  # Or, run each handler in separate contexts each with its
90
95
  # own thread.
91
- ctx2 = ZM::Reactor.new(:B).run do |context|
96
+ ctx2 = ZM::Reactor.new.run do |context|
92
97
  # start the publishers and subscribers after a 1 sec delay; give time
93
98
  # to the forwarder device to start up and get ready
94
99
  context.oneshot_timer(1000) do
@@ -75,7 +75,7 @@ end
75
75
 
76
76
 
77
77
  # Run both handlers within the same reactor context
78
- ctx1 = ZM::Reactor.new(:pong).run do |context|
78
+ ctx1 = ZM::Reactor.new.run do |context|
79
79
  @pong_handler = PongHandler.new context
80
80
  context.pair_socket @pong_handler
81
81
 
@@ -87,7 +87,7 @@ end
87
87
 
88
88
  # Or, run each handler in separate contexts each with its
89
89
  # own thread.
90
- ctx2 = ZM::Reactor.new(:ping).run do |context|
90
+ ctx2 = ZM::Reactor.new.run do |context|
91
91
  @ping_handler = PingHandler.new context
92
92
  context.pair_socket @ping_handler
93
93
  end
@@ -0,0 +1,129 @@
1
+
2
+
3
+ module ZMQMachine
4
+ module ConfigClassMaker
5
+ module MethodMaker
6
+
7
+ # Helper to create some accessor methods. We have a standard one where it returns
8
+ # the value with no arg, or sets the value with an arg. Then we create a second
9
+ # method with the explicit '=' on it to allow directly setting the value.
10
+ #
11
+ # This let's us do nice things like eval a block and set the instance vars.
12
+ # e.g.
13
+ # SomeClass.new do
14
+ # field1 'set to this string'
15
+ # end
16
+ #
17
+ # or
18
+ #
19
+ # some_class = SomeClass.new
20
+ # some_class.field1 = 'set to this string'
21
+ # puts some_class.field1 # => set to this string
22
+ #
23
+ # or
24
+ #
25
+ # some_class = SomeClass.new
26
+ # some_class.field1('set to this string')
27
+ # puts some_class.field1 # => set to this string
28
+ #
29
+ def self.create_accessors(mod, fields)
30
+ fields.each do |field_name|
31
+ code = <<-code
32
+ def #{ field_name } (value = nil)
33
+ if value
34
+ @#{field_name} = value
35
+ else
36
+ @#{field_name}
37
+ end
38
+ end
39
+
40
+ def #{ field_name }=(value)
41
+ @#{field_name} = value
42
+ end
43
+ code
44
+
45
+ mod.class_eval code
46
+ end
47
+ end
48
+
49
+ end # module MethodMaker
50
+
51
+ # Dynamically generates Configuration classes. It can also create subclasses.
52
+ # Allows us to easily build subclasses of Configuration within the library. This
53
+ # functionality is *also* provided for users of this library to create their
54
+ # own Configuration classes when writing client/server programs. All of the
55
+ # inheritance is taken care of.
56
+ #
57
+ # +klass_name+ - should usually be 'Configuration'
58
+ # +fields+ - an array of strings corresponding to the accessor names
59
+ # +parent+ - the parent of this subclass (Object at the top level)
60
+ # +mod+ - the module under which this subclass should be placed
61
+ #
62
+ # module ZMQMachine
63
+ # ZMQMachine::ConfigClassMaker.create_class('Configuration', %w( one two three), Object, ZMQMachine)
64
+ # end
65
+ #
66
+ # OR for a subclass
67
+ #
68
+ # module ZMQMachine
69
+ # module Server
70
+ # ZMQMachine::ConfigClassMaker.create_class('Configuration', %w( one two three), ZMQMachine::Configuration, ZMQMachine::Server)
71
+ # end
72
+ # end
73
+ #
74
+ def self.create_class(klass_name, fields, parent, mod)
75
+ # the "here doc" usage here confuses the syntax beautifier so the indentation
76
+ # is wrong
77
+ klass = <<-KLASS
78
+ #{klass_name} = Class.new(#{parent}) do
79
+
80
+ Fields = #{fields.inspect}
81
+
82
+ # Creates a Configuration object from another object that conforms
83
+ # to the Configuration protocol.
84
+ #
85
+ def self.create_from(other_config)
86
+ config = new
87
+ Fields.each do |name|
88
+ config.send(name, other_config.send(name.to_sym)) if config.respond_to?(name.to_sym)
89
+ end
90
+ config
91
+ end
92
+
93
+ def initialize(&blk)
94
+ instance_eval(&blk) if block_given?
95
+ end
96
+
97
+ ZMQMachine::ConfigClassMaker::MethodMaker.create_accessors(self, Fields)
98
+ end
99
+ KLASS
100
+
101
+ mod.module_eval(klass)
102
+ end
103
+ end
104
+ end
105
+
106
+ module ZMQMachine
107
+
108
+ ZMQMachine::ConfigClassMaker.create_class('Configuration', %w( name poll_interval context log_endpoint exception_handler ), Object, ZMQMachine)
109
+ end # ZMQMachine
110
+
111
+
112
+
113
+ # TEST
114
+ if $0 == __FILE__
115
+ ZMQMachine::ConfigClassMaker.create_class('Configuration', %w( name context log_endpoint exception_handler ), Object, ZMQMachine)
116
+
117
+ p (ZMQMachine::Configuration.methods - Object.methods).sort
118
+ puts "\n\n"
119
+ p (ZMQMachine::Configuration.new.methods - Object.new.methods).sort
120
+
121
+ module ZMQMachine
122
+ module Server
123
+ ZMQMachine::ConfigClassMaker.create_class('Configuration', %w( bind connect endpoint ), ZMQMachine::Configuration, ZMQMachine::Server)
124
+ end
125
+ end
126
+
127
+ puts "\n\n"
128
+ p (ZMQMachine::Server::Configuration.new.methods - Object.new.methods).sort
129
+ end
@@ -0,0 +1,4 @@
1
+
2
+ %w( configuration forwarder queue ).each do |rb_file|
3
+ require File.join(File.dirname(__FILE__), 'device', rb_file)
4
+ end
@@ -0,0 +1,24 @@
1
+
2
+ module ZMQMachine
3
+ module Device
4
+ ZMQMachine::ConfigClassMaker.create_class('Configuration',
5
+ %w( reactor incoming_endpoint outgoing_endpoint topic hwm linger verbose ),
6
+ ZMQMachine::Configuration,
7
+ ZMQMachine::Device)
8
+
9
+ class Configuration
10
+
11
+ def initialize(&blk)
12
+ instance_eval(&blk) if block_given?
13
+
14
+ # set defaults
15
+ self.verbose ||= false
16
+ self.hwm ||= 1
17
+ self.linger ||= 0
18
+ self.topic ||= ''
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+
@@ -35,10 +35,8 @@
35
35
  #
36
36
 
37
37
  module ZMQMachine
38
-
39
38
  module Device
40
39
 
41
-
42
40
  # Used in conjunction with PUB/SUB sockets to allow multiple publishers to
43
41
  # all publish to the same "bus."
44
42
  #
@@ -51,108 +49,112 @@ module ZMQMachine
51
49
  #
52
50
  # # the queue creates sockets and binds to both given addresses; all messages get
53
51
  # # republished from +incoming+ to +outgoing+
54
- # forwarder = ZM::Device::Forwarder.new reactor, "tcp://192.168.0.100:5050", "tcp://192.168.0.100:5051"
52
+ # config = ZM::Device::Configuration.new
53
+ # config.reactor = reactor
54
+ # config.incoming_endpoint = "tcp://192.168.0.100:5050"
55
+ # config.outgoing_endpoint = "tcp://192.168.0.100:5051"
56
+ # config.verbose = false
57
+ # config.linger = 10 # ms
58
+ # config.hwm = 0
59
+ # config.topic = '' # get everything
60
+ # forwarder = ZM::Device::Forwarder.new(config)
55
61
  #
56
62
  # # 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
63
+ # pub1 = reactor.pub_socket(pub_handler)
64
+ # pub2 = reactor.pub_socket(pub_handler)
59
65
  #
60
66
  # # the +sub_handler+ internally calls "connect" to the outgoing address given above
61
- # subscriber = reactor.sub_socket sub_handler
67
+ # subscriber = reactor.sub_socket(sub_handler)
62
68
  #
63
69
  class Forwarder
64
70
 
65
71
  class Handler
66
72
  attr_accessor :socket_out
67
73
 
68
- def initialize reactor, address, opts = {}
69
- @reactor = reactor
74
+ def initialize(config, address)
75
+ @reactor = config.reactor
70
76
  @address = address
71
- @verbose = opts[:verbose] || false
72
- @opts = opts
77
+ @verbose = config.verbose
78
+ @config = config
73
79
 
74
80
  @messages = []
75
81
  end
76
82
 
77
- def on_attach socket
78
- set_options socket
79
- rc = socket.bind @address
83
+ def on_attach(socket)
84
+ set_options(socket)
85
+ rc = socket.bind(@address)
80
86
  error_check(rc)
81
- #FIXME: error handling!
87
+
82
88
  error_check(socket.subscribe_all) if :sub == socket.kind
83
89
  end
84
90
 
85
- def on_writable socket
86
- @reactor.deregister_writable socket
91
+ def on_writable(socket)
92
+ @reactor.deregister_writable(socket)
87
93
  end
88
94
 
89
- def on_readable socket, messages
95
+ def on_readable(socket, messages)
90
96
  messages.each { |msg| @reactor.log(:device, "[fwd] [#{msg.copy_out_string}]") } if @verbose
91
97
 
92
98
  if @socket_out
93
- rc = socket_out.send_messages messages
99
+ rc = socket_out.send_messages(messages)
94
100
  error_check(rc)
95
101
  messages.each { |message| message.close }
96
102
  end
97
103
  end
98
104
 
99
- def on_readable_error socket, return_code
100
- STDERR.puts "#{self.class}#on_readable_error, rc [#{return_code}], errno [#{ZMQ::Util.errno}], descr [#{ZMQ::Util.error_string}]"
105
+ def on_readable_error(socket, return_code)
106
+ @reactor.log(:error, "#{self.class}#on_readable_error, rc [#{return_code}], errno [#{ZMQ::Util.errno}], descr [#{ZMQ::Util.error_string}]")
101
107
  end
102
108
 
103
- if LibZMQ.version2?
109
+ if ZMQ::LibZMQ.version2?
104
110
 
105
- def set_options socket
106
- error_check(socket.raw_socket.setsockopt(ZMQ::HWM, (@opts[:hwm] || 1)))
107
- error_check(socket.raw_socket.setsockopt(ZMQ::LINGER, (@opts[:linger] || 0)))
111
+ def set_options(socket)
112
+ error_check(socket.raw_socket.setsockopt(ZMQ::HWM, @config.hwm))
113
+ error_check(socket.raw_socket.setsockopt(ZMQ::LINGER, @config.linger))
108
114
  end
109
115
 
110
- elsif LibZMQ.version3?
116
+ elsif ZMQ::LibZMQ.version3?
111
117
 
112
- def set_options socket
113
- error_check(socket.raw_socket.setsockopt(ZMQ::SNDHWM, (@opts[:hwm] || 1)))
114
- error_check(socket.raw_socket.setsockopt(ZMQ::RCVHWM, (@opts[:hwm] || 1)))
115
- error_check(socket.raw_socket.setsockopt(ZMQ::LINGER, (@opts[:linger] || 0)))
118
+ def set_options(socket)
119
+ error_check(socket.raw_socket.setsockopt(ZMQ::SNDHWM, @config.hwm))
120
+ error_check(socket.raw_socket.setsockopt(ZMQ::RCVHWM, @config.hwm))
121
+ error_check(socket.raw_socket.setsockopt(ZMQ::LINGER, @config.linger))
116
122
  end
117
123
 
118
124
  end
119
125
 
120
- def error_check rc
126
+ def error_check(rc)
121
127
  if ZMQ::Util.resultcode_ok?(rc)
122
128
  false
123
129
  else
124
- STDERR.puts "Operation failed, errno [#{ZMQ::Util.errno}] description [#{ZMQ::Util.error_string}]"
125
- caller(1).each { |callstack| STDERR.puts(callstack) }
130
+ @reactor.log(:error, "Operation failed, errno [#{ZMQ::Util.errno}] description [#{ZMQ::Util.error_string}]")
131
+ caller(1).each { |callstack| @reactor.log(:callstack, callstack) }
126
132
  true
127
133
  end
128
134
  end
129
-
130
135
  end # class Handler
131
136
 
132
137
 
133
- # Takes either a properly formatted string that can be converted into a ZM::Address
134
- # or takes a ZM::Address directly.
135
- #
136
138
  # Forwards all messages received by the +incoming+ address to the +outgoing+ address.
137
139
  #
138
- def initialize reactor, incoming, outgoing, opts = {:verbose => false}
139
- incoming = Address.from_string incoming if incoming.kind_of? String
140
- outgoing = Address.from_string outgoing if outgoing.kind_of? String
140
+ def initialize(config)
141
+ @reactor = config.reactor
142
+ incoming = Address.from_string(config.incoming_endpoint.to_s)
143
+ outgoing = Address.from_string(config.outgoing_endpoint.to_s)
141
144
 
142
145
  # setup the handlers for processing messages
143
- @handler_in = Handler.new reactor, incoming, opts
144
- @handler_out = Handler.new reactor, outgoing, opts
146
+ @handler_in = Handler.new(config, incoming)
147
+ @handler_out = Handler.new(config, outgoing)
145
148
 
146
149
  # create each socket and pass in the appropriate handler
147
- @incoming = reactor.sub_socket @handler_in
148
- @outgoing = reactor.pub_socket @handler_out
150
+ @incoming_sock = @reactor.sub_socket(@handler_in)
151
+ @outgoing_sock = @reactor.pub_socket(@handler_out)
149
152
 
150
153
  # set each handler's outgoing socket
151
- @handler_in.socket_out = @outgoing
152
- @handler_out.socket_out = @incoming
154
+ @handler_in.socket_out = @outgoing_sock
155
+ @handler_out.socket_out = @incoming_sock
153
156
  end
154
157
  end # class Forwarder
155
158
 
156
159
  end # module Device
157
-
158
160
  end # module ZMQMachine