em-zmq-tp10 0.1.7

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