redpack 1.0.1 → 1.0.2
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/rblib/redpack.rb +35 -0
- data/rblib/redpack/base.rb +45 -0
- data/rblib/redpack/client.rb +144 -0
- data/rblib/redpack/dispatcher.rb +106 -0
- data/rblib/redpack/exception.rb +50 -0
- data/rblib/redpack/future.rb +101 -0
- data/rblib/redpack/message.rb +12 -0
- data/rblib/redpack/server.rb +88 -0
- data/rblib/redpack/transport.rb +196 -0
- metadata +12 -3
data/rblib/redpack.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#
|
2
|
+
# RedPack-RPC for Ruby modified from MessagePack-RPC
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
# Copyright (C) 2010 Lux Delux Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'rblib'))
|
21
|
+
|
22
|
+
module RedPack #:nodoc:
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'bson'
|
26
|
+
require 'redis'
|
27
|
+
require 'em-redis'
|
28
|
+
require 'redpack/base'
|
29
|
+
require 'redpack/dispatcher'
|
30
|
+
require 'redpack/future'
|
31
|
+
require 'redpack/client'
|
32
|
+
require 'redpack/exception'
|
33
|
+
require 'redpack/message'
|
34
|
+
require 'redpack/server'
|
35
|
+
require 'redpack/transport'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# RedPack-RPC for Ruby modified from MessagePack-RPC
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
# Copyright (C) 2010 Lux Delux Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
module RedPack
|
20
|
+
|
21
|
+
module MessageReceiver
|
22
|
+
def on_message(msg, *ctx)
|
23
|
+
case msg[0]
|
24
|
+
when REQUEST
|
25
|
+
on_request(msg[1], msg[2], msg[3], *ctx)
|
26
|
+
when RESPONSE
|
27
|
+
on_response(msg[1], msg[2], msg[3], *ctx)
|
28
|
+
when NOTIFY
|
29
|
+
on_notify(msg[1], msg[2], *ctx)
|
30
|
+
else
|
31
|
+
raise RPCError.new("unknown message type #{msg[0]}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#def on_request(msgid, method, param)
|
36
|
+
#end
|
37
|
+
|
38
|
+
#def on_notify(method, param)
|
39
|
+
#end
|
40
|
+
|
41
|
+
#def on_response(msgid, error, result)
|
42
|
+
#end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
#
|
2
|
+
# RedPack-RPC for Ruby modified from MessagePack-RPC
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
# Copyright (C) 2010 Lux Delux Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
module RedPack
|
20
|
+
|
21
|
+
class Client
|
22
|
+
def initialize(name, redis_options = {:host => 'localhost'})
|
23
|
+
@timeout = 10
|
24
|
+
@seqid = 0
|
25
|
+
@reqtable = {}
|
26
|
+
@transport = RedisClientTransport.new(self, redis_options, name)
|
27
|
+
end
|
28
|
+
attr_accessor :timeout
|
29
|
+
|
30
|
+
# call-seq:
|
31
|
+
# notify(symbol, *args) -> nil
|
32
|
+
#
|
33
|
+
# Calls remote method with NOTIFY protocol.
|
34
|
+
# It doesn't require server to return results.
|
35
|
+
# This method is non-blocking and returns nil.
|
36
|
+
def notify(method, *args)
|
37
|
+
send_notify(method, args)
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# call code like this for sync calls:
|
42
|
+
# counter = client.increment_remote_counter_sync(1, 2, 3)
|
43
|
+
#
|
44
|
+
# call code like this for async non-blocking calls:
|
45
|
+
# client.increment_remote_counter_async(1, 2, 3) do |error, result|
|
46
|
+
# if error
|
47
|
+
# raise "we got an error"
|
48
|
+
# else
|
49
|
+
# counter = result
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
def method_missing(method, *args, &block)
|
53
|
+
method_name = method.to_s
|
54
|
+
sync = method_name.end_with?("_sync")
|
55
|
+
async = method_name.end_with?("_async")
|
56
|
+
if sync || async
|
57
|
+
method_name.gsub!(/_sync$/, '') if sync
|
58
|
+
method_name.gsub!(/_async$/, '') if async
|
59
|
+
future = send_request(method_name, args)
|
60
|
+
future.attach_callback(block) if block
|
61
|
+
if sync
|
62
|
+
@transport.listen_for_return_sync
|
63
|
+
future.get
|
64
|
+
if future.error
|
65
|
+
raise error
|
66
|
+
else
|
67
|
+
return future.result
|
68
|
+
end
|
69
|
+
elsif async
|
70
|
+
@transport.listen_for_return_async
|
71
|
+
return future
|
72
|
+
end
|
73
|
+
else
|
74
|
+
super
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# Closes underlaying Transport and destroy resources.
|
80
|
+
def close
|
81
|
+
@timer.detach if @timer.attached?
|
82
|
+
@reqtable = {}
|
83
|
+
@transport.close
|
84
|
+
@seqid = 0
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
# from ClientTransport
|
89
|
+
def on_response(sock, msgid, error, result) #:nodoc:
|
90
|
+
if future = @reqtable.delete(msgid)
|
91
|
+
future.set_result(error, result)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# from ClientTransport
|
96
|
+
def on_connect_failed #:nodoc:
|
97
|
+
@reqtable.reject! {|msgid, future|
|
98
|
+
begin
|
99
|
+
future.set_result ConnectError.new, nil
|
100
|
+
rescue
|
101
|
+
end
|
102
|
+
true
|
103
|
+
}
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
107
|
+
# from Client, SessionPool
|
108
|
+
def step_timeout #:nodoc:
|
109
|
+
timedout = []
|
110
|
+
@reqtable.reject! {|msgid, future|
|
111
|
+
if future.step_timeout
|
112
|
+
timedout.push(future)
|
113
|
+
true
|
114
|
+
end
|
115
|
+
}
|
116
|
+
timedout.each {|future|
|
117
|
+
begin
|
118
|
+
future.set_result TimeoutError.new, nil
|
119
|
+
rescue
|
120
|
+
end
|
121
|
+
}
|
122
|
+
!@reqtable.empty?
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
private
|
127
|
+
def send_request(method, param)
|
128
|
+
method = method.to_s
|
129
|
+
msgid = @seqid
|
130
|
+
@seqid += 1; if @seqid >= 1<<31 then @seqid = 0 end
|
131
|
+
data = [REQUEST, msgid, method, param]
|
132
|
+
@transport.send_data(data, msgid)
|
133
|
+
@reqtable[msgid] = Future.new(self)
|
134
|
+
end
|
135
|
+
|
136
|
+
def send_notify(method, param)
|
137
|
+
method = method.to_s
|
138
|
+
data = [NOTIFY, method, param]
|
139
|
+
@transport.send_data(data, nil)
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
#
|
2
|
+
# RedPack-RPC for Ruby modified from MessagePack-RPC
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
# Copyright (C) 2010 Lux Delux Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
module RedPack
|
20
|
+
|
21
|
+
|
22
|
+
class AsyncResult
|
23
|
+
def initialize
|
24
|
+
@responder = nil
|
25
|
+
@sent = false
|
26
|
+
end
|
27
|
+
|
28
|
+
def result(retval, err = nil)
|
29
|
+
unless @sent
|
30
|
+
if @responder
|
31
|
+
@responder.result(retval, err)
|
32
|
+
else
|
33
|
+
@result = [retval, err]
|
34
|
+
end
|
35
|
+
@sent = true
|
36
|
+
end
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def error(err)
|
41
|
+
result(nil, err)
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_responder(res) #:nodoc:
|
46
|
+
@responder = res
|
47
|
+
if @sent && @result
|
48
|
+
@responder.result(*@result)
|
49
|
+
@result = nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
class ObjectDispatcher
|
56
|
+
def initialize(obj, accept = obj.public_methods)
|
57
|
+
@obj = obj
|
58
|
+
@accept = accept.map {|m| m.is_a?(Integer) ? m : m.to_s }
|
59
|
+
end
|
60
|
+
|
61
|
+
def dispatch_request(session, method, param, responder)
|
62
|
+
begin
|
63
|
+
sent = false
|
64
|
+
early_result = nil
|
65
|
+
result = forward_method(session, method, param) do |result_|
|
66
|
+
unless result_.is_a?(AsyncResult)
|
67
|
+
responder.result(result_)
|
68
|
+
sent = true
|
69
|
+
end
|
70
|
+
early_result = result_
|
71
|
+
end
|
72
|
+
|
73
|
+
# FIXME on NoMethodError
|
74
|
+
# res.error(NO_METHOD_ERROR); return
|
75
|
+
|
76
|
+
# FIXME on ArgumentError
|
77
|
+
# res.error(ArgumentError); return
|
78
|
+
|
79
|
+
rescue
|
80
|
+
responder.error($!.to_s)
|
81
|
+
return
|
82
|
+
end
|
83
|
+
|
84
|
+
if early_result.is_a?(AsyncResult)
|
85
|
+
early_result.set_responder(responder)
|
86
|
+
elsif sent
|
87
|
+
return
|
88
|
+
elsif result.is_a?(AsyncResult)
|
89
|
+
result.set_responder(responder)
|
90
|
+
else
|
91
|
+
responder.result(result)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def dispatch_notify(session, method, param)
|
96
|
+
forward_method(session, method, param)
|
97
|
+
rescue
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def forward_method(session, method, param, &block)
|
102
|
+
@obj.send(method, *param, &block)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#
|
2
|
+
# RedPack-RPC for Ruby modified from MessagePack-RPC
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
# Copyright (C) 2010 Lux Delux Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
module RedPack
|
20
|
+
|
21
|
+
class Error < StandardError
|
22
|
+
end
|
23
|
+
|
24
|
+
class RPCError < Error
|
25
|
+
def initialize(msg)
|
26
|
+
super(msg)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class RemoteError < RPCError
|
31
|
+
def initialize(msg, result = nil)
|
32
|
+
super(msg)
|
33
|
+
@result = result
|
34
|
+
end
|
35
|
+
attr_reader :result
|
36
|
+
end
|
37
|
+
|
38
|
+
class TimeoutError < Error
|
39
|
+
def initialize(msg = "request timed out")
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class ConnectError < TimeoutError
|
45
|
+
def initialize(msg = "connect failed")
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#
|
2
|
+
# RedPack-RPC for Ruby modified from MessagePack-RPC
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
# Copyright (C) 2010 Lux Delux Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
module RedPack
|
20
|
+
|
21
|
+
|
22
|
+
# Future describes result of remote procedure call that is initially not known,
|
23
|
+
# because it is not yet received.
|
24
|
+
# You can wait and get the result with get method.
|
25
|
+
class Future
|
26
|
+
def initialize(session, callback = nil) #:nodoc:
|
27
|
+
@timeout = session.timeout
|
28
|
+
@callback_handler = callback
|
29
|
+
@error_handler = nil
|
30
|
+
@result_handler = nil
|
31
|
+
@set = false
|
32
|
+
@error = nil
|
33
|
+
@result = nil
|
34
|
+
end
|
35
|
+
attr_accessor :result, :error
|
36
|
+
|
37
|
+
# Wait for receiving result of remote procedure call and returns its result.
|
38
|
+
# If the remote method raises error, then this method raises RemoteError.
|
39
|
+
# If the remote procedure call failed with timeout, this method raises TimeoutError.
|
40
|
+
# Otherwise this method returns the result of remote method.
|
41
|
+
def get
|
42
|
+
join
|
43
|
+
if error.nil?
|
44
|
+
if @result_handler
|
45
|
+
return @result_handler.call(@result)
|
46
|
+
else
|
47
|
+
return @result
|
48
|
+
end
|
49
|
+
end
|
50
|
+
if @error_handler
|
51
|
+
return @error_handler.call(self)
|
52
|
+
else
|
53
|
+
raise @error if @error.is_a?(Error)
|
54
|
+
raise RemoteError.new(@error, @result)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Wait for receiving result of remote procedure call.
|
59
|
+
# This method returns self.
|
60
|
+
# If a callback method is attached, it will be called.
|
61
|
+
def join
|
62
|
+
until @set
|
63
|
+
sleep(0.0001)
|
64
|
+
end
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# call-seq:
|
69
|
+
# attach_callback {|future| }
|
70
|
+
#
|
71
|
+
# Attaches a callback method that is called when the result of remote method is received.
|
72
|
+
def attach_callback(proc = nil, &block)
|
73
|
+
@callback_handler = proc || block
|
74
|
+
end
|
75
|
+
|
76
|
+
# For IDL
|
77
|
+
def attach_error_handler(proc = nil, &block) #:nodoc:
|
78
|
+
@error_handler = proc || block
|
79
|
+
end
|
80
|
+
|
81
|
+
# For IDL
|
82
|
+
def attach_result_handler(proc = nil, &block) #:nodoc:
|
83
|
+
@result_handler = proc || block
|
84
|
+
end
|
85
|
+
|
86
|
+
def set_result(err, res) #:nodoc:
|
87
|
+
@error = err
|
88
|
+
@result = res
|
89
|
+
if @callback_handler
|
90
|
+
if @callback_handler.arity == 2
|
91
|
+
# FIXME backward compatibility
|
92
|
+
@callback_handler.call(error, result)
|
93
|
+
else
|
94
|
+
@callback_handler.call(self)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
@set = true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module RedPack
|
2
|
+
|
3
|
+
REQUEST = 0 # [0, msgid, method, param]
|
4
|
+
RESPONSE = 1 # [1, msgid, error, result]
|
5
|
+
NOTIFY = 2 # [2, method, param]
|
6
|
+
|
7
|
+
NO_METHOD_ERROR = 0x01;
|
8
|
+
ARGUMENT_ERROR = 0x02;
|
9
|
+
|
10
|
+
REDIS_DATA = 535 # == 'redis'.split('').inject(0) {|sum,x| sum+=x.ord} -- I know this is lame, but had to think of something!
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#
|
2
|
+
# RedPack-RPC for Ruby modified from MessagePack-RPC
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
# Copyright (C) 2010 Lux Delux Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
module RedPack
|
20
|
+
|
21
|
+
# Server is usable for RPC server.
|
22
|
+
class Server
|
23
|
+
def initialize(name, obj, redis_options = {:host => 'localhost'})
|
24
|
+
@dispatcher = nil
|
25
|
+
@obj = obj
|
26
|
+
@listener = RedisServerTransport.new(name, redis_options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def serve(obj, accept = obj.public_methods)
|
30
|
+
@dispatcher = ObjectDispatcher.new(obj, accept)
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
# 1. listen(listener, obj = nil, accept = obj.public_methods)
|
35
|
+
# 2. listen(host, port, obj = nil, accept = obj.public_methods)
|
36
|
+
def start()
|
37
|
+
unless @obj.nil?
|
38
|
+
serve(@obj, @obj.public_methods)
|
39
|
+
end
|
40
|
+
|
41
|
+
@listener.listen(self)
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def close
|
46
|
+
@listener.close
|
47
|
+
end
|
48
|
+
|
49
|
+
# from ServerTransport
|
50
|
+
def on_request(sendable, msgid, method, param) #:nodoc:
|
51
|
+
responder = Responder.new(sendable, msgid)
|
52
|
+
@dispatcher.dispatch_request(self, method, param, responder)
|
53
|
+
end
|
54
|
+
|
55
|
+
# from ServerTransport
|
56
|
+
def on_notify(method, param) #:nodoc:
|
57
|
+
@dispatcher.dispatch_notify(self, method, param)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
class Responder
|
63
|
+
def initialize(sendable, msgid)
|
64
|
+
@sendable = sendable # send_message method is required
|
65
|
+
@msgid = msgid
|
66
|
+
@sent = false
|
67
|
+
end
|
68
|
+
|
69
|
+
def sent?
|
70
|
+
@sent
|
71
|
+
end
|
72
|
+
|
73
|
+
def result(retval, err = nil)
|
74
|
+
unless @sent
|
75
|
+
data = [RESPONSE, @msgid, err, retval]
|
76
|
+
@sendable.send_data(data)
|
77
|
+
@sendable.finish(@msgid)
|
78
|
+
@sent = true
|
79
|
+
end
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def error(err, retval = nil)
|
84
|
+
result(retval, err)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
#
|
2
|
+
# RedPack-RPC for Ruby modified from MessagePack-RPC
|
3
|
+
#
|
4
|
+
# Copyright (C) 2010 FURUHASHI Sadayuki
|
5
|
+
# Copyright (C) 2010 Lux Delux Inc
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
module RedPack
|
20
|
+
|
21
|
+
module Util
|
22
|
+
# convert a binary sring into an array of bytes
|
23
|
+
def to_bytes(binary_string)
|
24
|
+
binary_string.unpack('c*')
|
25
|
+
end
|
26
|
+
|
27
|
+
# convert an array of bytes into a binary string
|
28
|
+
def to_binary_string(bytes)
|
29
|
+
bytes.pack('c*')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class RedisClientTransport
|
34
|
+
include Util
|
35
|
+
attr_reader :identifier
|
36
|
+
|
37
|
+
# Pass in {:ignore_return_value => true} as part of the redis_options so that the server doesn't put the return
|
38
|
+
# object in a queue. This is useful if you're doing call_async and never care about the return value. It saves
|
39
|
+
# Redis from adding a key/list pair. A synchronous call will result in an immediate timeout exception.
|
40
|
+
def initialize(client, redis_options, name)
|
41
|
+
@client = client
|
42
|
+
@queue_name = "redpack_request_queue:#{name}"
|
43
|
+
@ignore_return_value = !!redis_options.delete(:ignore_return_value)
|
44
|
+
if @ignore_return_value
|
45
|
+
@client.timeout = 0
|
46
|
+
else
|
47
|
+
@client.timeout = 15
|
48
|
+
end
|
49
|
+
|
50
|
+
@redis_options = redis_options
|
51
|
+
@redis = Redis.new(redis_options)
|
52
|
+
|
53
|
+
# assign a unique client key for this instance which will be used for return values
|
54
|
+
@identifier = @redis.incr("redpack_response_queue_index")
|
55
|
+
@return_queue_name = "redpack_response_queue:#{@identifier}"
|
56
|
+
@unprocessed_requests_name = "#{@return_queue_name}:unprocessed"
|
57
|
+
@pool = 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def process_data(data)
|
61
|
+
# puts "done waiting for #{@return_queue_name}"
|
62
|
+
if data && data[1]
|
63
|
+
begin
|
64
|
+
# redis_packet = MessagePack.unpack(data[1])
|
65
|
+
redis_packet = BSON.deserialize(data[1])
|
66
|
+
msg = redis_packet["data"]
|
67
|
+
if msg
|
68
|
+
if msg[0] == RESPONSE
|
69
|
+
on_response(msg[1], msg[2], msg[3])
|
70
|
+
else
|
71
|
+
puts "unknown message type #{msg[0]}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
rescue => e
|
75
|
+
puts e
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def listen_for_return_sync
|
81
|
+
process_data(@redis.blpop(@return_queue_name, 0))
|
82
|
+
end
|
83
|
+
|
84
|
+
def listen_for_return_async
|
85
|
+
Thread.new do
|
86
|
+
unless @ignore_return_value
|
87
|
+
EM.run do
|
88
|
+
redis = EventMachine::Protocols::Redis.connect(@redis_options)
|
89
|
+
# puts "waiting for #{@return_queue_name}"
|
90
|
+
redis.blpop(@return_queue_name, 0) do |data|
|
91
|
+
process_data(data)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def redis_push(msgpack_data, msgid = nil)
|
99
|
+
if msgid
|
100
|
+
@redis.multi
|
101
|
+
# puts "setting key in #{@unprocessed_requests_name}"
|
102
|
+
@redis.hset(@unprocessed_requests_name, msgid.to_s, msgpack_data)
|
103
|
+
# puts "pushing item into #{@queue_name}"
|
104
|
+
@redis.rpush(@queue_name, msgpack_data)
|
105
|
+
@redis.exec
|
106
|
+
else
|
107
|
+
@redis.rpush(@queue_name, msgpack_data)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def send_data(data, msgid = nil)
|
112
|
+
if @ignore_return_value
|
113
|
+
redis_push(BSON.serialize({:data => data}), msgid)
|
114
|
+
else
|
115
|
+
redis_push(BSON.serialize({:data => data, :return => @return_queue_name}), msgid)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def close
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
def on_connect(sock)
|
124
|
+
end
|
125
|
+
|
126
|
+
def on_response(msgid, error, result)
|
127
|
+
@client.on_response(self, msgid, error, result)
|
128
|
+
end
|
129
|
+
|
130
|
+
def on_connect_failed(sock)
|
131
|
+
end
|
132
|
+
|
133
|
+
def on_close(sock)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class RedisServerTransport
|
138
|
+
include Util
|
139
|
+
|
140
|
+
def initialize(name, redis_options = {})
|
141
|
+
@ignore_nil_returns = !!redis_options.delete(:ignore_nil_returns)
|
142
|
+
@redis = Redis.new(redis_options)
|
143
|
+
@queue_name = "redpack_request_queue:#{name}"
|
144
|
+
@timeout_mechanism = redis_options[:timeout_mechanism]
|
145
|
+
end
|
146
|
+
|
147
|
+
# ServerTransport interface
|
148
|
+
def listen(server)
|
149
|
+
@server = server
|
150
|
+
loop do
|
151
|
+
begin
|
152
|
+
# puts "listening to #{@queue_name}"
|
153
|
+
data = @redis.blpop(@queue_name, 0)
|
154
|
+
# puts "popped item off of #{@queue_name}"
|
155
|
+
if data && data[1]
|
156
|
+
redis_packet = BSON.deserialize(data[1])
|
157
|
+
if redis_packet["data"]
|
158
|
+
@return_queue_name = redis_packet["return"]
|
159
|
+
@unprocessed_requests_name = "#{@return_queue_name}:unprocessed"
|
160
|
+
msg = redis_packet["data"]
|
161
|
+
case msg[0]
|
162
|
+
when REQUEST
|
163
|
+
@server.on_request(self, msg[1], msg[2], msg[3])
|
164
|
+
when RESPONSE
|
165
|
+
puts "response message on server session"
|
166
|
+
when NOTIFY
|
167
|
+
@server.on_notify(msg[1], msg[2])
|
168
|
+
else
|
169
|
+
puts "unknown message type #{msg[0]}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
rescue => e
|
174
|
+
# probably timed out
|
175
|
+
p e
|
176
|
+
@timeout_mechanism.call if @timeout_mechanism
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def finish(msgid)
|
182
|
+
# puts "removing key from #{@unprocessed_requests_name}"
|
183
|
+
@redis.hdel(@unprocessed_requests_name, msgid.to_s)
|
184
|
+
end
|
185
|
+
|
186
|
+
def send_data(data)
|
187
|
+
# puts "putting data on #{@return_queue_name}"
|
188
|
+
@redis.rpush(@return_queue_name, BSON.serialize({:data => data})) if @return_queue_name
|
189
|
+
end
|
190
|
+
|
191
|
+
# ServerTransport interface
|
192
|
+
def close
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 1.0.
|
8
|
+
- 2
|
9
|
+
version: 1.0.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Dean Mao
|
@@ -56,6 +56,15 @@ extensions: []
|
|
56
56
|
extra_rdoc_files: []
|
57
57
|
|
58
58
|
files:
|
59
|
+
- rblib/redpack/base.rb
|
60
|
+
- rblib/redpack/client.rb
|
61
|
+
- rblib/redpack/dispatcher.rb
|
62
|
+
- rblib/redpack/exception.rb
|
63
|
+
- rblib/redpack/future.rb
|
64
|
+
- rblib/redpack/message.rb
|
65
|
+
- rblib/redpack/server.rb
|
66
|
+
- rblib/redpack/transport.rb
|
67
|
+
- rblib/redpack.rb
|
59
68
|
- test/msgpack_rpc_test.rb
|
60
69
|
- test/test_helper.rb
|
61
70
|
- AUTHORS
|
@@ -69,7 +78,7 @@ post_install_message:
|
|
69
78
|
rdoc_options: []
|
70
79
|
|
71
80
|
require_paths:
|
72
|
-
- rblib
|
81
|
+
- rblib
|
73
82
|
required_ruby_version: !ruby/object:Gem::Requirement
|
74
83
|
none: false
|
75
84
|
requirements:
|