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
data/tests/helper.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'ffi-rzmq'
|
2
|
+
module Native
|
3
|
+
extend self
|
4
|
+
ZBIND_ADDR = 'tcp://127.0.0.1:7890'
|
5
|
+
ZCONNECT_ADDR = 'tcp://127.0.0.1:7891'
|
6
|
+
|
7
|
+
def native_context
|
8
|
+
@zctx = ZMQ::Context.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_context
|
12
|
+
native_context
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
close_context
|
16
|
+
end
|
17
|
+
|
18
|
+
def close_context
|
19
|
+
@zctx.terminate
|
20
|
+
end
|
21
|
+
|
22
|
+
def setup_pair(kind)
|
23
|
+
kind_n = ZMQ.const_get(kind)
|
24
|
+
@zbind = @zctx.socket(kind_n)
|
25
|
+
@zbind.identity = "BIND_#{kind}"
|
26
|
+
@zbind.bind(ZBIND_ADDR)
|
27
|
+
@zconnect = @zctx.socket(kind_n)
|
28
|
+
@zconnect.identity = "CONNECT_#{kind}"
|
29
|
+
@zconnect.connect(ZCONNECT_ADDR)
|
30
|
+
[@zbind, @zconnect]
|
31
|
+
end
|
32
|
+
|
33
|
+
def with_socket_pair(kind)
|
34
|
+
with_context do
|
35
|
+
begin
|
36
|
+
yield setup_pair(kind)
|
37
|
+
ensure
|
38
|
+
close_pair
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def close_pair
|
44
|
+
@zbind.setsockopt(ZMQ::LINGER, 0)
|
45
|
+
@zconnect.setsockopt(ZMQ::LINGER, 0)
|
46
|
+
@zbind.close
|
47
|
+
@zconnect.close
|
48
|
+
end
|
49
|
+
|
50
|
+
def setup_socket(kind)
|
51
|
+
kind_n = ZMQ.const_get(kind)
|
52
|
+
@zbind = @zctx.socket(kind_n)
|
53
|
+
@zbind.identity = "BIND_#{kind}"
|
54
|
+
@zbind.bind(ZBIND_ADDR)
|
55
|
+
@zbind
|
56
|
+
end
|
57
|
+
|
58
|
+
def with_socket(kind)
|
59
|
+
with_context do
|
60
|
+
begin
|
61
|
+
yield setup_socket(kind)
|
62
|
+
ensure
|
63
|
+
close_socket
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def close_socket
|
69
|
+
@zbind.setsockopt(ZMQ::LINGER, 0)
|
70
|
+
@zbind.close
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module DeferredMixin
|
75
|
+
def initialize(opts={})
|
76
|
+
super(opts)
|
77
|
+
@connected = opts[:connected]
|
78
|
+
@finished = opts[:finished]
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_writer :connected, :finished
|
82
|
+
|
83
|
+
def peer_free(peer_ident, connection)
|
84
|
+
super
|
85
|
+
if @connected && @free_peers.size == 2
|
86
|
+
@connected, connected = nil, @connected
|
87
|
+
connected.succeed
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
module IncomingMixin
|
93
|
+
attr :incoming_queue
|
94
|
+
def initialize(opts={})
|
95
|
+
super
|
96
|
+
@incoming_queue = []
|
97
|
+
end
|
98
|
+
|
99
|
+
def receive_message(message)
|
100
|
+
@incoming_queue << message
|
101
|
+
end
|
102
|
+
end
|
data/tests/run_all.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require File.expand_path('../helper.rb', __FILE__)
|
4
|
+
|
5
|
+
require 'em/protocols/zmq2/dealer'
|
6
|
+
|
7
|
+
describe 'Dealer' do
|
8
|
+
|
9
|
+
let(:connected) do
|
10
|
+
EM::DefaultDeferrable.new
|
11
|
+
end
|
12
|
+
|
13
|
+
describe EM::Protocols::Zmq2::PreDealer do
|
14
|
+
class MyPreDealer < EM::Protocols::Zmq2::PreDealer
|
15
|
+
|
16
|
+
attr :incoming_queue
|
17
|
+
def initialize(opts={})
|
18
|
+
super(opts)
|
19
|
+
@connected = opts[:connected]
|
20
|
+
@incoming_queue = []
|
21
|
+
end
|
22
|
+
def peer_free(peer_ident, connection)
|
23
|
+
super
|
24
|
+
@connected.succeed if @free_peers.size == 2
|
25
|
+
end
|
26
|
+
def receive_message(message)
|
27
|
+
@incoming_queue << message
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr :dealer
|
32
|
+
before do
|
33
|
+
@dealer = MyPreDealer.new(identity: 'MyDealer', connected: connected)
|
34
|
+
@dealer.connect(Native::ZBIND_ADDR)
|
35
|
+
@dealer.bind(Native::ZCONNECT_ADDR)
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:messages) do
|
39
|
+
64.times.map{|n| ['', 'hello', 'world', n.to_s]}
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be able to send message" do
|
43
|
+
results = []
|
44
|
+
Native.with_socket_pair('ROUTER') do |zbind, zconnect|
|
45
|
+
EM.run {
|
46
|
+
connected.callback {
|
47
|
+
messages.each{|message|
|
48
|
+
dealer.send_message(message).must_equal true
|
49
|
+
}
|
50
|
+
dealer.close do
|
51
|
+
EM.next_tick{ EM.stop }
|
52
|
+
end
|
53
|
+
}
|
54
|
+
}
|
55
|
+
result = []
|
56
|
+
while zbind.recv_strings(result, ZMQ::NOBLOCK) != -1
|
57
|
+
result.shift.must_equal 'MyDealer'
|
58
|
+
results << result
|
59
|
+
result = []
|
60
|
+
end
|
61
|
+
results.size.must_be :>, 0
|
62
|
+
results.size.must_be :<, messages.size
|
63
|
+
while zconnect.recv_strings(result, ZMQ::NOBLOCK) != -1
|
64
|
+
result.shift.must_equal 'MyDealer'
|
65
|
+
results << result
|
66
|
+
result = []
|
67
|
+
end
|
68
|
+
end
|
69
|
+
(messages - results).must_be_empty
|
70
|
+
(results - messages).must_be_empty
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should be able to receive messages" do
|
74
|
+
Native.with_socket_pair('ROUTER') do |zbind, zconnect|
|
75
|
+
EM.run {
|
76
|
+
connected.callback {
|
77
|
+
messages[0...(messages.size/2)].each{|message|
|
78
|
+
zbind.send_strings ['MyDealer', *message]
|
79
|
+
}
|
80
|
+
messages[(messages.size/2)..-1].each{|message|
|
81
|
+
zconnect.send_strings ['MyDealer', *message]
|
82
|
+
}
|
83
|
+
}
|
84
|
+
cb = proc do
|
85
|
+
if dealer.incoming_queue.size < messages.size
|
86
|
+
EM.add_timer(0.5, cb)
|
87
|
+
else
|
88
|
+
EM.next_tick{ EM.stop }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
cb.call
|
92
|
+
}
|
93
|
+
end
|
94
|
+
dealer.incoming_queue.size.must_equal messages.size
|
95
|
+
(dealer.incoming_queue - messages).must_be_empty
|
96
|
+
(messages - dealer.incoming_queue).must_be_empty
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe EM::Protocols::Zmq2::Dealer do
|
101
|
+
class MyDealer < EM::Protocols::Zmq2::Dealer
|
102
|
+
attr :incoming_queue, :canceled_messages
|
103
|
+
def initialize(opts={})
|
104
|
+
super(opts)
|
105
|
+
@connected = opts[:connected]
|
106
|
+
@incoming_queue = []
|
107
|
+
@canceled_messages = []
|
108
|
+
end
|
109
|
+
def peer_free(peer_ident, connection)
|
110
|
+
super
|
111
|
+
@connected.succeed if @free_peers.size == 2
|
112
|
+
end
|
113
|
+
def receive_message(message)
|
114
|
+
@incoming_queue << message
|
115
|
+
end
|
116
|
+
def cancel_message(message)
|
117
|
+
@canceled_messages << message
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
attr :dealer
|
122
|
+
before do
|
123
|
+
@dealer = MyDealer.new(identity: 'MyDealer', connected: connected)
|
124
|
+
@dealer.connect(Native::ZBIND_ADDR)
|
125
|
+
@dealer.bind(Native::ZCONNECT_ADDR)
|
126
|
+
end
|
127
|
+
|
128
|
+
let(:messages) do
|
129
|
+
10000.times.map{|n| ['', 'hello', 'world', n.to_s]}
|
130
|
+
end
|
131
|
+
|
132
|
+
class RouteCollector
|
133
|
+
attr :res_a, :res_b
|
134
|
+
def initialize(till)
|
135
|
+
@till = till
|
136
|
+
@res_a, @res_b = [], []
|
137
|
+
end
|
138
|
+
def full?
|
139
|
+
@res_a.size + @res_b.size >= @till
|
140
|
+
end
|
141
|
+
def full_res
|
142
|
+
@res_a + @res_b
|
143
|
+
end
|
144
|
+
def set_sockets(zbind, zconnect)
|
145
|
+
@zbind = zbind
|
146
|
+
@zconnect = zconnect
|
147
|
+
end
|
148
|
+
def thread
|
149
|
+
Thread.new do
|
150
|
+
result = []
|
151
|
+
until full?
|
152
|
+
while @zbind.recv_strings(result, ZMQ::NOBLOCK) != -1
|
153
|
+
result.shift.must_equal 'MyDealer'
|
154
|
+
@res_a << result
|
155
|
+
result = []
|
156
|
+
end
|
157
|
+
while @zconnect.recv_strings(result, ZMQ::NOBLOCK) != -1
|
158
|
+
result.shift.must_equal 'MyDealer'
|
159
|
+
@res_b << result
|
160
|
+
result = []
|
161
|
+
end
|
162
|
+
sleep(0.01)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should be able to send a lot of messages" do
|
169
|
+
collector = RouteCollector.new(messages.size)
|
170
|
+
Native.with_socket_pair('ROUTER') do |zbind, zconnect|
|
171
|
+
collector.set_sockets zbind, zconnect
|
172
|
+
thrd = collector.thread
|
173
|
+
EM.run {
|
174
|
+
connected.callback {
|
175
|
+
messages.each{|message|
|
176
|
+
dealer.send_message(message)
|
177
|
+
}
|
178
|
+
dealer.close do
|
179
|
+
EM.next_tick{ EM.stop }
|
180
|
+
end
|
181
|
+
}
|
182
|
+
}
|
183
|
+
thrd.join
|
184
|
+
end
|
185
|
+
collector.full?.must_equal true
|
186
|
+
collector.res_a.size.must_be :>, 0
|
187
|
+
collector.res_a.size.must_be :<, messages.size
|
188
|
+
(messages - collector.full_res).must_be_empty
|
189
|
+
(collector.full_res - messages).must_be_empty
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should be closed after writting" do
|
193
|
+
collector = RouteCollector.new(messages.size)
|
194
|
+
Native.with_socket_pair('ROUTER') do |zbind, zconnect|
|
195
|
+
collector.set_sockets zbind, zconnect
|
196
|
+
thrd = collector.thread
|
197
|
+
EM.run {
|
198
|
+
connected.callback {
|
199
|
+
messages.each{|message|
|
200
|
+
dealer.send_message(message)
|
201
|
+
}
|
202
|
+
dealer.close do
|
203
|
+
EM.next_tick{ EM.stop }
|
204
|
+
end
|
205
|
+
}
|
206
|
+
}
|
207
|
+
thrd.join
|
208
|
+
end
|
209
|
+
collector.full?.must_equal true
|
210
|
+
collector.res_a.size.must_be :>, 0
|
211
|
+
collector.res_a.size.must_be :<, messages.size
|
212
|
+
(messages - collector.full_res).must_be_empty
|
213
|
+
(collector.full_res - messages).must_be_empty
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should not accept message on low hwm with strategy :drop_last" do
|
217
|
+
dealer.hwm = 1
|
218
|
+
dealer.hwm_strategy = :drop_last
|
219
|
+
EM.run {
|
220
|
+
dealer.send_message(['hi', 'ho1']).must_equal true
|
221
|
+
dealer.send_message(['hi', 'ho2']).wont_equal true
|
222
|
+
EM.stop
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should cancel earlier message on low hwm with strategy :drop_first" do
|
227
|
+
dealer.hwm = 1
|
228
|
+
dealer.hwm_strategy = :drop_first
|
229
|
+
EM.run {
|
230
|
+
dealer.send_message(['hi', 'ho1']).must_equal true
|
231
|
+
dealer.send_message(['hi', 'ho2']).must_equal true
|
232
|
+
EM.stop
|
233
|
+
}
|
234
|
+
dealer.canceled_messages.must_equal [['hi', 'ho1']]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require File.expand_path('../helper.rb', __FILE__)
|
4
|
+
|
5
|
+
require 'em/protocols/zmq2'
|
6
|
+
|
7
|
+
describe 'InProc' do
|
8
|
+
let(:connected) do
|
9
|
+
EM::DefaultDeferrable.new
|
10
|
+
end
|
11
|
+
|
12
|
+
class IPDealer < EM::Protocols::Zmq2::Dealer
|
13
|
+
include DeferredMixin
|
14
|
+
include IncomingMixin
|
15
|
+
def peers
|
16
|
+
@peers
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:messages) do
|
21
|
+
1000.times.map{|i| ['hi', i.to_s]}
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:fan) do
|
25
|
+
IPDealer.new(connected: connected, identity: 'fan')
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:sinks) do
|
29
|
+
2.times.map{|i| IPDealer.new(identity: "sink.#{i}") }
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_test
|
33
|
+
EM.run {
|
34
|
+
connected.callback do
|
35
|
+
fan.peers['sink.0'].wont_be_nil
|
36
|
+
fan.peers['sink.1'].wont_be_nil
|
37
|
+
sinks.each{|sink| sink.peers['fan'].wont_be_nil}
|
38
|
+
messages.each{|message| fan.send_message(message)}
|
39
|
+
fan.close do
|
40
|
+
EM.next_tick{ EM.stop }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
}
|
44
|
+
sinks.each do |sink|
|
45
|
+
sink.incoming_queue.size.must_be :>, 0
|
46
|
+
sink.incoming_queue.size.must_be :<, messages.size
|
47
|
+
end
|
48
|
+
incomings = sinks.map(&:incoming_queue).flatten(1)
|
49
|
+
(incomings - messages).must_be_empty
|
50
|
+
(messages - incomings).must_be_empty
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should send messages to several connected socket" do
|
54
|
+
sinks.each{|sink| sink.connect 'inproc://fan'}
|
55
|
+
fan.bind 'inproc://fan'
|
56
|
+
run_test
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should send messages to several binded sockets" do
|
60
|
+
sinks.each{|sink|
|
61
|
+
bind_point = "inproc://#{sink.identity}"
|
62
|
+
sink.bind bind_point
|
63
|
+
fan.connect bind_point
|
64
|
+
}
|
65
|
+
run_test
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should send messages to inproc and tcp" do
|
69
|
+
sinks[0].bind 'tcp://127.0.0.1:9876'
|
70
|
+
fan.connect 'tcp://127.0.0.1:9876'
|
71
|
+
sinks[1].bind 'inproc://sink.1'
|
72
|
+
fan.connect 'inproc://sink.1'
|
73
|
+
run_test
|
74
|
+
end
|
75
|
+
|
76
|
+
if RUBY_PLATFORM !~ /window/
|
77
|
+
it "should send messages to inproc and tcp" do
|
78
|
+
sinks[0].bind 'ipc://falcon'
|
79
|
+
fan.connect 'ipc://falcon'
|
80
|
+
sinks[1].bind 'inproc://sink.1'
|
81
|
+
fan.connect 'inproc://sink.1'
|
82
|
+
run_test
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class IPPub < EM::Protocols::Zmq2::Pub
|
87
|
+
include DeferredMixin
|
88
|
+
include IncomingMixin
|
89
|
+
end
|
90
|
+
class IPSub < EM::Protocols::Zmq2::Sub
|
91
|
+
include DeferredMixin
|
92
|
+
include IncomingMixin
|
93
|
+
end
|
94
|
+
it "should pub messages to inproc and tcp sub" do
|
95
|
+
pub = IPPub.new(identity: 'pub', connected: connected)
|
96
|
+
subs = 2.times.map{ sub = IPSub.new(subscribe: 'hi') }
|
97
|
+
subs[0].bind 'tcp://127.0.0.1:9876'
|
98
|
+
pub.connect 'tcp://127.0.0.1:9876'
|
99
|
+
subs[1].bind 'inproc://sink.1'
|
100
|
+
pub.connect 'inproc://sink.1'
|
101
|
+
EM.run {
|
102
|
+
connected.callback {
|
103
|
+
messages.each{|message| pub.send_message(message)}
|
104
|
+
pub.close do
|
105
|
+
EM.next_tick{ EM.stop }
|
106
|
+
end
|
107
|
+
}
|
108
|
+
}
|
109
|
+
subs.each {|sub|
|
110
|
+
sub.incoming_queue.must_equal messages
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|