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