msgpack-rpc 0.1.4 → 0.2.0
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/Rakefile +1 -1
- data/lib/msgpack/rpc.rb +479 -149
- data/test/msgpack_rpc_test.rb +18 -0
- metadata +7 -6
data/Rakefile
CHANGED
|
@@ -17,7 +17,7 @@ DESCRIPTION = "RPC library using MessagePack, a ainary-based efficient dat
|
|
|
17
17
|
RUBYFORGE_PROJECT = "msgpack"
|
|
18
18
|
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
|
19
19
|
BIN_FILES = %w( )
|
|
20
|
-
VERS = "0.
|
|
20
|
+
VERS = "0.2.0"
|
|
21
21
|
|
|
22
22
|
#REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
|
23
23
|
REV = nil
|
data/lib/msgpack/rpc.rb
CHANGED
|
@@ -40,11 +40,16 @@ class RemoteError < RPCError
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
class TimeoutError < Error
|
|
43
|
-
def initialize
|
|
44
|
-
super
|
|
43
|
+
def initialize(msg = "request timed out")
|
|
44
|
+
super
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
class ConnectError < TimeoutError
|
|
49
|
+
def initialize(msg = "connect failed")
|
|
50
|
+
super
|
|
51
|
+
end
|
|
52
|
+
end
|
|
48
53
|
|
|
49
54
|
class Responder
|
|
50
55
|
def initialize(socket, msgid)
|
|
@@ -53,7 +58,7 @@ class Responder
|
|
|
53
58
|
end
|
|
54
59
|
|
|
55
60
|
def result(retval, err = nil)
|
|
56
|
-
@socket.
|
|
61
|
+
@socket.write RPCSocket.pack_response(@msgid, retval, err)
|
|
57
62
|
end
|
|
58
63
|
|
|
59
64
|
def error(err)
|
|
@@ -62,75 +67,141 @@ class Responder
|
|
|
62
67
|
end
|
|
63
68
|
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
class Address
|
|
71
|
+
# +--+----+
|
|
72
|
+
# | 2| 4 |
|
|
73
|
+
# +--+----+
|
|
74
|
+
# port network byte order
|
|
75
|
+
# IPv4 address
|
|
76
|
+
#
|
|
77
|
+
# +--+----------------+
|
|
78
|
+
# | 2| 16 |
|
|
79
|
+
# +--+----------------+
|
|
80
|
+
# port network byte order
|
|
81
|
+
# IPv6 address
|
|
82
|
+
#
|
|
83
|
+
|
|
84
|
+
test = Socket.pack_sockaddr_in(0,'0.0.0.0')
|
|
85
|
+
if test[0] == "\0"[0] || test[1] == "\0"[0]
|
|
86
|
+
# Linux
|
|
87
|
+
IMPL_LINUX = true
|
|
88
|
+
else
|
|
89
|
+
# BSD
|
|
90
|
+
IMPL_LINUX = false
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def initialize(host, port)
|
|
94
|
+
raw = Socket.pack_sockaddr_in(port, host)
|
|
95
|
+
if IMPL_LINUX
|
|
96
|
+
family = raw.unpack('S')[0]
|
|
97
|
+
else
|
|
98
|
+
family = raw.unpack('CC')[1]
|
|
99
|
+
end
|
|
100
|
+
if family == Socket::AF_INET
|
|
101
|
+
@serial = raw[2,6]
|
|
102
|
+
elsif family == Socket::AF_INET6
|
|
103
|
+
@serial = raw[2,2] + raw[8,16]
|
|
104
|
+
else
|
|
105
|
+
raise "Unknown address family: #{family}"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
68
108
|
|
|
109
|
+
def host
|
|
110
|
+
unpack[0]
|
|
111
|
+
end
|
|
69
112
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@session = s
|
|
73
|
-
s.add_socket(self)
|
|
113
|
+
def port
|
|
114
|
+
unpack[1]
|
|
74
115
|
end
|
|
75
116
|
|
|
76
|
-
def
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
117
|
+
def connectable?
|
|
118
|
+
port != 0
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def sockaddr
|
|
122
|
+
Address.parse_sockaddr(@serial)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def unpack
|
|
126
|
+
Address.parse(@serial)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def self.parse_sockaddr(raw)
|
|
130
|
+
if raw.length == 6
|
|
131
|
+
addr = Socket.pack_sockaddr_in(0, '0.0.0.0')
|
|
132
|
+
addr[2,6] = raw[0,6]
|
|
84
133
|
else
|
|
85
|
-
|
|
134
|
+
addr = Socket.pack_sockaddr_in(0, '::')
|
|
135
|
+
addr[2,2] = raw[0,2]
|
|
136
|
+
addr[8,16] = raw[2,16]
|
|
86
137
|
end
|
|
138
|
+
addr
|
|
87
139
|
end
|
|
88
140
|
|
|
89
|
-
def
|
|
90
|
-
|
|
91
|
-
@session.on_close(self)
|
|
92
|
-
@session = nil
|
|
93
|
-
rescue
|
|
94
|
-
nil
|
|
141
|
+
def self.parse(raw)
|
|
142
|
+
Socket.unpack_sockaddr_in(parse_sockaddr(raw)).reverse
|
|
95
143
|
end
|
|
96
144
|
|
|
97
|
-
def
|
|
98
|
-
|
|
99
|
-
@session.on_request(method, param, Responder.new(self,msgid))
|
|
145
|
+
def self.load(raw)
|
|
146
|
+
Address.new *parse(raw)
|
|
100
147
|
end
|
|
101
148
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
@session.on_notify(method, param)
|
|
149
|
+
def dump
|
|
150
|
+
@serial
|
|
105
151
|
end
|
|
106
152
|
|
|
107
|
-
def
|
|
108
|
-
|
|
109
|
-
@session.on_response(msgid, error, result)
|
|
153
|
+
def to_msgpack(out = '')
|
|
154
|
+
@serial.to_msgpack(out)
|
|
110
155
|
end
|
|
111
156
|
|
|
112
|
-
def
|
|
113
|
-
|
|
157
|
+
def to_s
|
|
158
|
+
unpack.join(':')
|
|
114
159
|
end
|
|
115
160
|
|
|
116
|
-
def
|
|
117
|
-
|
|
161
|
+
def to_a
|
|
162
|
+
unpack
|
|
118
163
|
end
|
|
119
164
|
|
|
120
|
-
def
|
|
121
|
-
|
|
165
|
+
def inspect
|
|
166
|
+
"#<#{self.class} #{to_s} @serial=#{@serial.inspect}>"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def eql?(o)
|
|
171
|
+
o.class == Address && dump.eql?(o.dump)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def hash
|
|
175
|
+
dump.hash
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def ==(o)
|
|
179
|
+
eql?(o)
|
|
122
180
|
end
|
|
123
181
|
end
|
|
124
182
|
|
|
125
183
|
|
|
126
|
-
|
|
127
|
-
|
|
184
|
+
REQUEST = 0
|
|
185
|
+
RESPONSE = 1
|
|
186
|
+
NOTIFY = 2
|
|
187
|
+
INIT = 3
|
|
188
|
+
|
|
128
189
|
|
|
129
|
-
|
|
190
|
+
class RPCSocket < Rev::TCPSocket
|
|
191
|
+
def initialize(sock, session)
|
|
130
192
|
@buffer = ''
|
|
131
193
|
@nread = 0
|
|
132
194
|
@mpac = MessagePack::Unpacker.new
|
|
133
|
-
|
|
195
|
+
@s = session
|
|
196
|
+
super(sock)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def bind_session(s)
|
|
200
|
+
@s = s
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def bound?
|
|
204
|
+
!@s.nil?
|
|
134
205
|
end
|
|
135
206
|
|
|
136
207
|
def on_read(data)
|
|
@@ -155,28 +226,96 @@ class RevSocket < ::Rev::TCPSocket
|
|
|
155
226
|
end
|
|
156
227
|
end
|
|
157
228
|
|
|
229
|
+
def on_message(msg)
|
|
230
|
+
case msg[0]
|
|
231
|
+
when REQUEST
|
|
232
|
+
on_request(msg[1], msg[2], msg[3])
|
|
233
|
+
when RESPONSE
|
|
234
|
+
on_response(msg[1], msg[2], msg[3])
|
|
235
|
+
when NOTIFY
|
|
236
|
+
on_notify(msg[1], msg[2])
|
|
237
|
+
when INIT
|
|
238
|
+
on_init(msg[1])
|
|
239
|
+
else
|
|
240
|
+
raise RPCError.new("unknown message type #{msg[0]}")
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def on_connect
|
|
245
|
+
return unless @s
|
|
246
|
+
@s.on_connect(self)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def on_connect_failed
|
|
250
|
+
return unless @s
|
|
251
|
+
@s.on_connect_failed(self)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def on_close
|
|
255
|
+
return unless @s
|
|
256
|
+
@s.on_close(self)
|
|
257
|
+
@s = nil
|
|
258
|
+
rescue
|
|
259
|
+
nil
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def on_request(msgid, method, param)
|
|
263
|
+
return unless @s
|
|
264
|
+
@s.on_request(method, param, Responder.new(self,msgid))
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def on_notify(method, param)
|
|
268
|
+
return unless @s
|
|
269
|
+
@s.on_notify(method, param)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def on_response(msgid, error, result)
|
|
273
|
+
return unless @s
|
|
274
|
+
@s.on_response(msgid, error, result)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def on_init(msg)
|
|
278
|
+
# ignore
|
|
279
|
+
end
|
|
280
|
+
|
|
158
281
|
def send_message(msg)
|
|
159
282
|
write msg.to_msgpack
|
|
160
283
|
end
|
|
284
|
+
|
|
285
|
+
def self.pack_request(msgid, method, param)
|
|
286
|
+
[REQUEST, msgid, method, param].to_msgpack
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def self.pack_response(msgid, result, error)
|
|
290
|
+
[RESPONSE, msgid, error, result].to_msgpack
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def self.pack_notify(method, param)
|
|
294
|
+
[NOTIFY, method, param].to_msgpack
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def self.pack_init(msg)
|
|
298
|
+
[INIT, msg].to_msgpack
|
|
299
|
+
end
|
|
161
300
|
end
|
|
162
301
|
|
|
163
302
|
|
|
164
|
-
class
|
|
303
|
+
class Session
|
|
165
304
|
|
|
166
305
|
class BasicRequest
|
|
167
|
-
def initialize(
|
|
168
|
-
@
|
|
169
|
-
@timeout =
|
|
306
|
+
def initialize(s, loop)
|
|
307
|
+
@s = s
|
|
308
|
+
@timeout = s.timeout
|
|
170
309
|
@loop = loop
|
|
171
310
|
end
|
|
172
311
|
attr_reader :loop
|
|
173
312
|
|
|
174
313
|
def call(err, res)
|
|
175
|
-
@
|
|
314
|
+
@s = nil
|
|
176
315
|
end
|
|
177
316
|
|
|
178
317
|
def join
|
|
179
|
-
while @
|
|
318
|
+
while @s
|
|
180
319
|
@loop.run_once
|
|
181
320
|
end
|
|
182
321
|
self
|
|
@@ -193,8 +332,8 @@ class ClientSession
|
|
|
193
332
|
end
|
|
194
333
|
|
|
195
334
|
class AsyncRequest < BasicRequest
|
|
196
|
-
def initialize(
|
|
197
|
-
super(
|
|
335
|
+
def initialize(s, loop)
|
|
336
|
+
super(s, loop)
|
|
198
337
|
@error = nil
|
|
199
338
|
@result = nil
|
|
200
339
|
end
|
|
@@ -203,13 +342,13 @@ class ClientSession
|
|
|
203
342
|
def call(err, res)
|
|
204
343
|
@error = err
|
|
205
344
|
@result = res
|
|
206
|
-
@
|
|
345
|
+
@s = nil
|
|
207
346
|
end
|
|
208
347
|
end
|
|
209
348
|
|
|
210
349
|
class CallbackRequest < BasicRequest
|
|
211
|
-
def initialize(
|
|
212
|
-
super(
|
|
350
|
+
def initialize(s, loop, block)
|
|
351
|
+
super(s, loop)
|
|
213
352
|
@block = block
|
|
214
353
|
end
|
|
215
354
|
|
|
@@ -219,89 +358,164 @@ class ClientSession
|
|
|
219
358
|
end
|
|
220
359
|
|
|
221
360
|
|
|
222
|
-
def initialize(loop)
|
|
223
|
-
@
|
|
224
|
-
@
|
|
225
|
-
@
|
|
361
|
+
def initialize(initmsg, target_addr, dispatcher, loop)
|
|
362
|
+
@initmsg = initmsg
|
|
363
|
+
@target_addr = target_addr
|
|
364
|
+
@dispatcher = dispatcher || NullDispatcher.new
|
|
226
365
|
@loop = loop
|
|
227
|
-
@timeout =
|
|
366
|
+
@timeout = 10 # FIXME default timeout time
|
|
367
|
+
@reconnect = 5 # FIXME default reconnect limit
|
|
368
|
+
reset
|
|
369
|
+
end
|
|
370
|
+
attr_accessor :timeout, :reconnect
|
|
371
|
+
|
|
372
|
+
def address
|
|
373
|
+
@target_addr
|
|
228
374
|
end
|
|
229
|
-
attr_accessor :timeout
|
|
230
375
|
|
|
231
|
-
def
|
|
232
|
-
@sock
|
|
376
|
+
def on_connect(sock)
|
|
377
|
+
@sockpool.push(sock)
|
|
378
|
+
sock.write @pending
|
|
379
|
+
@pending = ""
|
|
380
|
+
@connecting = 0
|
|
233
381
|
end
|
|
234
382
|
|
|
383
|
+
def on_connect_failed(sock)
|
|
384
|
+
if @connecting < @reconnect
|
|
385
|
+
try_connect
|
|
386
|
+
@connecting += 1
|
|
387
|
+
else
|
|
388
|
+
@connecting = 0
|
|
389
|
+
@reqtable.reject! {|msgid, req|
|
|
390
|
+
begin
|
|
391
|
+
req.call ConnectError.new, nil
|
|
392
|
+
rescue
|
|
393
|
+
end
|
|
394
|
+
true
|
|
395
|
+
}
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
|
|
235
400
|
def send(method, *args)
|
|
236
|
-
|
|
401
|
+
msgid = send_request(method, args)
|
|
402
|
+
@reqtable[msgid] = AsyncRequest.new(self, @loop)
|
|
237
403
|
end
|
|
238
404
|
|
|
239
405
|
def callback(method, *args, &block)
|
|
240
|
-
|
|
406
|
+
msgid = send_request(method, args)
|
|
407
|
+
@reqtable[msgid] = CallbackRequest.new(self, @loop, block)
|
|
241
408
|
end
|
|
242
409
|
|
|
243
410
|
def call(method, *args)
|
|
411
|
+
# FIXME if @reqtable.empty? optimize
|
|
244
412
|
req = send(method, *args)
|
|
245
413
|
req.join
|
|
246
414
|
if req.error
|
|
247
|
-
raise
|
|
415
|
+
raise req.error if req.error.is_a?(Error)
|
|
248
416
|
raise RemoteError.new(req.error, req.result)
|
|
249
417
|
end
|
|
250
418
|
req.result
|
|
251
419
|
end
|
|
252
420
|
|
|
253
421
|
def notify(method, *args)
|
|
254
|
-
|
|
422
|
+
send_notify(method, args)
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def close
|
|
426
|
+
@sockpool.reject! {|sock|
|
|
427
|
+
sock.detach if sock.attached?
|
|
428
|
+
sock.close
|
|
429
|
+
true
|
|
430
|
+
}
|
|
431
|
+
reset
|
|
432
|
+
self
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def step_timeout
|
|
436
|
+
reqs = []
|
|
437
|
+
@reqtable.reject! {|msgid, req|
|
|
438
|
+
if req.step_timeout
|
|
439
|
+
reqs.push(req)
|
|
440
|
+
end
|
|
441
|
+
}
|
|
442
|
+
reqs.each {|req|
|
|
443
|
+
begin
|
|
444
|
+
req.call TimeoutError.new, nil
|
|
445
|
+
rescue
|
|
446
|
+
end
|
|
447
|
+
}
|
|
448
|
+
!@reqtable.empty?
|
|
255
449
|
end
|
|
256
450
|
|
|
257
451
|
|
|
258
452
|
def on_response(msgid, error, result)
|
|
259
453
|
if req = @reqtable.delete(msgid)
|
|
260
|
-
req.call
|
|
454
|
+
req.call(error, result)
|
|
261
455
|
end
|
|
262
456
|
end
|
|
263
457
|
|
|
264
|
-
def on_notify(method, param)
|
|
265
|
-
raise RPCError.new("unexpected notify message")
|
|
266
|
-
end
|
|
267
|
-
|
|
268
458
|
def on_request(method, param, res)
|
|
269
|
-
|
|
459
|
+
@dispatcher.dispatch_request(self, method, param, res)
|
|
270
460
|
end
|
|
271
461
|
|
|
272
|
-
def
|
|
273
|
-
@
|
|
462
|
+
def on_notify(method, param)
|
|
463
|
+
@dispatcher.dispatch_notify(self, method, param)
|
|
274
464
|
end
|
|
275
465
|
|
|
276
|
-
def
|
|
277
|
-
@
|
|
466
|
+
def on_close(sock)
|
|
467
|
+
@sockpool.delete(sock)
|
|
278
468
|
end
|
|
279
469
|
|
|
280
470
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
@
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
reqs.each {|req| req.call :TimeoutError, nil }
|
|
471
|
+
private
|
|
472
|
+
def reset
|
|
473
|
+
@sockpool = []
|
|
474
|
+
@reqtable = {}
|
|
475
|
+
@seqid = 0
|
|
476
|
+
@pending = ""
|
|
477
|
+
@connecting = 0
|
|
289
478
|
end
|
|
290
479
|
|
|
291
|
-
|
|
292
|
-
def send_real(method, param, req)
|
|
480
|
+
def send_request(method, param)
|
|
293
481
|
method = method.to_s unless method.is_a?(Integer)
|
|
294
482
|
msgid = @seqid
|
|
295
483
|
@seqid += 1; if @seqid >= 1<<31 then @seqid = 0 end
|
|
296
|
-
|
|
297
|
-
|
|
484
|
+
send_data RPCSocket.pack_request(msgid, method, param)
|
|
485
|
+
msgid
|
|
298
486
|
end
|
|
299
487
|
|
|
300
|
-
def
|
|
488
|
+
def send_notify(method, param)
|
|
301
489
|
method = method.to_s unless method.is_a?(Integer)
|
|
302
|
-
|
|
490
|
+
send_data RPCSocket.pack_notify(method, param)
|
|
303
491
|
nil
|
|
304
492
|
end
|
|
493
|
+
|
|
494
|
+
def send_data(msg)
|
|
495
|
+
unless @target_addr
|
|
496
|
+
raise RPCError.new("unexpected send request on server session")
|
|
497
|
+
end
|
|
498
|
+
if @sockpool.empty?
|
|
499
|
+
if @connecting == 0
|
|
500
|
+
try_connect
|
|
501
|
+
@connecting = 1
|
|
502
|
+
end
|
|
503
|
+
@pending << msg
|
|
504
|
+
else
|
|
505
|
+
# FIXME pesudo connection load balance
|
|
506
|
+
sock = @sockpool.first
|
|
507
|
+
sock.write msg
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
def try_connect
|
|
512
|
+
port, host = ::Socket.unpack_sockaddr_in(@target_addr.sockaddr)
|
|
513
|
+
sock = RPCSocket.connect(host, port, self) # async connect
|
|
514
|
+
if @initmsg
|
|
515
|
+
sock.write @initmsg
|
|
516
|
+
end
|
|
517
|
+
@loop.attach(sock)
|
|
518
|
+
end
|
|
305
519
|
end
|
|
306
520
|
|
|
307
521
|
|
|
@@ -338,19 +552,25 @@ class AsyncResult
|
|
|
338
552
|
end
|
|
339
553
|
|
|
340
554
|
|
|
341
|
-
class
|
|
555
|
+
class NullDispatcher
|
|
556
|
+
def dispatch_request(session, method, param, res)
|
|
557
|
+
raise RPCError.new("unexpected request message")
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def dispatch_notify(session, method, param)
|
|
561
|
+
raise RPCError.new("unexpected notify message")
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
class ObjectDispatcher
|
|
342
566
|
def initialize(obj, accept = obj.public_methods)
|
|
343
567
|
@obj = obj
|
|
344
568
|
@accept = accept.map {|m| m.is_a?(Integer) ? m : m.to_s}
|
|
345
569
|
end
|
|
346
570
|
|
|
347
|
-
def
|
|
348
|
-
# do nothing
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
def on_request(method, param, res)
|
|
571
|
+
def dispatch_request(session, method, param, res)
|
|
352
572
|
begin
|
|
353
|
-
result = forward_method(method, param)
|
|
573
|
+
result = forward_method(session, method, param)
|
|
354
574
|
rescue
|
|
355
575
|
res.error($!.to_s)
|
|
356
576
|
return
|
|
@@ -362,30 +582,23 @@ class ServerSession
|
|
|
362
582
|
end
|
|
363
583
|
end
|
|
364
584
|
|
|
365
|
-
def
|
|
366
|
-
forward_method(method, param)
|
|
585
|
+
def dispatch_notify(session, method, param)
|
|
586
|
+
forward_method(session, method, param)
|
|
367
587
|
rescue
|
|
368
588
|
end
|
|
369
589
|
|
|
370
|
-
def on_response(msgid, error, result)
|
|
371
|
-
raise RPCError.new("unexpected response message")
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
def on_close(sock)
|
|
375
|
-
# do nothing
|
|
376
|
-
@sock = nil
|
|
377
|
-
end
|
|
378
|
-
|
|
379
590
|
private
|
|
380
|
-
def forward_method(method, param)
|
|
591
|
+
def forward_method(session, method, param)
|
|
381
592
|
unless @accept.include?(method)
|
|
382
593
|
raise NoMethodError, "method `#{method}' is not accepted"
|
|
383
594
|
end
|
|
384
|
-
@obj.send(method, *param)
|
|
595
|
+
@obj.send(method, *param) { session }
|
|
385
596
|
end
|
|
386
597
|
end
|
|
387
598
|
|
|
388
|
-
|
|
599
|
+
|
|
600
|
+
Loop = Rev::Loop
|
|
601
|
+
|
|
389
602
|
|
|
390
603
|
module LoopUtil
|
|
391
604
|
attr_reader :loop
|
|
@@ -404,11 +617,42 @@ module LoopUtil
|
|
|
404
617
|
@loop.attach Timer.new(interval, repeating, &block)
|
|
405
618
|
end
|
|
406
619
|
|
|
620
|
+
class TaskQueue < Rev::AsyncWatcher
|
|
621
|
+
def initialize
|
|
622
|
+
@queue = []
|
|
623
|
+
super
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def push(task)
|
|
627
|
+
@queue.push(task)
|
|
628
|
+
signal
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def on_signal
|
|
632
|
+
while task = @queue.shift
|
|
633
|
+
begin
|
|
634
|
+
task.call
|
|
635
|
+
rescue
|
|
636
|
+
end
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
def submit(task = nil, &block)
|
|
642
|
+
task ||= block
|
|
643
|
+
unless @queue
|
|
644
|
+
@queue = TaskQueue.new
|
|
645
|
+
@loop.attach(@queue)
|
|
646
|
+
end
|
|
647
|
+
@queue.push(task)
|
|
648
|
+
end
|
|
649
|
+
|
|
407
650
|
def run
|
|
408
651
|
@loop.run
|
|
409
652
|
end
|
|
410
653
|
|
|
411
654
|
def stop
|
|
655
|
+
@queue.detach if @queue && @queue.attached?
|
|
412
656
|
@loop.stop
|
|
413
657
|
# attach dummy timer
|
|
414
658
|
@loop.attach Rev::TimerWatcher.new(0, false)
|
|
@@ -417,86 +661,172 @@ module LoopUtil
|
|
|
417
661
|
end
|
|
418
662
|
|
|
419
663
|
|
|
420
|
-
class Client
|
|
664
|
+
class Client < Session
|
|
421
665
|
def initialize(host, port, loop = Loop.new)
|
|
422
666
|
@loop = loop
|
|
423
667
|
@host = host
|
|
424
668
|
@port = port
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
@timer = Timer.new(1, true) {
|
|
669
|
+
|
|
670
|
+
target_addr = Address.new(host, port)
|
|
671
|
+
super(nil, target_addr, NullDispatcher.new, loop)
|
|
672
|
+
|
|
673
|
+
@timer = Timer.new(1, true) {
|
|
674
|
+
step_timeout
|
|
675
|
+
}
|
|
430
676
|
loop.attach(@timer)
|
|
431
677
|
end
|
|
432
678
|
attr_reader :host, :port
|
|
433
679
|
|
|
680
|
+
def self.open(host, port, loop = Loop.new, &block)
|
|
681
|
+
cli = new(host, port, loop)
|
|
682
|
+
begin
|
|
683
|
+
block.call(cli)
|
|
684
|
+
ensure
|
|
685
|
+
cli.close
|
|
686
|
+
end
|
|
687
|
+
end
|
|
688
|
+
|
|
434
689
|
def close
|
|
435
690
|
@timer.detach if @timer.attached?
|
|
436
|
-
|
|
437
|
-
@s.close
|
|
438
|
-
nil
|
|
691
|
+
super
|
|
439
692
|
end
|
|
440
693
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
end
|
|
694
|
+
include LoopUtil
|
|
695
|
+
end
|
|
444
696
|
|
|
445
|
-
def callback(method, *args, &block)
|
|
446
|
-
@s.callback(method, *args, &block)
|
|
447
|
-
end
|
|
448
697
|
|
|
449
|
-
|
|
450
|
-
|
|
698
|
+
class SessionPool
|
|
699
|
+
def initialize(loop = Loop.new)
|
|
700
|
+
@loop = loop
|
|
701
|
+
@spool = {}
|
|
702
|
+
@stimer = Timer.new(1, true, &method(:step_timeout))
|
|
703
|
+
loop.attach(@stimer)
|
|
451
704
|
end
|
|
452
705
|
|
|
453
|
-
def
|
|
454
|
-
|
|
706
|
+
def get_session(host, port)
|
|
707
|
+
target_addr = Address.new(host, port)
|
|
708
|
+
@spool[target_addr] ||= create_session(target_addr)
|
|
455
709
|
end
|
|
456
710
|
|
|
457
|
-
def
|
|
458
|
-
@s
|
|
711
|
+
def close
|
|
712
|
+
@spool.reject! {|target_addr, s|
|
|
713
|
+
s.close
|
|
714
|
+
true
|
|
715
|
+
}
|
|
716
|
+
@stimer.detach if @stimer.attached?
|
|
717
|
+
nil
|
|
459
718
|
end
|
|
460
719
|
|
|
461
|
-
|
|
462
|
-
|
|
720
|
+
include LoopUtil
|
|
721
|
+
|
|
722
|
+
protected
|
|
723
|
+
def create_session(target_addr)
|
|
724
|
+
Session.new(nil, target_addr, nil, @loop)
|
|
463
725
|
end
|
|
464
726
|
|
|
465
|
-
|
|
727
|
+
private
|
|
728
|
+
def step_timeout
|
|
729
|
+
@spool.each_pair {|target_addr, s|
|
|
730
|
+
s.step_timeout
|
|
731
|
+
}
|
|
732
|
+
end
|
|
466
733
|
end
|
|
467
734
|
|
|
468
735
|
|
|
469
|
-
class Server
|
|
470
|
-
class Socket <
|
|
471
|
-
def initialize(
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
self.session = ServerSession.new(obj, accept)
|
|
475
|
-
super(*args)
|
|
736
|
+
class Server < SessionPool
|
|
737
|
+
class Socket < RPCSocket
|
|
738
|
+
def initialize(sock, dispatcher, loop)
|
|
739
|
+
s = Session.new(nil, nil, dispatcher, loop)
|
|
740
|
+
super(sock, s)
|
|
476
741
|
end
|
|
477
742
|
end
|
|
478
743
|
|
|
479
744
|
def initialize(loop = Loop.new)
|
|
480
|
-
|
|
481
|
-
@
|
|
745
|
+
super(loop)
|
|
746
|
+
@lsocks = []
|
|
482
747
|
end
|
|
483
748
|
|
|
484
749
|
def listen(host, port, obj, accept = obj.public_methods)
|
|
485
|
-
|
|
486
|
-
|
|
750
|
+
dispatcher = ObjectDispatcher.new(obj, accept)
|
|
751
|
+
lsock = Rev::TCPServer.new(host, port, Server::Socket, dispatcher, @loop)
|
|
752
|
+
@lsocks.push(lsock)
|
|
487
753
|
@loop.attach(lsock)
|
|
488
754
|
end
|
|
489
755
|
|
|
490
756
|
def close
|
|
491
|
-
@
|
|
757
|
+
@lsocks.reject! {|lsock|
|
|
492
758
|
lsock.detach if lsock.attached?
|
|
493
759
|
lsock.close
|
|
494
760
|
true
|
|
495
761
|
}
|
|
496
|
-
|
|
762
|
+
super
|
|
763
|
+
end
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
class Cluster < SessionPool
|
|
768
|
+
class Socket < RPCSocket
|
|
769
|
+
def initialize(sock, binder)
|
|
770
|
+
@binder = binder
|
|
771
|
+
super(sock, nil)
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
def on_message(msg)
|
|
775
|
+
if bound?
|
|
776
|
+
super
|
|
777
|
+
else
|
|
778
|
+
@binder.call(self, msg)
|
|
779
|
+
end
|
|
780
|
+
end
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
def initialize(host, port, loop = Loop.new)
|
|
784
|
+
super(loop)
|
|
785
|
+
@host = host
|
|
786
|
+
@port = port
|
|
787
|
+
@dispatcher = nil
|
|
788
|
+
self_addr = Address.new(host, port)
|
|
789
|
+
@initmsg = RPCSocket.pack_init(self_addr)
|
|
790
|
+
end
|
|
791
|
+
attr_reader :host, :port
|
|
792
|
+
|
|
793
|
+
def serve(obj, accept = obj.public_methods)
|
|
794
|
+
@dispatcher = ObjectDispatcher.new(obj, accept)
|
|
795
|
+
@lsock = Rev::TCPServer.new(host, port, Cluster::Socket, method(:lazy_bind))
|
|
796
|
+
@loop.attach(@lsock)
|
|
797
|
+
self
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
def close
|
|
801
|
+
if @lsock
|
|
802
|
+
@lsock.detach if @lsock.attached?
|
|
803
|
+
@lsock.close
|
|
804
|
+
end
|
|
805
|
+
super
|
|
497
806
|
end
|
|
498
807
|
|
|
499
808
|
include LoopUtil
|
|
809
|
+
|
|
810
|
+
protected
|
|
811
|
+
def create_session(target_addr)
|
|
812
|
+
Session.new(@initmsg, target_addr, @dispatcher, @loop)
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
private
|
|
816
|
+
def lazy_bind(sock, msg)
|
|
817
|
+
if msg[0] == INIT
|
|
818
|
+
# cluster
|
|
819
|
+
target_addr = Address.load(msg[1])
|
|
820
|
+
s = Session.new(@initmsg, target_addr, @dispatcher, @loop)
|
|
821
|
+
sock.bind_session(s)
|
|
822
|
+
@spool[target_addr] = s
|
|
823
|
+
else
|
|
824
|
+
# subsys
|
|
825
|
+
s = Session.new(nil, nil, @dispatcher, @loop)
|
|
826
|
+
sock.bind_session(s)
|
|
827
|
+
sock.on_message(msg)
|
|
828
|
+
end
|
|
829
|
+
end
|
|
500
830
|
end
|
|
501
831
|
|
|
502
832
|
|
data/test/msgpack_rpc_test.rb
CHANGED
|
@@ -211,6 +211,24 @@ class MessagePackRPCTest < Test::Unit::TestCase
|
|
|
211
211
|
end
|
|
212
212
|
|
|
213
213
|
|
|
214
|
+
def test_pool
|
|
215
|
+
svr, cli = start_server
|
|
216
|
+
|
|
217
|
+
sp = MessagePack::RPC::SessionPool.new
|
|
218
|
+
s = sp.get_session('127.0.0.1', cli.port)
|
|
219
|
+
|
|
220
|
+
result = s.call(:hello)
|
|
221
|
+
assert_equal(result, "ok")
|
|
222
|
+
|
|
223
|
+
result = s.call(:sum, 1, 2)
|
|
224
|
+
assert_equal(result, 3)
|
|
225
|
+
|
|
226
|
+
sp.close
|
|
227
|
+
cli.close
|
|
228
|
+
svr.stop
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
|
|
214
232
|
def test_loop
|
|
215
233
|
port = next_port
|
|
216
234
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: msgpack-rpc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- FURUHASHI Sadayuki
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-
|
|
12
|
+
date: 2009-12-05 00:00:00 +09:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
@@ -48,11 +48,12 @@ files:
|
|
|
48
48
|
- Rakefile
|
|
49
49
|
- test/msgpack_rpc_test.rb
|
|
50
50
|
- test/test_helper.rb
|
|
51
|
-
- lib/msgpack
|
|
52
51
|
- lib/msgpack/rpc.rb
|
|
53
52
|
- AUTHORS
|
|
54
|
-
has_rdoc:
|
|
53
|
+
has_rdoc: true
|
|
55
54
|
homepage: http://msgpack.rubyforge.org
|
|
55
|
+
licenses: []
|
|
56
|
+
|
|
56
57
|
post_install_message:
|
|
57
58
|
rdoc_options:
|
|
58
59
|
- --title
|
|
@@ -84,9 +85,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
84
85
|
requirements: []
|
|
85
86
|
|
|
86
87
|
rubyforge_project: msgpack
|
|
87
|
-
rubygems_version: 1.3.
|
|
88
|
+
rubygems_version: 1.3.5
|
|
88
89
|
signing_key:
|
|
89
|
-
specification_version:
|
|
90
|
+
specification_version: 3
|
|
90
91
|
summary: RPC library using MessagePack, a ainary-based efficient data interchange format.
|
|
91
92
|
test_files:
|
|
92
93
|
- test/test_helper.rb
|