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.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +237 -0
- data/Rakefile +9 -0
- data/em-zmq-tp10.gemspec +23 -0
- data/lib/em-zmq-tp10.rb +1 -0
- data/lib/em/protocols/zmq2.rb +25 -0
- data/lib/em/protocols/zmq2/connection.rb +37 -0
- data/lib/em/protocols/zmq2/dealer.rb +133 -0
- data/lib/em/protocols/zmq2/inproc.rb +147 -0
- data/lib/em/protocols/zmq2/pub_sub.rb +102 -0
- data/lib/em/protocols/zmq2/queue_per_peer.rb +57 -0
- data/lib/em/protocols/zmq2/rep.rb +64 -0
- data/lib/em/protocols/zmq2/req.rb +325 -0
- data/lib/em/protocols/zmq2/router.rb +69 -0
- data/lib/em/protocols/zmq2/socket.rb +236 -0
- data/lib/em/protocols/zmq2/socket_connection.rb +151 -0
- data/lib/em/protocols/zmq2/version.rb +7 -0
- data/lib/em/protocols/zmq_tp10.rb +2 -0
- data/tests/helper.rb +102 -0
- data/tests/run_all.rb +3 -0
- data/tests/test_dealer.rb +237 -0
- data/tests/test_inproc.rb +113 -0
- data/tests/test_pub_sub.rb +271 -0
- data/tests/test_reconnect.rb +64 -0
- data/tests/test_rep.rb +117 -0
- data/tests/test_req.rb +229 -0
- data/tests/test_router.rb +221 -0
- metadata +108 -0
@@ -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
|