thrift-client 0.1.0 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/thrift_client.rb +6 -5
- data/lib/thrift_client/abstract_thrift_client.rb +106 -69
- data/lib/thrift_client/pool.rb +34 -0
- data/lib/thrift_client/pool/fiber_connection_pool.rb +67 -0
- data/lib/thrift_client/pool/thread_connection_pool.rb +69 -0
- data/lib/thrift_client/{abstract_server.rb → server.rb} +62 -60
- data/lib/thrift_client/thrift/client.rb +15 -0
- data/lib/thrift_client/thrift/processor.rb +44 -0
- data/lib/thrift_client/{thrift.rb → thrift/protocol.rb} +1 -72
- data/lib/thrift_client/thrift/struct.rb +15 -0
- data/lib/thrift_client/thrift/transport.rb +208 -0
- data/test/em_test.rb +1 -0
- data/test/foobar/bar_service.rb +133 -0
- data/test/foobar/common_service.rb +388 -0
- data/test/foobar/foo_service.rb +133 -0
- data/test/{foobar_constants.rb → foobar/foobar_constants.rb} +3 -3
- data/test/{foobar_types.rb → foobar/foobar_types.rb} +12 -10
- data/test/foobar/handler.rb +74 -0
- data/test/helper.rb +50 -0
- data/test/passport.rb +51 -0
- data/test/start_server.rb +8 -0
- data/test/test.rb +27 -0
- data/test/test_client.rb +111 -0
- data/test/test_config.rb +54 -0
- data/test/test_connection_pool.rb +43 -0
- data/test/test_eventmachine_transprot.rb +146 -0
- data/test/test_multiplexed.rb +62 -0
- metadata +47 -24
- data/lib/thrift_client/multi_client_server.rb +0 -59
- data/lib/thrift_client/single_client_server.rb +0 -37
- data/test/client_test.rb +0 -172
- data/test/foobar_service.rb +0 -282
- data/test/multiplexed_protocol_test.rb +0 -60
- data/test/multiplexed_server.rb +0 -102
- data/test/server.rb +0 -97
@@ -1,60 +1,62 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
=
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
if @options[:
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
end
|
1
|
+
class Server
|
2
|
+
|
3
|
+
def initialize(connect_string, options = {})
|
4
|
+
@active = false
|
5
|
+
@host, @port = parse_connect_string(connect_string)
|
6
|
+
@options = options
|
7
|
+
pool_class = nil
|
8
|
+
if @options[:transport] == Thrift::EventMachineTransport
|
9
|
+
require "thrift_client/pool/fiber_connection_pool"
|
10
|
+
pool_class = FiberConnectionPool
|
11
|
+
else
|
12
|
+
require "thrift_client/pool/thread_connection_pool"
|
13
|
+
pool_class = ThreadConnectionPool
|
14
|
+
end
|
15
|
+
@connection_pool = pool_class.new(:size => @options[:size]){
|
16
|
+
create_client
|
17
|
+
}
|
18
|
+
@active = true
|
19
|
+
end
|
20
|
+
|
21
|
+
def active?
|
22
|
+
@active
|
23
|
+
end
|
24
|
+
|
25
|
+
def destroy
|
26
|
+
@active = false
|
27
|
+
@connection_pool.destroy
|
28
|
+
end
|
29
|
+
|
30
|
+
def with
|
31
|
+
@connection_pool.execute { | connection |
|
32
|
+
if connection == nil
|
33
|
+
puts "fetal error, connection is nil"
|
34
|
+
end
|
35
|
+
yield connection
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"#{@host}:#{@port}"
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
def create_client
|
45
|
+
transport = @options[:transport].new(@host, @port, @options[:timeout])
|
46
|
+
transport = @options[:transport_wrapper].new(transport) if @options[:transport_wrapper]
|
47
|
+
protocol = @options[:protocol].new(transport)
|
48
|
+
if @options[:multiplexed]
|
49
|
+
protocol = Thrift::MultiplexedProtocol.new(protocol,@options[:client_class].name.split("::")[-2].downcase.gsub("service",""))
|
50
|
+
end
|
51
|
+
client = @options[:client_class].new(protocol)
|
52
|
+
# client.connect
|
53
|
+
client
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_connect_string(connect_string)
|
57
|
+
host, port = connect_string.split(":")
|
58
|
+
raise ArgumentError, 'Server must be in the format "host:port"' unless host and port
|
59
|
+
[host, port]
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Thrift
|
2
|
+
module Processor
|
3
|
+
def write_result(result, oprot, name, seqid)
|
4
|
+
if @service
|
5
|
+
oprot.write_message_begin(@service + ':' + name, MessageTypes::REPLY, seqid)
|
6
|
+
else
|
7
|
+
oprot.write_message_begin(name, MessageTypes::REPLY, seqid)
|
8
|
+
end
|
9
|
+
result.write(oprot)
|
10
|
+
oprot.write_message_end
|
11
|
+
oprot.trans.flush
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class MultiplexedProcessor
|
16
|
+
def initialize
|
17
|
+
@actual_processors = Hash.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def register(service_name, processor)
|
21
|
+
processor.instance_variable_set(:@service_name, service_name)
|
22
|
+
@actual_processors[service_name] = processor
|
23
|
+
end
|
24
|
+
|
25
|
+
def process(iprot, oprot)
|
26
|
+
name, type, seqid = iprot.read_message_begin
|
27
|
+
service_name, method = name.split(':')
|
28
|
+
actual_processor = @actual_processors[service_name]
|
29
|
+
if actual_processor.respond_to?("process_#{method}")
|
30
|
+
actual_processor.send("process_#{method}", seqid, iprot, oprot)
|
31
|
+
true
|
32
|
+
else
|
33
|
+
iprot.skip(Types::STRUCT)
|
34
|
+
iprot.read_message_end
|
35
|
+
x = ApplicationException.new(ApplicationException::UNKNOWN_METHOD, 'Unknown function ' + name)
|
36
|
+
oprot.write_message_begin(name, MessageTypes::EXCEPTION, seqid)
|
37
|
+
x.write(oprot)
|
38
|
+
oprot.write_message_end
|
39
|
+
oprot.trans.flush
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,75 +1,4 @@
|
|
1
1
|
module Thrift
|
2
|
-
module Client
|
3
|
-
def close
|
4
|
-
@iprot.trans.close
|
5
|
-
end
|
6
|
-
|
7
|
-
def open
|
8
|
-
@iprot.trans.open
|
9
|
-
end
|
10
|
-
|
11
|
-
def open?
|
12
|
-
@iprot.trans.open?
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
module Struct
|
17
|
-
def to_hash
|
18
|
-
hash = {}
|
19
|
-
each_field do |fid, field_info|
|
20
|
-
name = field_info[:name]
|
21
|
-
value = instance_variable_get("@#{name}")
|
22
|
-
unless field_info[:optional] && value.nil?
|
23
|
-
hash.store(name, value)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
return hash
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
class MultiplexedProcessor
|
31
|
-
def initialize
|
32
|
-
@actual_processors = Hash.new
|
33
|
-
end
|
34
|
-
|
35
|
-
def register(service_name, processor)
|
36
|
-
processor.instance_variable_set(:@service_name, service_name)
|
37
|
-
@actual_processors[service_name] = processor
|
38
|
-
end
|
39
|
-
|
40
|
-
def process(iprot, oprot)
|
41
|
-
name, type, seqid = iprot.read_message_begin
|
42
|
-
service_name, method = name.split(':')
|
43
|
-
actual_processor = @actual_processors[service_name]
|
44
|
-
if actual_processor.respond_to?("process_#{method}")
|
45
|
-
actual_processor.send("process_#{method}", seqid, iprot, oprot)
|
46
|
-
true
|
47
|
-
else
|
48
|
-
iprot.skip(Types::STRUCT)
|
49
|
-
iprot.read_message_end
|
50
|
-
x = ApplicationException.new(ApplicationException::UNKNOWN_METHOD, 'Unknown function ' + name)
|
51
|
-
oprot.write_message_begin(name, MessageTypes::EXCEPTION, seqid)
|
52
|
-
x.write(oprot)
|
53
|
-
oprot.write_message_end
|
54
|
-
oprot.trans.flush
|
55
|
-
false
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
module Processor
|
61
|
-
def write_result(result, oprot, name, seqid)
|
62
|
-
if @service
|
63
|
-
oprot.write_message_begin(@service + ':' + name, MessageTypes::REPLY, seqid)
|
64
|
-
else
|
65
|
-
oprot.write_message_begin(name, MessageTypes::REPLY, seqid)
|
66
|
-
end
|
67
|
-
result.write(oprot)
|
68
|
-
oprot.write_message_end
|
69
|
-
oprot.trans.flush
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
2
|
class MultiplexedProtocol < BaseProtocol
|
74
3
|
|
75
4
|
def initialize(protocol,service_name)
|
@@ -87,7 +16,7 @@ module Thrift
|
|
87
16
|
@protocol.write_message_begin(@service_name + ":" + name, type, seqid)
|
88
17
|
else
|
89
18
|
@protocol.write_message_begin(name, type, seqid)
|
90
|
-
end
|
19
|
+
end
|
91
20
|
end
|
92
21
|
|
93
22
|
def write_message_end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Thrift
|
2
|
+
module Struct
|
3
|
+
def to_hash
|
4
|
+
hash = {}
|
5
|
+
each_field do |fid, field_info|
|
6
|
+
name = field_info[:name]
|
7
|
+
value = instance_variable_get("@#{name}")
|
8
|
+
unless field_info[:optional] && value.nil?
|
9
|
+
hash.store(name, value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
return hash
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'fiber'
|
3
|
+
|
4
|
+
module Thrift
|
5
|
+
class TransportException
|
6
|
+
ALREADY_CLOSE = 5
|
7
|
+
end
|
8
|
+
class EventMachineTransport < BaseTransport
|
9
|
+
def initialize(host, port=9090, timeout=nil)
|
10
|
+
@host, @port, @timeout = host, port, timeout
|
11
|
+
@connection = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def open?
|
15
|
+
@connection && @connection.connected?
|
16
|
+
end
|
17
|
+
|
18
|
+
def open
|
19
|
+
@connection = EventMachine.connect(@host, @port, EventMachineConnection) do |conn|
|
20
|
+
conn.pending_connect_timeout = @timeout
|
21
|
+
end
|
22
|
+
|
23
|
+
fiber = Fiber.current
|
24
|
+
|
25
|
+
@connection.callback { |arg|
|
26
|
+
if fiber == Fiber.current
|
27
|
+
return arg
|
28
|
+
else
|
29
|
+
fiber.resume(arg)
|
30
|
+
end
|
31
|
+
}
|
32
|
+
@connection.errback { |arg|
|
33
|
+
if fiber == Fiber.current
|
34
|
+
return arg
|
35
|
+
else
|
36
|
+
fiber.resume(arg)
|
37
|
+
end
|
38
|
+
}
|
39
|
+
|
40
|
+
Fiber.yield
|
41
|
+
|
42
|
+
raise TransportException, TransportException::NOT_OPEN, "Unable to connect to #{@host}:#{@port}" unless @connection.connected?
|
43
|
+
@connection
|
44
|
+
end
|
45
|
+
|
46
|
+
def close
|
47
|
+
@connection.close if @connection && @connection.connected?
|
48
|
+
end
|
49
|
+
|
50
|
+
def read(sz)
|
51
|
+
raise IOError, "read failed, closed stream." unless open?
|
52
|
+
@connection.read(sz,@timeout)
|
53
|
+
end
|
54
|
+
|
55
|
+
def write(buf)
|
56
|
+
raise IOError, "write failed, closed stream." unless open?
|
57
|
+
@connection.write(buf,@timeout)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class EventMachineConnection < EventMachine::Connection
|
62
|
+
include EventMachine::Deferrable
|
63
|
+
BUFFER_SIZE = 4096 # 4kB
|
64
|
+
|
65
|
+
def post_init
|
66
|
+
@connected = false
|
67
|
+
@deferrable = nil
|
68
|
+
@rbuf = ''
|
69
|
+
@index = 0
|
70
|
+
end
|
71
|
+
|
72
|
+
def connected?
|
73
|
+
@connected
|
74
|
+
end
|
75
|
+
|
76
|
+
def connection_completed
|
77
|
+
@connected = true
|
78
|
+
succeed
|
79
|
+
end
|
80
|
+
|
81
|
+
def unbind
|
82
|
+
connected = @connected
|
83
|
+
@connected = false
|
84
|
+
deferrable = @deferrable
|
85
|
+
if connected
|
86
|
+
if deferrable
|
87
|
+
@deferrable = nil
|
88
|
+
deferrable.fail(:unbind)
|
89
|
+
else
|
90
|
+
puts "connection closed by server, but no deferrable was found."
|
91
|
+
fail
|
92
|
+
end
|
93
|
+
else
|
94
|
+
puts "connection closed"
|
95
|
+
fail
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def on_read_timeout
|
100
|
+
trap do
|
101
|
+
deferrable = @deferrable
|
102
|
+
if deferrable
|
103
|
+
@deferrable = nil
|
104
|
+
deferrable.fail(:read)
|
105
|
+
else
|
106
|
+
puts "read timeout, but no deferrable was found."
|
107
|
+
fail
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def close
|
113
|
+
trap do
|
114
|
+
if @connected
|
115
|
+
@connected = false
|
116
|
+
close_connection(true)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def write(buf,timeout)
|
122
|
+
callback {
|
123
|
+
send_data(buf)
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
def read(size,timeout)
|
128
|
+
timer = nil
|
129
|
+
if timeout and timeout > 0
|
130
|
+
timer = EventMachine.add_timer(timeout){
|
131
|
+
on_read_timeout
|
132
|
+
}
|
133
|
+
end
|
134
|
+
begin
|
135
|
+
data = nil
|
136
|
+
if can_read?(size)
|
137
|
+
data = yank(size)
|
138
|
+
else
|
139
|
+
@size = size
|
140
|
+
@deferrable ||= EventMachine::DefaultDeferrable.new
|
141
|
+
data = EventMachine::Synchrony.sync @deferrable
|
142
|
+
if data == :unbind
|
143
|
+
raise TransportException.new(TransportException::ALREADY_CLOSE, "connection already closed.")
|
144
|
+
elsif data == :read
|
145
|
+
raise TransportException.new(TransportException::TIMED_OUT, "read timeout")
|
146
|
+
elsif data == :readerror
|
147
|
+
raise TransportException.new(TransportException::UNKNOWN, "read unknown error")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
ensure
|
151
|
+
if timer
|
152
|
+
EventMachine.cancel_timer(timer)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
data
|
156
|
+
end
|
157
|
+
|
158
|
+
def receive_data(data)
|
159
|
+
trap do
|
160
|
+
puts data.length
|
161
|
+
@rbuf << data
|
162
|
+
if @deferrable and can_read?(@size)
|
163
|
+
data = yank(@size)
|
164
|
+
@size = nil
|
165
|
+
deferrable = @deferrable
|
166
|
+
@deferrable = nil
|
167
|
+
deferrable.succeed(data)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
def can_read?(size)
|
175
|
+
@rbuf.size >= @index + size
|
176
|
+
end
|
177
|
+
|
178
|
+
def yank(len)
|
179
|
+
data = @rbuf.slice(@index, len)
|
180
|
+
@index += len
|
181
|
+
@index = @rbuf.size if @index > @rbuf.size
|
182
|
+
if @index >= BUFFER_SIZE
|
183
|
+
@rbuf = @rbuf.slice(@index..-1)
|
184
|
+
@index = 0
|
185
|
+
end
|
186
|
+
data
|
187
|
+
end
|
188
|
+
|
189
|
+
def trap
|
190
|
+
begin
|
191
|
+
yield
|
192
|
+
rescue Exception => e
|
193
|
+
puts e.message
|
194
|
+
puts e.backtrace.join("\n")
|
195
|
+
# deferrable = @deferrable
|
196
|
+
# if deferrable
|
197
|
+
# @deferrable = nil
|
198
|
+
# deferrable.fail(:readerror)
|
199
|
+
# else
|
200
|
+
# puts "read data error, but no deferrable was found."
|
201
|
+
# fail
|
202
|
+
# end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|