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/test_req.rb
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require File.expand_path('../helper.rb', __FILE__)
|
4
|
+
|
5
|
+
require 'em/protocols/zmq2/req'
|
6
|
+
|
7
|
+
describe 'Req' do
|
8
|
+
let(:connected) do
|
9
|
+
EM::DefaultDeferrable.new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe EM::Protocols::Zmq2::PreReq do
|
13
|
+
class MyPreReq < EM::Protocols::Zmq2::PreReq
|
14
|
+
attr :incoming_queue
|
15
|
+
def initialize(opts={})
|
16
|
+
super
|
17
|
+
@connected = opts[:connected]
|
18
|
+
@incoming_queue = []
|
19
|
+
end
|
20
|
+
def peer_free(peer_identity, connection)
|
21
|
+
super
|
22
|
+
@connected.succeed
|
23
|
+
end
|
24
|
+
def receive_reply(message, data, request_id)
|
25
|
+
message.first.must_equal 'world'
|
26
|
+
message.last.must_equal data.to_s
|
27
|
+
@incoming_queue << ['hello', message.last]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr :req
|
32
|
+
before do
|
33
|
+
@req = MyPreReq.new(identity: 'REQ', connected: connected)
|
34
|
+
@req.connect(Native::ZBIND_ADDR)
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:messages){
|
38
|
+
300.times.map{|i| ['hello', i.to_s]} << ['hello', 'xxx']
|
39
|
+
}
|
40
|
+
|
41
|
+
it 'should send requests' do
|
42
|
+
Native.with_socket('REP') do |zrep|
|
43
|
+
thrd = Thread.new do
|
44
|
+
messages.size.times do
|
45
|
+
ar = []
|
46
|
+
zrep.recv_strings ar
|
47
|
+
ar[0] = 'world'
|
48
|
+
zrep.send_strings ar
|
49
|
+
end
|
50
|
+
end
|
51
|
+
EM.run {
|
52
|
+
connected.callback {
|
53
|
+
dup = messages.dup
|
54
|
+
cb = lambda {
|
55
|
+
if dup.empty?
|
56
|
+
EM.add_timer(0.3){
|
57
|
+
EM.next_tick{ EM.stop }
|
58
|
+
}
|
59
|
+
else
|
60
|
+
message = dup.first
|
61
|
+
if String === req.send_request(message, message.last)
|
62
|
+
dup.shift
|
63
|
+
EM.next_tick cb
|
64
|
+
else
|
65
|
+
EM.add_timer 0.1, cb
|
66
|
+
end
|
67
|
+
end
|
68
|
+
}
|
69
|
+
cb.call
|
70
|
+
}
|
71
|
+
}
|
72
|
+
thrd.join
|
73
|
+
end
|
74
|
+
(req.incoming_queue - messages).must_be_empty
|
75
|
+
(messages - req.incoming_queue).must_be_empty
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe EM::Protocols::Zmq2::Req do
|
80
|
+
class MyReq < EM::Protocols::Zmq2::Req
|
81
|
+
attr :incoming_queue, :canceled_requests
|
82
|
+
def initialize(opts={}, defered_conn, defered_mess)
|
83
|
+
super opts
|
84
|
+
@defered_conn = defered_conn
|
85
|
+
@defered_mess = defered_mess
|
86
|
+
@incoming_queue = []
|
87
|
+
@canceled_requests = []
|
88
|
+
end
|
89
|
+
def peer_free(peer_identity, connection)
|
90
|
+
super
|
91
|
+
@defered_conn.succeed
|
92
|
+
end
|
93
|
+
def receive_reply(message, data, request_id)
|
94
|
+
message.first.must_equal 'world'
|
95
|
+
message.last.must_equal data.to_s
|
96
|
+
@incoming_queue << ['hello', message.last]
|
97
|
+
@defered_mess.succeed if message.last == 'xxx'
|
98
|
+
end
|
99
|
+
def cancel_request(request_id)
|
100
|
+
@canceled_requests << request_id
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
let(:finished){ EM::DefaultDeferrable.new }
|
105
|
+
attr :req
|
106
|
+
before do
|
107
|
+
@req = MyReq.new({identity: 'REQ'}, connected, finished)
|
108
|
+
@req.connect(Native::ZBIND_ADDR)
|
109
|
+
end
|
110
|
+
let(:messages){
|
111
|
+
5000.times.map{|i| ['hello', i.to_s]} << ['hello', 'xxx']
|
112
|
+
}
|
113
|
+
|
114
|
+
it 'should send a lot of requests' do
|
115
|
+
Native.with_socket('REP') do |zrep|
|
116
|
+
thrd = Thread.new do
|
117
|
+
messages.size.times do
|
118
|
+
ar = []
|
119
|
+
zrep.recv_strings ar
|
120
|
+
ar[0] = 'world'
|
121
|
+
zrep.send_strings ar
|
122
|
+
end
|
123
|
+
end
|
124
|
+
EM.run {
|
125
|
+
connected.callback {
|
126
|
+
messages.each{|message|
|
127
|
+
req.send_request(message, message.last)
|
128
|
+
}
|
129
|
+
}
|
130
|
+
finished.callback {
|
131
|
+
EM.next_tick{ EM.stop }
|
132
|
+
}
|
133
|
+
}
|
134
|
+
thrd.join
|
135
|
+
end
|
136
|
+
(req.incoming_queue - messages).must_be_empty
|
137
|
+
(messages - req.incoming_queue).must_be_empty
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should not accept message on low hwm with strategy :drop_last" do
|
141
|
+
req.hwm = 2
|
142
|
+
req.hwm_strategy = :drop_last
|
143
|
+
EM.run {
|
144
|
+
req.send_request(['hi', 'ho1'], nil).must_be_kind_of String
|
145
|
+
req.send_request(['hi', 'ho2'], nil).must_be_kind_of String
|
146
|
+
req.send_request(['hi', 'ho3'], nil).wont_be_kind_of String
|
147
|
+
EM.stop
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should cancel earlier message on low hwm with strategy :drop_first" do
|
152
|
+
req.hwm = 2
|
153
|
+
req.hwm_strategy = :drop_first
|
154
|
+
first_req = nil
|
155
|
+
EM.run {
|
156
|
+
first_req = req.send_request(['hi', 'ho1'], nil)
|
157
|
+
first_req.must_be_kind_of String
|
158
|
+
req.send_request(['hi', 'ho2'], nil).must_be_kind_of String
|
159
|
+
req.send_request(['hi', 'ho3'], nil).must_be_kind_of String
|
160
|
+
EM.stop
|
161
|
+
}
|
162
|
+
req.canceled_requests.must_equal [first_req]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe EM::Protocols::Zmq2::ReqDefer do
|
167
|
+
class TSTRep < EM::Protocols::Zmq2::Rep
|
168
|
+
include DeferredMixin
|
169
|
+
def receive_request(message, environment)
|
170
|
+
send_reply([message.first, 'yeah'], environment)
|
171
|
+
end
|
172
|
+
def peer_free(peer, conn)
|
173
|
+
super
|
174
|
+
@connected.succeed
|
175
|
+
end
|
176
|
+
end
|
177
|
+
it "should correctly setup and call deferrable" do
|
178
|
+
req = EM::Protocols::Zmq2::ReqDefer.new
|
179
|
+
def req.send_message(message, even = false)
|
180
|
+
res = super
|
181
|
+
res
|
182
|
+
end
|
183
|
+
rep = TSTRep.new(connected: connected)
|
184
|
+
rep.bind('inproc://tst')
|
185
|
+
first_success, first_error = nil, nil
|
186
|
+
second_success, second_error = nil, nil
|
187
|
+
require 'pp'
|
188
|
+
EM.run {
|
189
|
+
uniq = Object.new.freeze
|
190
|
+
first = nil
|
191
|
+
follow = proc do |null, data|
|
192
|
+
data.must_equal uniq
|
193
|
+
null.must_be_nil
|
194
|
+
req.connect('inproc://tst')
|
195
|
+
connected.callback do
|
196
|
+
stop = proc{ EM.next_tick{ EM.stop } }
|
197
|
+
second = req.send_request(['hi', 'ho'], uniq) do |reply, data|
|
198
|
+
second_success = true
|
199
|
+
reply.must_equal ['hi', 'yeah']
|
200
|
+
data.must_equal uniq
|
201
|
+
end
|
202
|
+
second.must_be_kind_of EM::Deferrable
|
203
|
+
second.errback do
|
204
|
+
second_error = true
|
205
|
+
end
|
206
|
+
second.callback &stop
|
207
|
+
second.errback &stop
|
208
|
+
end
|
209
|
+
end
|
210
|
+
first = req.send_request(['hi', 'ho'], uniq) do |reply, data|
|
211
|
+
first_success = true
|
212
|
+
reply.must_equal ['hi', 'yeah']
|
213
|
+
data.must_equal uniq
|
214
|
+
end
|
215
|
+
first.must_be_kind_of EM::Deferrable
|
216
|
+
first.timeout(0.3, 1, uniq)
|
217
|
+
first.errback do
|
218
|
+
first_error = true
|
219
|
+
end
|
220
|
+
first.callback &follow
|
221
|
+
first.errback &follow
|
222
|
+
}
|
223
|
+
first_success.must_equal nil
|
224
|
+
first_error.must_equal true
|
225
|
+
second_success.must_equal true
|
226
|
+
second_error.must_equal nil
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require File.expand_path('../helper.rb', __FILE__)
|
4
|
+
|
5
|
+
require 'em/protocols/zmq2/router'
|
6
|
+
|
7
|
+
describe 'Router' do
|
8
|
+
|
9
|
+
let(:connected) do
|
10
|
+
EM::DefaultDeferrable.new
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:finished) do
|
14
|
+
EM::DefaultDeferrable.new
|
15
|
+
end
|
16
|
+
|
17
|
+
class DealerCollector
|
18
|
+
attr :res_a, :res_b
|
19
|
+
def initialize(till)
|
20
|
+
@till = till
|
21
|
+
@res_a, @res_b = [], []
|
22
|
+
end
|
23
|
+
def full?
|
24
|
+
@res_a.size + @res_b.size >= @till
|
25
|
+
end
|
26
|
+
def full_res
|
27
|
+
@res_a + @res_b
|
28
|
+
end
|
29
|
+
def set_sockets(zbind, zconnect)
|
30
|
+
@zbind = zbind
|
31
|
+
@zconnect = zconnect
|
32
|
+
end
|
33
|
+
def thread
|
34
|
+
Thread.new do
|
35
|
+
begin
|
36
|
+
result = []
|
37
|
+
until full?
|
38
|
+
while @zbind.recv_strings(result, ZMQ::NOBLOCK) != -1
|
39
|
+
result.first.must_equal 'hello'
|
40
|
+
@res_a << result
|
41
|
+
result = []
|
42
|
+
end
|
43
|
+
while @zconnect.recv_strings(result, ZMQ::NOBLOCK) != -1
|
44
|
+
result.first.must_equal 'hello'
|
45
|
+
@res_b << result
|
46
|
+
result = []
|
47
|
+
end
|
48
|
+
sleep(0.01)
|
49
|
+
end
|
50
|
+
rescue
|
51
|
+
puts $!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe EM::Protocols::Zmq2::PreRouter do
|
58
|
+
class MyPreRouter < EM::Protocols::Zmq2::PreRouter
|
59
|
+
attr :incoming_queue
|
60
|
+
def initialize(connected, finished, opts={})
|
61
|
+
super opts
|
62
|
+
@connected = connected
|
63
|
+
@finished = finished
|
64
|
+
@incoming_queue = {}
|
65
|
+
end
|
66
|
+
def peer_free(peer_ident, connection)
|
67
|
+
super
|
68
|
+
@connected.succeed if @free_peers.size == 2
|
69
|
+
end
|
70
|
+
def receive_message(message)
|
71
|
+
(@incoming_queue[message.first] ||= []) << message[1..-1]
|
72
|
+
@finished.succeed if message.last == 'xxx'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
attr :router
|
77
|
+
before do
|
78
|
+
@router = MyPreRouter.new(connected, finished, identity: 'MyRouter')
|
79
|
+
@router.bind(Native::ZCONNECT_ADDR)
|
80
|
+
@router.connect(Native::ZBIND_ADDR)
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:messages) do
|
84
|
+
62.times.map{|n| ['hello', n.to_s] } << ['hello', 'xxx']
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should be able to receive messages" do
|
88
|
+
halves = messages[0...(messages.size/2)], messages[(messages.size/2)..-1]
|
89
|
+
Native.with_socket_pair('DEALER') do |zbind, zconnect|
|
90
|
+
EM.run {
|
91
|
+
connected.callback do
|
92
|
+
halves[0].each do |message|
|
93
|
+
zbind.send_strings(message)
|
94
|
+
end
|
95
|
+
halves[1].each do |message|
|
96
|
+
zconnect.send_strings(message)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
finished.callback do
|
100
|
+
EM.next_tick{ EM.stop }
|
101
|
+
end
|
102
|
+
}
|
103
|
+
end
|
104
|
+
router.incoming_queue['BIND_DEALER'].must_equal halves[0]
|
105
|
+
router.incoming_queue['CONNECT_DEALER'].must_equal halves[1]
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should be able to route messages" do
|
109
|
+
collector = DealerCollector.new(messages.size)
|
110
|
+
halves = messages[0...(messages.size/2)], messages[(messages.size/2)..-1]
|
111
|
+
Native.with_socket_pair('DEALER') do |zbind, zconnect|
|
112
|
+
collector.set_sockets zbind, zconnect
|
113
|
+
thrd = collector.thread
|
114
|
+
EM.run {
|
115
|
+
dup_halves = halves.map(&:dup)
|
116
|
+
connected.callback do
|
117
|
+
dup_halves[0].each do |message|
|
118
|
+
router.send_message(['BIND_DEALER', *message]).must_equal true
|
119
|
+
end
|
120
|
+
dup_halves[1].each do |message|
|
121
|
+
router.send_message(['CONNECT_DEALER', *message]).must_equal true
|
122
|
+
end
|
123
|
+
EM.defer(proc do thrd.join end, proc do
|
124
|
+
EM.next_tick{ EM.stop }
|
125
|
+
end)
|
126
|
+
end
|
127
|
+
}
|
128
|
+
end
|
129
|
+
collector.res_a.must_equal halves[0]
|
130
|
+
collector.res_b.must_equal halves[1]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe EM::Protocols::Zmq2::Router do
|
135
|
+
class MyRouter < EM::Protocols::Zmq2::Router
|
136
|
+
attr :incoming_queue, :canceled_messages
|
137
|
+
def initialize(connected, finished, opts={})
|
138
|
+
super opts
|
139
|
+
@connected = connected
|
140
|
+
@finished = finished
|
141
|
+
@incoming_queue = {}
|
142
|
+
@canceled_messages = []
|
143
|
+
end
|
144
|
+
def peer_free(peer_ident, connection)
|
145
|
+
super
|
146
|
+
@connected.succeed if @free_peers.size == 2
|
147
|
+
end
|
148
|
+
def receive_message(message)
|
149
|
+
(@incoming_queue[message.first] ||= []) << message[1..-1]
|
150
|
+
@finished.succeed if message.last == 'xxx'
|
151
|
+
end
|
152
|
+
def cancel_message(message)
|
153
|
+
@canceled_messages << message
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
attr :router
|
158
|
+
before do
|
159
|
+
@router = MyRouter.new(connected, finished, identity: 'MyRouter')
|
160
|
+
@router.bind(Native::ZCONNECT_ADDR)
|
161
|
+
@router.connect(Native::ZBIND_ADDR)
|
162
|
+
end
|
163
|
+
|
164
|
+
let(:messages) do
|
165
|
+
10000.times.map{|n| ['hello', n.to_s] } << ['hello', 'xxx']
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should be able to route a lot of messages" do
|
169
|
+
collector = DealerCollector.new(messages.size)
|
170
|
+
halves = messages[0...(messages.size/2)], messages[(messages.size/2)..-1]
|
171
|
+
Native.with_socket_pair('DEALER') do |zbind, zconnect|
|
172
|
+
collector.set_sockets zbind, zconnect
|
173
|
+
thrd = collector.thread
|
174
|
+
EM.run {
|
175
|
+
dup_halves = halves.map(&:dup)
|
176
|
+
connected.callback do
|
177
|
+
dup_halves[0].each do |message|
|
178
|
+
router.send_message(['BIND_DEALER', *message])
|
179
|
+
end
|
180
|
+
dup_halves[1].each do |message|
|
181
|
+
router.send_message(['CONNECT_DEALER', *message])
|
182
|
+
end
|
183
|
+
EM.defer(proc do thrd.join end, proc do
|
184
|
+
EM.next_tick{ EM.stop }
|
185
|
+
end)
|
186
|
+
end
|
187
|
+
}
|
188
|
+
thrd.join
|
189
|
+
end
|
190
|
+
collector.res_a.must_equal halves[0]
|
191
|
+
collector.res_b.must_equal halves[1]
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should not accept message on low hwm with strategy :drop_last" do
|
195
|
+
router.hwm = 1
|
196
|
+
router.hwm_strategy = :drop_last
|
197
|
+
EM.run {
|
198
|
+
router.send_message(['FIRST_PEER', 'hi', 'ho1']).must_equal true
|
199
|
+
router.send_message(['FIRST_PEER', 'hi', 'ho2']).wont_equal true
|
200
|
+
router.send_message(['SECOND_PEER', 'hi', 'ho1']).must_equal true
|
201
|
+
router.send_message(['SECOND_PEER', 'hi', 'ho2']).wont_equal true
|
202
|
+
EM.stop
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should cancel earlier message on low hwm with strategy :drop_first" do
|
207
|
+
router.hwm = 1
|
208
|
+
router.hwm_strategy = :drop_first
|
209
|
+
EM.run {
|
210
|
+
router.send_message(['FIRST_PEER', 'hi', 'ho1']).must_equal true
|
211
|
+
router.send_message(['FIRST_PEER', 'hi', 'ho2']).must_equal true
|
212
|
+
router.send_message(['SECOND_PEER', 'hi', 'ho1']).must_equal true
|
213
|
+
router.send_message(['SECOND_PEER', 'hi', 'ho2']).must_equal true
|
214
|
+
EM.stop
|
215
|
+
}
|
216
|
+
router.canceled_messages.count.must_equal 2
|
217
|
+
router.canceled_messages.must_include ['FIRST_PEER', 'hi', 'ho1']
|
218
|
+
router.canceled_messages.must_include ['SECOND_PEER', 'hi', 'ho1']
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|