em-zmq-tp10 0.1.7

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.
@@ -0,0 +1,147 @@
1
+ require 'em/protocols/zmq2/connection'
2
+ module EventMachine
3
+ module Protocols
4
+ module Zmq2
5
+ module InProc
6
+ class AlreadyBinded < RuntimeError; end
7
+ class NotBinded < RuntimeError; end
8
+ BindPoints = {}
9
+ def self.bind(name, socket)
10
+ unless BindPoints[name].nil? || BindPoints[name] == socket
11
+ raise AlreadyBinded, "ZMQ inproc://#{name} already binded"
12
+ end
13
+ BindPoints[name] = socket
14
+ unless BindPoints[:shutdown_hook_set]
15
+ BindPoints[:shutdown_hook_set] = true
16
+ EM.add_shutdown_hook { BindPoints.clear }
17
+ end
18
+ name
19
+ end
20
+
21
+ def self.unbind(name, socket)
22
+ unless BindPoints[name] == socket
23
+ unless BindPoints[name]
24
+ raise NotBinded, "ZMQ bind point inproc://#{name} binded to other socket"
25
+ else
26
+ raise NotBinded, "ZMQ bind point inproc://#{name} is not binded"
27
+ end
28
+ end
29
+ BindPoints.delete name
30
+ end
31
+
32
+ def self.connect(name, socket)
33
+ connect = Connection.new(socket)
34
+ EM.next_tick {
35
+ if server = BindPoints[name]
36
+ sconnect = Connection.new(server)
37
+ sconnect.peer = connect
38
+ connect.peer = sconnect
39
+ else
40
+ connect.unbind
41
+ end
42
+ }
43
+ connect
44
+ end
45
+
46
+ class Connection
47
+ include ConnectionMixin
48
+ attr :peer
49
+ attr_accessor :peer_waiting, :notify_when_free
50
+ def initialize(socket)
51
+ @socket = socket
52
+ @peer = nil
53
+ @peer_waiting = false
54
+ @outgoing_queue = []
55
+ @recursion = 0
56
+ @state = :connecting
57
+ end
58
+
59
+ def peer=(peer)
60
+ raise "Already has peer" if @peer
61
+ @state = :connected
62
+ @peer = peer
63
+ peer.peer_waiting!
64
+ EM.next_tick { post_init }
65
+ end
66
+
67
+ def _not_too_busy?
68
+ @state == :connected && @outgoing_queue.size <= 32
69
+ end
70
+
71
+ def shift_outgoing_queue
72
+ message = @outgoing_queue.shift
73
+ EM.next_tick { sent_data } if @notify_when_free && _not_too_busy?
74
+ message
75
+ end
76
+
77
+ def call
78
+ if message = @peer.shift_outgoing_queue
79
+ receive_strings(message)
80
+ else
81
+ @peer.peer_waiting!
82
+ end
83
+ end
84
+
85
+ def receive_strings(message)
86
+ super
87
+ @peer.peer_waiting!
88
+ end
89
+
90
+ def peer_waiting!
91
+ unless @outgoing_queue.empty?
92
+ @peer_waiting = false
93
+ if (@recursion += 1) == 10
94
+ EM.next_tick @peer
95
+ else
96
+ @peer.call
97
+ end
98
+ else
99
+ unbind if @state == :closing
100
+ @peer_waiting = true
101
+ end
102
+ end
103
+
104
+ def send_strings(strings)
105
+ if @state == :connected
106
+ unless Array === strings
107
+ strings = Array(strings)
108
+ end
109
+ @outgoing_queue << strings
110
+ end
111
+ if @peer_waiting
112
+ @peer_waiting = false
113
+ EM.next_tick @peer
114
+ end
115
+ end
116
+
117
+ def send_strings_or_prepared(strings, prepared)
118
+ send_strings(strings)
119
+ end
120
+
121
+ def close_connection(after_writting = false)
122
+ @peer.close_connection(:peer) unless after_writting == :peer
123
+ unless after_writting == true
124
+ @outgoing_queue.clear
125
+ end
126
+ if @peer_waiting
127
+ EM.next_tick @peer
128
+ end
129
+ @state = :closing
130
+ end
131
+
132
+ def unbind
133
+ old, @state = @state, :closed
134
+ if old == :closing
135
+ @peer.peer_waiting!
136
+ end
137
+ super
138
+ end
139
+
140
+ def error?
141
+ false
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,102 @@
1
+ require 'em/protocols/zmq2/socket'
2
+ require 'em/protocols/zmq2/queue_per_peer'
3
+ module EventMachine
4
+ module Protocols
5
+ module Zmq2
6
+ # ZMQ socket which acts as SUB
7
+ # subscriptions are done by subscribe method
8
+ class Sub < Socket
9
+ class DefaultAction
10
+ def initialize(sub)
11
+ @sub = sub
12
+ end
13
+
14
+ def call(message)
15
+ @sub.receive_message(message)
16
+ end
17
+ end
18
+
19
+ def initialize(opts = {})
20
+ super
21
+ @subscriptions = {}
22
+ @default_action = DefaultAction.new(self)
23
+ subscribe_many [*opts[:subscribe]]
24
+ end
25
+
26
+ def subscribe_many(subscriptions)
27
+ subscriptions.each{|sub| subscribe *sub} if subscriptions
28
+ end
29
+
30
+ def subscribe(s, cb = nil, &block)
31
+ @subscriptions[s] = cb || block || @default_action
32
+ end
33
+
34
+ def receive_message_and_peer(message, peer)
35
+ for sub, callback in @subscriptions
36
+ matched = if String === sub
37
+ message.first.start_with?(sub)
38
+ elsif sub.respond_to?(:call)
39
+ sub.call(message.first)
40
+ else
41
+ sub === message.first # Regexp and anything you want
42
+ end
43
+ callback.call(message) if matched
44
+ end
45
+ end
46
+
47
+ def receive_message(message)
48
+ raise NoMethodError
49
+ end
50
+
51
+ private :send_message
52
+ end
53
+
54
+ class PrePub < Socket
55
+ include PackString
56
+ def send_message(message, even_if_busy = false)
57
+ sent = false
58
+ prepared = prepare_message(message)
59
+ peers = even_if_busy ? @peers : @free_peers
60
+ for identity, peer in peers
61
+ if !peer.error?
62
+ peer.send_strings_or_prepared(message, prepared)
63
+ sent = true
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ class Pub < Socket
70
+ include QueuePerPeer
71
+ include PackString
72
+
73
+ def send_message(message)
74
+ sent = false
75
+ prepared = prepare_message(message)
76
+ idents = @peers.keys | @queues.keys
77
+ for identity in idents
78
+ peer = @free_peers[identity]
79
+ queue = @queues[identity]
80
+ if peer && (queue.empty? || flush_queue(queue, peer)) &&
81
+ !peer.error? && peer.not_too_busy?
82
+ peer.send_strings_or_prepared(message, prepared)
83
+ sent = true
84
+ else
85
+ pushed = push_to_queue(queue, [message, prepared])
86
+ sent ||= pushed
87
+ end
88
+ end
89
+ sent
90
+ end
91
+ private
92
+ def cancel_message(message)
93
+ false
94
+ end
95
+
96
+ def send_formed_message(peer, from_queue)
97
+ peer.send_strings_or_prepared(*from_queue)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,57 @@
1
+ module EventMachine
2
+ module Protocols
3
+ module Zmq2
4
+ module QueuePerPeer
5
+ def initialize(opts = {})
6
+ super
7
+ @queues = {}
8
+ end
9
+
10
+ def register_peer(peer_identity, connection)
11
+ peer_identity = super
12
+ @queues[peer_identity] ||= []
13
+ peer_identity
14
+ end
15
+
16
+ def unregister_peer(peer_identity)
17
+ super
18
+ if generated_identity?(peer_identity)
19
+ @queues.delete peer_identity
20
+ end
21
+ end
22
+
23
+ def peer_free(peer, connection)
24
+ super
25
+ peer_conn = @peers[peer]
26
+ queue = @queues[peer]
27
+ flush_queue(queue, peer_conn)
28
+ end
29
+
30
+ private
31
+ def react_on_hwm_decrease
32
+ @queues.each{|_, queue| push_to_queue(queue)}
33
+ end
34
+
35
+ def flush_queue(queue, peer, even_if_busy = false)
36
+ until queue.empty?
37
+ return false if peer.error? || !(even_if_busy || peer.not_too_busy?)
38
+ send_formed_message(peer, queue.shift)
39
+ end
40
+ true
41
+ end
42
+
43
+ def send_formed_message(peer, from_queue)
44
+ peer.send_strings(from_queue)
45
+ end
46
+
47
+ def flush_all_queue
48
+ @peers.each{|peer_identity, peer|
49
+ if queue = @queues[peer_identity]
50
+ flush_queue(queue, peer, true)
51
+ end
52
+ }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,64 @@
1
+ require 'em/protocols/zmq2/router'
2
+
3
+ module EventMachine
4
+ module Protocols
5
+ module Zmq2
6
+ # mixin which transforms +PreRouter+ to +PreRep+ and +Router+ to +Rep+
7
+ module RepMixin
8
+ # you should not override +#receive_message+ in +PreRep+ and +Rep+
9
+ # see +#receive_request+ instead
10
+ def receive_message(message)
11
+ envelope, message = split_message(message)
12
+ receive_request(message, envelope)
13
+ end
14
+
15
+ # callback on incoming message, splits message and envelope
16
+ # use envelope later in +#send_reply+
17
+ def receive_request(message, envelope)
18
+ raise NoMethodError
19
+ end
20
+
21
+ # joins message and envelope into single message and sends it
22
+ def send_reply(message, envelope)
23
+ send_message([*envelope, EMPTY, *message])
24
+ end
25
+ end
26
+
27
+ # ZMQ socket which acts like REP (without outgoing queue)
28
+ #
29
+ # class EchoBangRep < EM::Protocols::Zmq2::PreRep
30
+ # def receive_request(message, envelope)
31
+ # message << "!"
32
+ # if send_reply(message, envelope)
33
+ # puts "reply sent successfuly"
34
+ # end
35
+ # end
36
+ # end
37
+ # rep = EchoBangRep.new
38
+ # rep.bind('ipc://rep')
39
+ class PreRep < PreRouter
40
+ include RepMixin
41
+ private :send_message
42
+ end
43
+
44
+ # ZMQ socket which acts like REP
45
+ #
46
+ #
47
+ # class EchoBangRep < EM::Protocols::Zmq2::Rep
48
+ # def receive_request(message, envelope)
49
+ # message << "!"
50
+ # if send_reply(message, envelope)
51
+ # puts "reply sent successfuly (or placed into queue)"
52
+ # end
53
+ # end
54
+ # end
55
+ # rep = EchoBangRep.new
56
+ # rep.bind('ipc://rep')
57
+ class Rep < Router
58
+ include RepMixin
59
+ private :send_message
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,325 @@
1
+ require 'em/protocols/zmq2/dealer'
2
+ module EventMachine
3
+ module Protocols
4
+ module Zmq2
5
+ # ZMQ socket which acts like REQ, but without outgoing message queueing,
6
+ # It generates unique request ids and uses ZMQ routing scheme for mapping
7
+ # replies.
8
+ #
9
+ # Note, that on subclassing, you should override +#receive_reply+ and not
10
+ # +#receive_message+ , and you should use +#send_request+ instead of
11
+ # +#send_message+
12
+ #
13
+ # @example
14
+ # class MyReq < EM::Protocols::Zmq2::PreReq
15
+ # def receive_reply(message, data, request_id)
16
+ # puts "received message #{message} and stored data #{data}
17
+ # end
18
+ # end
19
+ #
20
+ # req = MyReq.new
21
+ # if request_id = req.send_request(['hi'], 'ho')
22
+ # puts "Message sent"
23
+ # end
24
+ #
25
+ # @example
26
+ # class TimeoutedPreReq < EM::Protocols::Zmq2::PreReq
27
+ # def initialize(opts={})
28
+ # super
29
+ # @timeout = opts[:timeout] || 1
30
+ # end
31
+ # def cancel_request(request_id)
32
+ # data = super
33
+ # EM.cancel_timer(data[:timer]) if data[:timer]
34
+ # data[:data]
35
+ # end
36
+ # def receive_reply(message, data, request_id)
37
+ # if timer = data[:timer]
38
+ # EM.cancel_timer(data[:timer])
39
+ # end
40
+ # puts "receive message #{message.inspect}, associated data #{data[:data].inspect}"
41
+ # end
42
+ # def send_request(message, data)
43
+ # data = {data: data}
44
+ # if request_id = super(message, data)
45
+ # data[:timer] = EM.add_timer(@timeout){ cancel_request(request_id) }
46
+ # end
47
+ # request_id
48
+ # end
49
+ # end
50
+ # req = TimeoutedPreReq.new
51
+ # req.bind('ipc://req')
52
+ # callback_data = { some_data: "" }
53
+ # if request_id = req.send_request(['hello', 'world'], callback_data)
54
+ # puts "Request sent with request_id #{request_id}"
55
+ # else
56
+ # puts "No free peers"
57
+ # end
58
+ class PreReq < PreDealer
59
+ def initialize(opts = {}) # :nodoc:
60
+ super
61
+ @data = {}
62
+ end
63
+
64
+ # cancel pending request, so that callback will not be called on incoming
65
+ # message.
66
+ # @return associated data with request_id
67
+ def cancel_request(request_id)
68
+ @data.delete request_id
69
+ end
70
+
71
+ # do not override +#receive_message+ or for PreReq subclasses
72
+ def receive_message(message)
73
+ request_id, message = split_message(message)
74
+ request_id = request_id.first
75
+ if data = @data.delete(request_id)
76
+ receive_reply(message, data, request_id)
77
+ end
78
+ end
79
+
80
+ # override it to react on incoming message
81
+ # Also accept saved data by +#send_request+
82
+ def receive_reply(message, data, request_id)
83
+ raise NoMethodError
84
+ end
85
+
86
+ def form_request(message) # :nodoc:
87
+ request_id = next_uniq_identity # I believe, it large enough
88
+ [request_id, EMPTY, *message]
89
+ end
90
+
91
+ # send request and store associated callback data
92
+ # Returns assigned request id or nil, if sending is unsuccessfull
93
+ def send_request(message, data, even_if_busy = false)
94
+ request = form_request(message)
95
+ request_id = request.first
96
+ @data[request_id] = data
97
+ if send_message(request, even_if_busy)
98
+ request_id
99
+ else
100
+ @data.delete request_id
101
+ false
102
+ end
103
+ end
104
+
105
+ private :send_message
106
+ private
107
+ def cancel_message(message) # :nodoc:
108
+ cancel_request(message.first)
109
+ end
110
+ end
111
+
112
+ # ZMQ socket which acts like REQ. It also reacts on connection busyness, so
113
+ # that it is a bit smarter, than ZMQ REQ.
114
+ # It generates unique request ids and uses ZMQ routing scheme for mapping
115
+ # replies.
116
+ #
117
+ # The only visible change from PreReq is less frequent +send_request+ false return
118
+ #
119
+ # Note, that on subclassing, you should override +#receive_reply+ and not
120
+ # +#receive_message+ , and you should use +#send_request+ instead of
121
+ # +#send_message+
122
+ #
123
+ # class MyReq < EM::Protocols::Zmq2::Req
124
+ # def receive_reply(message, data, request_id)
125
+ # puts "received message #{message} and stored data #{data}
126
+ # end
127
+ # end
128
+ #
129
+ # req = MyReq.new
130
+ # if request_id = req.send_request('hi', 'ho')
131
+ # puts "Message sent"
132
+ # end
133
+ #
134
+ # class TimeoutedReq < EM::Protocols::Zmq2::PreReq
135
+ # def initialize(opts={})
136
+ # super
137
+ # @timeout = opts[:timeout] || 1
138
+ # end
139
+ # def cancel_request(request_id)
140
+ # data = super
141
+ # EM.cancel_timer(data[:timer]) if data[:timer]
142
+ # data[:data]
143
+ # end
144
+ # def receive_reply(message, data)
145
+ # if timer = data[:timer]
146
+ # EM.cancel_timer(data[:timer])
147
+ # end
148
+ # puts "receive message #{message.inspect}, associated data #{data[:data].inspect}"
149
+ # end
150
+ # def send_request(message, data)
151
+ # data = {data: data}
152
+ # if request_id = super(message, data)
153
+ # data[:timer] = EM.add_timer(@timeout){ cancel_request(request_id) }
154
+ # end
155
+ # request_id
156
+ # end
157
+ # end
158
+ # req = TimeoutedReq.new
159
+ # req.bind('ipc://req')
160
+ # callback_data = { some_data: "" }
161
+ # if request_id = req.send_request(['hello', 'world'], callback_data)
162
+ # puts "Request sent with request_id #{request_id}"
163
+ # else
164
+ # puts "No free peers and highwatermark reached"
165
+ # end
166
+ class Req < PreReq
167
+ def initialize(opts = {})
168
+ super
169
+ @requests = {}
170
+ end
171
+
172
+ # cancel pending request, so that callback will not be called on incoming
173
+ # message
174
+ # @return associated data with request_id
175
+ def cancel_request(request_id)
176
+ @requests.delete request_id
177
+ @data.delete request_id
178
+ end
179
+
180
+ def send_request(message, data)
181
+ request = form_request(message)
182
+ request_id = request.first
183
+ @data[request_id] = data
184
+ if flush_queue && send_message(request) || push_to_queue(request)
185
+ request_id
186
+ end
187
+ end
188
+
189
+ def flush_queue(even_if_busy = false)
190
+ until @requests.empty?
191
+ request_id, request = @requests.first
192
+ return false unless send_message(request, even_if_busy)
193
+ @requests.delete(request_id)
194
+ end
195
+ true
196
+ end
197
+
198
+ def flush_all_queue # :nodoc:
199
+ flush_queue(true)
200
+ end
201
+
202
+ def peer_free(peer, connection)
203
+ super
204
+ flush_queue
205
+ end
206
+
207
+ private
208
+
209
+ def react_on_hwm_decrease
210
+ push_to_queue
211
+ end
212
+
213
+ def cancel_message(message)
214
+ cancel_request(message.first)
215
+ end
216
+
217
+ def push_to_queue(request = nil)
218
+ if @requests.size >= @hwm
219
+ case @hwm_strategy
220
+ when :drop_last
221
+ if @requests.size > @hwm
222
+ new_requests = {}
223
+ @requests.each do |k,message|
224
+ if new_requests.size >= @hwm
225
+ cancel_message(message)
226
+ else
227
+ new_requests[k] = message
228
+ end
229
+ end
230
+ @requests = new_requests
231
+ end
232
+ false
233
+ when :drop_first
234
+ hwm = @hwm - (request ? 1 : 0)
235
+ while @requests.size > hwm
236
+ k, message = @requests.shift
237
+ cancel_message(message)
238
+ end
239
+ @requests[request.first] = request.dup if request
240
+ true
241
+ end
242
+ else
243
+ @requests[request.first] = request.dup if request
244
+ true
245
+ end
246
+ end
247
+ end
248
+
249
+ # Convinient Req class which accepts callback as data
250
+ #
251
+ # @example
252
+ # req = EM::Protocols::Zmq2::ReqCb.new
253
+ # req.bind('ipc://req')
254
+ # timer = nil
255
+ # request_id = req.send_request(['hello', 'world']) do |message|
256
+ # EM.cancel_timer(timer)
257
+ # puts "Message #{message}"
258
+ # end
259
+ # if request_id
260
+ # timer = EM.add_timer(1) {
261
+ # req.cancel_request(request_id)
262
+ # }
263
+ # end
264
+ class ReqCb < Req
265
+ def cancel_request(request_id)
266
+ callback = super
267
+ callback.call(nil, request_id)
268
+ end
269
+
270
+ def receive_reply(message, callback, request_id)
271
+ callback.call(message, request_id)
272
+ end
273
+
274
+ def send_request(message, callback = nil, &block)
275
+ super message, callback || block
276
+ end
277
+ end
278
+
279
+ # Convinient Req class, which returns EM::DefaultDeferable on +#send_request+
280
+ #
281
+ # @example
282
+ # req = EM::Protocolse::Zmq2::ReqDefer.new
283
+ # req.bind('ipc://req')
284
+ # data = {hi: 'ho'}
285
+ # deferable = req.send_request(['hello', 'world'], data) do |reply, data|
286
+ # puts "Reply received #{reply} #{data}"
287
+ # end
288
+ # deferable.timeout 1
289
+ # deferable.errback do
290
+ # puts "Message canceled"
291
+ # end
292
+ # deferable.callback do |reply, data|
293
+ # puts "Another callback #{reply} #{data}"
294
+ # end
295
+ class ReqDefer < Req
296
+ class Wrapped < ::Struct.new(:data, :deferrable); end
297
+
298
+ def cancel_request(request_id)
299
+ wrapped = super
300
+ wrapped.deferrable.fail(nil, wrapped.data)
301
+ end
302
+ def receive_reply(reply, wrapped, request_id)
303
+ wrapped.deferrable.succeed(reply, wrapped.data)
304
+ end
305
+ def send_request(message, data, callback = nil, &block)
306
+ deferrable = EM::DefaultDeferrable.new
307
+ wrapped = Wrapped.new(data, deferrable)
308
+ callback ||= block
309
+ if Proc === callback
310
+ deferrable.callback &callback
311
+ else
312
+ deferrable.callback{|reply, data| callback.call(reply, data)}
313
+ end
314
+ if request_id = super(message, wrapped)
315
+ deferrable.errback{ cancel_request(request_id) }
316
+ else
317
+ deferrable.fail(nil, data)
318
+ end
319
+ deferrable
320
+ end
321
+ end
322
+
323
+ end
324
+ end
325
+ end