msgpack-rpc 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/NOTICE +5 -1
- data/lib/msgpack/rpc.rb +144 -1430
- data/lib/msgpack/rpc/address.rb +144 -0
- data/lib/msgpack/rpc/client.rb +80 -0
- data/lib/msgpack/rpc/dispatcher.rb +111 -0
- data/lib/msgpack/rpc/exception.rb +53 -0
- data/lib/msgpack/rpc/future.rb +116 -0
- data/lib/msgpack/rpc/loop.rb +87 -0
- data/lib/msgpack/rpc/message.rb +31 -0
- data/lib/msgpack/rpc/server.rb +111 -0
- data/lib/msgpack/rpc/session.rb +157 -0
- data/lib/msgpack/rpc/session_pool.rb +91 -0
- data/lib/msgpack/rpc/transport/base.rb +48 -0
- data/lib/msgpack/rpc/transport/tcp.rb +242 -0
- data/lib/msgpack/rpc/transport/udp.rb +192 -0
- data/lib/msgpack/rpc/transport/unix.rb +165 -0
- data/test/test_helper.rb +2 -2
- metadata +17 -3
@@ -0,0 +1,87 @@
|
|
1
|
+
#
|
2
|
+
# MessagePack-RPC for Ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module MessagePack
|
19
|
+
module RPC
|
20
|
+
|
21
|
+
|
22
|
+
Loop = Rev::Loop
|
23
|
+
|
24
|
+
|
25
|
+
module LoopUtil
|
26
|
+
attr_reader :loop
|
27
|
+
|
28
|
+
class Timer < Rev::TimerWatcher
|
29
|
+
def initialize(interval, repeating, &block)
|
30
|
+
@block = block
|
31
|
+
super(interval, repeating)
|
32
|
+
end
|
33
|
+
def on_timer
|
34
|
+
@block.call
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def start_timer(interval, repeating, &block)
|
39
|
+
@loop.attach Timer.new(interval, repeating, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
class TaskQueue < Rev::AsyncWatcher
|
43
|
+
def initialize
|
44
|
+
@queue = []
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
def push(task)
|
49
|
+
@queue.push(task)
|
50
|
+
signal
|
51
|
+
end
|
52
|
+
|
53
|
+
def on_signal
|
54
|
+
while task = @queue.shift
|
55
|
+
begin
|
56
|
+
task.call
|
57
|
+
rescue
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def submit(task = nil, &block)
|
64
|
+
task ||= block
|
65
|
+
unless @queue
|
66
|
+
@queue = TaskQueue.new
|
67
|
+
@loop.attach(@queue)
|
68
|
+
end
|
69
|
+
@queue.push(task)
|
70
|
+
end
|
71
|
+
|
72
|
+
def run
|
73
|
+
@loop.run
|
74
|
+
end
|
75
|
+
|
76
|
+
def stop
|
77
|
+
@queue.detach if @queue && @queue.attached?
|
78
|
+
@loop.stop
|
79
|
+
# attach dummy timer
|
80
|
+
@loop.attach Rev::TimerWatcher.new(0, false)
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# MessagePack-RPC for Ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module MessagePack
|
19
|
+
module RPC
|
20
|
+
|
21
|
+
|
22
|
+
REQUEST = 0 # [0, msgid, method, param]
|
23
|
+
RESPONSE = 1 # [1, msgid, error, result]
|
24
|
+
NOTIFY = 2 # [2, method, param]
|
25
|
+
|
26
|
+
NO_METHOD_ERROR = 0x01;
|
27
|
+
ARGUMENT_ERROR = 0x02;
|
28
|
+
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
#
|
2
|
+
# MessagePack-RPC for Ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module MessagePack
|
19
|
+
module RPC
|
20
|
+
|
21
|
+
|
22
|
+
# Server is usable for RPC server.
|
23
|
+
# Note that Server is a SessionPool.
|
24
|
+
class Server < SessionPool
|
25
|
+
# 1. initialize(builder, loop = Loop.new)
|
26
|
+
# 2. initialize(loop = Loop.new)
|
27
|
+
def initialize(arg1=nil, arg2=nil)
|
28
|
+
super(arg1, arg2)
|
29
|
+
@dispatcher = nil
|
30
|
+
@listeners = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def serve(obj, accept = obj.public_methods)
|
34
|
+
@dispatcher = ObjectDispatcher.new(obj, accept)
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
# 1. listen(listener, obj = nil, accept = obj.public_methods)
|
39
|
+
# 2. listen(host, port, obj = nil, accept = obj.public_methods)
|
40
|
+
def listen(arg1, arg2 = nil, arg3 = nil, arg4 = nil)
|
41
|
+
if arg1.respond_to?(:listen)
|
42
|
+
# 1.
|
43
|
+
listener = arg1
|
44
|
+
obj = arg2
|
45
|
+
accept = arg3 || obj.public_methods
|
46
|
+
else
|
47
|
+
# 2.
|
48
|
+
listener = TCPServerTransport.new(Address.new(arg1,arg2))
|
49
|
+
obj = arg3
|
50
|
+
accept = arg4 || obj.public_methods
|
51
|
+
end
|
52
|
+
|
53
|
+
unless obj.nil?
|
54
|
+
serve(obj, accept)
|
55
|
+
end
|
56
|
+
|
57
|
+
listener.listen(self)
|
58
|
+
@listeners.push(listener)
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def close
|
63
|
+
@listeners.reject! {|listener|
|
64
|
+
listener.close
|
65
|
+
true
|
66
|
+
}
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
# from ServerTransport
|
71
|
+
def on_request(sendable, msgid, method, param) #:nodoc:
|
72
|
+
responder = Responder.new(sendable, msgid)
|
73
|
+
@dispatcher.dispatch_request(self, method, param, responder)
|
74
|
+
end
|
75
|
+
|
76
|
+
# from ServerTransport
|
77
|
+
def on_notify(method, param) #:nodoc:
|
78
|
+
@dispatcher.dispatch_notify(self, method, param)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
class Responder
|
84
|
+
def initialize(sendable, msgid)
|
85
|
+
@sendable = sendable # send_message method is required
|
86
|
+
@msgid = msgid
|
87
|
+
end
|
88
|
+
|
89
|
+
def result(retval, err = nil)
|
90
|
+
data = [RESPONSE, @msgid, err, retval].to_msgpack
|
91
|
+
@sendable.send_data(data)
|
92
|
+
end
|
93
|
+
|
94
|
+
def error(err, retval = nil)
|
95
|
+
result(retval, err)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
#:nodoc:
|
101
|
+
class Server::Base
|
102
|
+
def initialize(*args)
|
103
|
+
@base = Server.new(*args)
|
104
|
+
@base.serve(self)
|
105
|
+
end
|
106
|
+
attr_reader :base
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
#
|
2
|
+
# MessagePack-RPC for Ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module MessagePack
|
19
|
+
module RPC
|
20
|
+
|
21
|
+
|
22
|
+
# Session is a abstract class corresponds with a remote host.
|
23
|
+
# You can call remote method using call, call_async, callback or notify method.
|
24
|
+
class Session
|
25
|
+
def initialize(builder, address, loop)
|
26
|
+
@address = address
|
27
|
+
@loop = loop
|
28
|
+
@reqtable = {}
|
29
|
+
@timeout = 10 # FIXME default timeout time
|
30
|
+
@seqid = 0
|
31
|
+
@transport = builder.build_transport(self, address)
|
32
|
+
end
|
33
|
+
attr_reader :loop, :address
|
34
|
+
|
35
|
+
# Sets and gets timeout in seconds.
|
36
|
+
attr_accessor :timeout
|
37
|
+
|
38
|
+
# backward compatibility
|
39
|
+
def port #:nodoc:
|
40
|
+
@address.port;
|
41
|
+
end
|
42
|
+
|
43
|
+
# backward compatibility
|
44
|
+
def host #:nodoc:
|
45
|
+
@address.host;
|
46
|
+
end
|
47
|
+
|
48
|
+
# call-seq:
|
49
|
+
# call(symbol, *args) -> result of remote method
|
50
|
+
#
|
51
|
+
# Calls remote method.
|
52
|
+
# This method is same as call_async(method, *args).get
|
53
|
+
def call(method, *args)
|
54
|
+
call_async(method, *args).get
|
55
|
+
end
|
56
|
+
|
57
|
+
# call-seq:
|
58
|
+
# call_async(symbol, *args) -> Future
|
59
|
+
#
|
60
|
+
# Calls remote method asynchronously.
|
61
|
+
# This method is non-blocking and returns Future.
|
62
|
+
def call_async(method, *args)
|
63
|
+
future = send_request(method, args)
|
64
|
+
end
|
65
|
+
|
66
|
+
# backward compatibility
|
67
|
+
alias send call_async #:nodoc:
|
68
|
+
|
69
|
+
# call-seq:
|
70
|
+
# callback(symbol, *args) {|future| }
|
71
|
+
#
|
72
|
+
# Calls remote method asynchronously.
|
73
|
+
# The callback method is called with Future when the result is reached.
|
74
|
+
# This method is same as call_async(method, *args).attach_callback {|future| }
|
75
|
+
def callback(method, *args, &block)
|
76
|
+
future = send_request(method, args)
|
77
|
+
future.attach_callback(block)
|
78
|
+
future
|
79
|
+
end
|
80
|
+
|
81
|
+
# call-seq:
|
82
|
+
# notify(symbol, *args) -> nil
|
83
|
+
#
|
84
|
+
# Calls remote method with NOTIFY protocol.
|
85
|
+
# It doesn't require server to return results.
|
86
|
+
# This method is non-blocking and returns nil.
|
87
|
+
def notify(method, *args)
|
88
|
+
send_notify(method, args)
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
# Closes underlaying Transport and destroy resources.
|
93
|
+
def close
|
94
|
+
@transport.close
|
95
|
+
@reqtable = {}
|
96
|
+
@seqid = 0
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
# from ClientTransport
|
101
|
+
def on_response(sock, msgid, error, result) #:nodoc:
|
102
|
+
if future = @reqtable.delete(msgid)
|
103
|
+
future.set_result(error, result)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# from ClientTransport
|
108
|
+
def on_connect_failed #:nodoc:
|
109
|
+
@reqtable.reject! {|msgid, future|
|
110
|
+
begin
|
111
|
+
future.set_result ConnectError.new, nil
|
112
|
+
rescue
|
113
|
+
end
|
114
|
+
true
|
115
|
+
}
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
|
119
|
+
# from Client, SessionPool
|
120
|
+
def step_timeout #:nodoc:
|
121
|
+
timedout = []
|
122
|
+
@reqtable.reject! {|msgid, future|
|
123
|
+
if future.step_timeout
|
124
|
+
timedout.push(future)
|
125
|
+
true
|
126
|
+
end
|
127
|
+
}
|
128
|
+
timedout.each {|future|
|
129
|
+
begin
|
130
|
+
future.set_result TimeoutError.new, nil
|
131
|
+
rescue
|
132
|
+
end
|
133
|
+
}
|
134
|
+
!@reqtable.empty?
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
def send_request(method, param)
|
139
|
+
method = method.to_s
|
140
|
+
msgid = @seqid
|
141
|
+
@seqid += 1; if @seqid >= 1<<31 then @seqid = 0 end
|
142
|
+
data = [REQUEST, msgid, method, param].to_msgpack
|
143
|
+
@transport.send_data(data)
|
144
|
+
@reqtable[msgid] = Future.new(self, @loop)
|
145
|
+
end
|
146
|
+
|
147
|
+
def send_notify(method, param)
|
148
|
+
method = method.to_s
|
149
|
+
data = [NOTIFY, method, param].to_msgpack
|
150
|
+
@transport.send_data(data)
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
#
|
2
|
+
# MessagePack-RPC for Ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module MessagePack
|
19
|
+
module RPC
|
20
|
+
|
21
|
+
|
22
|
+
# SessionPool is usable for connection pooling.
|
23
|
+
# You can get pooled Session using get_session method.
|
24
|
+
# Note that SessionPool includes LoopUtil.
|
25
|
+
class SessionPool
|
26
|
+
# 1. initialize(builder, loop = Loop.new)
|
27
|
+
# 2. initialize(loop = Loop.new)
|
28
|
+
#
|
29
|
+
# Creates an SessionPool.
|
30
|
+
def initialize(arg1=nil, arg2=nil)
|
31
|
+
if arg1.respond_to?(:build_transport)
|
32
|
+
# 1.
|
33
|
+
builder = arg1
|
34
|
+
loop = arg2 || Loop.new
|
35
|
+
else
|
36
|
+
# 2.
|
37
|
+
builder = TCPTransport.new
|
38
|
+
loop = arg1 || Loop.new
|
39
|
+
end
|
40
|
+
|
41
|
+
@builder = builder
|
42
|
+
@loop = loop
|
43
|
+
@pool = {}
|
44
|
+
|
45
|
+
@timer = Timer.new(1, true, &method(:step_timeout))
|
46
|
+
loop.attach(@timer)
|
47
|
+
end
|
48
|
+
|
49
|
+
# 1. get_session(address)
|
50
|
+
# 2. get_session(host, port)
|
51
|
+
#
|
52
|
+
# Returns pooled Session.
|
53
|
+
# If there are no pooled Session for the specified address,
|
54
|
+
# this method creates the Session and pools it.
|
55
|
+
def get_session(arg1, arg2=nil)
|
56
|
+
if arg2.nil?
|
57
|
+
# 1.
|
58
|
+
addr = arg1
|
59
|
+
else
|
60
|
+
# 2.
|
61
|
+
host = arg1
|
62
|
+
port = arg2
|
63
|
+
addr = Address.new(host, port)
|
64
|
+
end
|
65
|
+
|
66
|
+
@pool[addr] ||= Session.new(@builder, addr, @loop)
|
67
|
+
end
|
68
|
+
|
69
|
+
# backward compatibility
|
70
|
+
alias get_session_addr get_session #:nodoc:
|
71
|
+
|
72
|
+
def close
|
73
|
+
@pool.reject! {|addr, s|
|
74
|
+
s.close
|
75
|
+
true
|
76
|
+
}
|
77
|
+
@timer.detach if @timer.attached?
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
|
81
|
+
include LoopUtil
|
82
|
+
|
83
|
+
private
|
84
|
+
def step_timeout
|
85
|
+
@pool.each_pair {|addr,s| s.step_timeout }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|