thrift-client 0.1.0 → 0.3.4
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.
- 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
|