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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1480c3fe436b9ab1ac5fd52d5d97216695684994
|
4
|
+
data.tar.gz: 5821642a275b1d38d9d611eef88d0afb1cf4a30a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e40434bf095d9fcafcda355cf2fc8ed49000b8877955e7e60e1e804c4a0be6e5be9bdc838e50c3a5c78df9cb858cb6e5da10a3d13be5fa3a615d51886baa6406
|
7
|
+
data.tar.gz: c3f9a470486255f5d1420a5deb4b5c7f2e48c1a54a23d3ccf0bdd5bc523c7ca782b2ac82985b047e19dad7e88f9bee7a4ff4c6392f95beb59b320602a3ea2cd0
|
data/lib/thrift_client.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'thrift'
|
2
|
-
require 'thrift_client/thrift'
|
3
|
-
require 'thrift_client/
|
4
|
-
require 'thrift_client/
|
5
|
-
require 'thrift_client/
|
2
|
+
require 'thrift_client/thrift/client'
|
3
|
+
require 'thrift_client/thrift/processor'
|
4
|
+
require 'thrift_client/thrift/protocol'
|
5
|
+
require 'thrift_client/thrift/struct'
|
6
|
+
require 'thrift_client/thrift/transport'
|
6
7
|
require 'thrift_client/abstract_thrift_client'
|
7
8
|
|
8
9
|
class ThriftClient < AbstractThriftClient
|
9
10
|
def initialize(options = {})
|
10
11
|
super
|
11
12
|
end
|
12
|
-
end
|
13
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require "thrift"
|
2
|
+
require "thrift_client/server"
|
3
|
+
|
2
4
|
=begin
|
3
5
|
abstract thrift client proxy, contains:
|
4
6
|
1.exception handle
|
@@ -6,6 +8,7 @@ require "thrift"
|
|
6
8
|
3.client create common logic encapsulation
|
7
9
|
=end
|
8
10
|
class NoAvailableServerError < RuntimeError; end
|
11
|
+
class ClientAlreadyDestroyedError < RuntimeError; end
|
9
12
|
class AbstractThriftClient
|
10
13
|
DISCONNECT_ERRORS = [
|
11
14
|
IOError,
|
@@ -30,10 +33,9 @@ class AbstractThriftClient
|
|
30
33
|
:size => 1,
|
31
34
|
:timeout => nil,
|
32
35
|
:client_class => nil,
|
33
|
-
:test_on_borrow => true,
|
34
|
-
:pool_timeout => 0,
|
35
36
|
:servers => "127.0.0.1:9090",
|
36
37
|
:multiplexed => false,
|
38
|
+
:retry => 0,
|
37
39
|
:before_method => nil,
|
38
40
|
:after_method => nil,
|
39
41
|
:on_exception => nil
|
@@ -41,31 +43,19 @@ class AbstractThriftClient
|
|
41
43
|
|
42
44
|
def initialize(options = {})
|
43
45
|
user_options = initialize_options(options)
|
44
|
-
|
46
|
+
|
45
47
|
servers = parse_servers(options["servers"])
|
46
48
|
|
47
49
|
#initialize options
|
48
50
|
@options = DEFAULTS.merge(user_options)
|
49
51
|
|
50
|
-
if(@options[:size] > 1)
|
51
|
-
@options[:server_class] = MultiClientServer
|
52
|
-
else
|
53
|
-
@options[:server_class] = SingleClientServer
|
54
|
-
end
|
55
|
-
|
56
52
|
#initialize servers info
|
57
53
|
@server_list = servers.collect do |s|
|
58
|
-
|
54
|
+
Server.new(s, @options)
|
59
55
|
end.sort_by { rand }
|
60
56
|
|
61
57
|
@callbacks = {}
|
62
|
-
|
63
|
-
# @callbacks[:after_method] = []
|
64
|
-
# @callbacks[:on_exception] = []
|
65
|
-
# @callbacks[:before_method].push(@options[:before_method])
|
66
|
-
# @callbacks[:after_method].push(@options[:after_method])
|
67
|
-
# @callbacks[:on_exception].push(DEFAULT_ON_EXCEPTION)
|
68
|
-
|
58
|
+
|
69
59
|
#initialize client methods
|
70
60
|
@client_methods = []
|
71
61
|
@options[:client_class].instance_methods.each do |method_name|
|
@@ -74,12 +64,14 @@ class AbstractThriftClient
|
|
74
64
|
@client_methods << $1
|
75
65
|
end
|
76
66
|
end
|
67
|
+
@retry = @options[:retry]
|
68
|
+
@state = 1
|
77
69
|
end
|
78
70
|
|
79
71
|
def add_callback(callback_type, &block)
|
80
72
|
case callback_type
|
81
73
|
when :before_method, :after_method, :on_exception
|
82
|
-
@callbacks[callback_type] ||= []
|
74
|
+
@callbacks[callback_type] ||= []
|
83
75
|
@callbacks[callback_type].push(block)
|
84
76
|
else
|
85
77
|
raise ArgumentError.new("Unknown callback type #{callback_type},only support before_method|after_method|on_exception")
|
@@ -87,43 +79,84 @@ class AbstractThriftClient
|
|
87
79
|
return self
|
88
80
|
end
|
89
81
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
82
|
+
#retry two times
|
83
|
+
def handle_method(method_name, *args)
|
84
|
+
check_state
|
85
|
+
server = next_server
|
86
|
+
do_callbacks(:before_method, method_name)
|
87
|
+
result = server.with do |client|
|
88
|
+
tries = 0
|
89
|
+
begin
|
90
|
+
ensure_connected client
|
91
|
+
client.send(method_name, *args)
|
92
|
+
rescue *@options[:application_exception_classes] => e
|
93
|
+
if(@callbacks[:on_exception] && @callbacks[:on_exception].length > 0)
|
94
|
+
do_callbacks(:on_exception, method_name, e)
|
95
|
+
else
|
96
|
+
raise e
|
97
|
+
end
|
98
|
+
rescue *@options[:disconnect_exception_classes] => e
|
99
|
+
# if e instance_of? Thrift::TransportException && e.type == Thrift::TransportException::TIMED_OUT
|
100
|
+
begin
|
101
|
+
if client.connected?
|
102
|
+
client.disconnect
|
103
|
+
end
|
104
|
+
rescue Exception => e
|
105
|
+
puts "disconnect error"
|
106
|
+
end
|
107
|
+
if tries < @retry # retry two times
|
108
|
+
tries += 1
|
109
|
+
retry
|
110
|
+
end
|
111
|
+
if(@callbacks[:on_exception] && @callbacks[:on_exception].length > 0)
|
112
|
+
do_callbacks(:on_exception, method_name, e)
|
113
|
+
else
|
114
|
+
raise e
|
115
|
+
end
|
105
116
|
end
|
106
|
-
|
117
|
+
end
|
118
|
+
do_callbacks(:after_method, method_name)
|
119
|
+
result
|
120
|
+
end
|
121
|
+
|
122
|
+
def destroy
|
123
|
+
@state = -1
|
124
|
+
@server_list.each { |server|
|
107
125
|
begin
|
108
|
-
server.
|
126
|
+
server.destroy if server
|
109
127
|
rescue Exception => e
|
128
|
+
puts "destroy server #{server} error, #{e.message}, #{e.backtrace}"
|
110
129
|
end
|
111
|
-
|
112
|
-
|
113
|
-
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
# 确保连接正常,在遇到异常时,重试一次
|
136
|
+
def ensure_connected(client)
|
137
|
+
try = true
|
138
|
+
begin
|
139
|
+
if !client.connected?
|
140
|
+
client.connect
|
141
|
+
end
|
142
|
+
client
|
143
|
+
rescue Exception => e
|
144
|
+
client.disconnect
|
145
|
+
if try
|
146
|
+
try = false
|
147
|
+
retry
|
114
148
|
else
|
115
|
-
raise
|
149
|
+
raise
|
116
150
|
end
|
117
|
-
ensure
|
118
|
-
server.return_client(client) if server
|
119
151
|
end
|
120
152
|
end
|
121
153
|
|
122
|
-
def
|
123
|
-
@
|
154
|
+
def check_state
|
155
|
+
if @state < 0
|
156
|
+
raise ClientAlreadyDestroyedError, "Client had already destroyed."
|
157
|
+
end
|
124
158
|
end
|
125
|
-
|
126
|
-
private
|
159
|
+
|
127
160
|
def next_server
|
128
161
|
@current_server_index ||= 0
|
129
162
|
@server_list.length.times do |i|
|
@@ -148,11 +181,18 @@ class AbstractThriftClient
|
|
148
181
|
|
149
182
|
def initialize_options(options)
|
150
183
|
user_options = {}
|
151
|
-
# puts options
|
152
|
-
|
153
184
|
if options["client_class"] != nil
|
154
|
-
|
155
|
-
if
|
185
|
+
client_class_str = options["client_class"].strip
|
186
|
+
if client_class_str.empty?
|
187
|
+
raise ArgumentError, "client class is empty string"
|
188
|
+
end
|
189
|
+
begin
|
190
|
+
client_class = eval(client_class_str)
|
191
|
+
flag = client_class.ancestors.include?(Thrift::Client)
|
192
|
+
rescue Exception => e
|
193
|
+
raise ArgumentError, "could not find class #{client_class_str}"
|
194
|
+
end
|
195
|
+
if flag
|
156
196
|
user_options.store(:client_class, client_class)
|
157
197
|
else
|
158
198
|
raise ArgumentError, "client class [#{client_class}] is not a subclass of Thrift::Client"
|
@@ -180,23 +220,21 @@ class AbstractThriftClient
|
|
180
220
|
when "socket"
|
181
221
|
transport_class = Thrift::Socket
|
182
222
|
when "unix_socket"
|
183
|
-
transport_class = Thrift::
|
223
|
+
transport_class = Thrift::UNIXSocket
|
224
|
+
when "eventmachine"
|
225
|
+
transport_class = Thrift::EventMachineTransport
|
226
|
+
when "em"
|
227
|
+
transport_class = Thrift::EventMachineTransport
|
184
228
|
else
|
185
|
-
raise ArgumentError, "Unknown transport type [#{options["transport"]}], use socket|unix_socket"
|
229
|
+
raise ArgumentError, "Unknown transport type [#{options["transport"]}], use socket|unix_socket|em(eventmachine)"
|
186
230
|
end
|
187
231
|
user_options.store(:transport, transport_class)
|
188
232
|
end
|
189
233
|
|
190
|
-
if options["
|
191
|
-
|
192
|
-
|
193
|
-
transport_wrapper_class = Thrift::FramedTransport
|
194
|
-
when "buffered"
|
195
|
-
transport_wrapper_class = Thrift::BufferedTransport
|
196
|
-
else
|
197
|
-
raise ArgumentError, "Unknown transport_wrapper type [#{options["transport_wrapper"]}], use framed|buffered"
|
234
|
+
if options["framed"] != nil
|
235
|
+
if options["framed"] != true
|
236
|
+
user_options.store(:transport_wrapper, Thrift::BufferedTransport)
|
198
237
|
end
|
199
|
-
user_options.store(:transport_wrapper, transport_wrapper_class)
|
200
238
|
end
|
201
239
|
|
202
240
|
if options["disconnect_exception_classes"] != nil
|
@@ -239,23 +277,22 @@ class AbstractThriftClient
|
|
239
277
|
user_options.store(:multiplexed, options["multiplexed"] || options["multiplexed"] == 'true')
|
240
278
|
end
|
241
279
|
|
242
|
-
if options["
|
243
|
-
user_options.store(:
|
244
|
-
end
|
245
|
-
|
246
|
-
if options["test_on_borrow"] != nil
|
247
|
-
user_options.store(:test_on_borrow, options["test_on_borrow"] || options["test_on_borrow"] == 'true')
|
280
|
+
if options["retry"] != nil
|
281
|
+
user_options.store(:retry, options["retry"])
|
248
282
|
end
|
249
|
-
|
250
|
-
return user_options
|
283
|
+
user_options
|
251
284
|
end
|
252
285
|
|
253
286
|
def parse_servers(servers_string)
|
254
287
|
if servers_string != nil
|
288
|
+
servers_string = servers_string.strip
|
289
|
+
if servers_string.empty?
|
290
|
+
raise ArgumentError, "servers info is empty string"
|
291
|
+
end
|
255
292
|
return servers_string.split(",")
|
256
293
|
else
|
257
294
|
raise ArgumentError, "servers info is nil"
|
258
295
|
end
|
259
296
|
end
|
260
297
|
|
261
|
-
end
|
298
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class ConnectionPool
|
2
|
+
|
3
|
+
def initialize(opts, &block)
|
4
|
+
@idle = [] #空闲的连接
|
5
|
+
@active = {} #活动的连接
|
6
|
+
@pending = [] #等待的线程或纤程id
|
7
|
+
opts[:size].times do
|
8
|
+
@idle.push(block.call) if block_given?
|
9
|
+
end
|
10
|
+
@size = opts[:size]
|
11
|
+
@state = true # mark this pool active
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns current pool utilization.
|
19
|
+
#
|
20
|
+
# @return [Hash] Current utilization.
|
21
|
+
def status
|
22
|
+
{
|
23
|
+
state: state,
|
24
|
+
idle: @idle.size,
|
25
|
+
active: @active.size,
|
26
|
+
pending: @pending.size
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def destroy
|
31
|
+
raise NotImplementedError
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "thrift_client/pool"
|
2
|
+
require "fiber"
|
3
|
+
|
4
|
+
class FiberConnectionPool < ConnectionPool
|
5
|
+
|
6
|
+
def initialize(opts, &block)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
# Choose first idle connection and pass it to the supplied
|
11
|
+
# block. This will block indefinitely until there is an idle
|
12
|
+
# connection to service the request.
|
13
|
+
def execute
|
14
|
+
f = Fiber.current
|
15
|
+
begin
|
16
|
+
conn = acquire(f)
|
17
|
+
yield conn
|
18
|
+
ensure
|
19
|
+
release(f)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Destroy all connections in this pool. It will waiting until
|
24
|
+
# all connections are idle and than close them one by one.
|
25
|
+
def destroy
|
26
|
+
while @idle.size < @size
|
27
|
+
fiber = Fiber.current
|
28
|
+
@pending.push fiber
|
29
|
+
Fiber.yield
|
30
|
+
end
|
31
|
+
@idle.each do | conn |
|
32
|
+
begin
|
33
|
+
conn.disconnect
|
34
|
+
rescue Exception => e
|
35
|
+
puts "close connection error! #{e.backtrace}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Acquire a lock on a connection and assign it to executing fiber
|
43
|
+
# - if connection is idle, pass it back to the calling block
|
44
|
+
# - if pool is full, yield the current fiber until connection is idle
|
45
|
+
def acquire(fiber)
|
46
|
+
if conn = @idle.pop
|
47
|
+
@active[fiber.object_id] = conn
|
48
|
+
conn
|
49
|
+
else
|
50
|
+
@pending.push fiber
|
51
|
+
Fiber.yield
|
52
|
+
acquire(fiber)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Release connection assigned to the supplied fiber and
|
57
|
+
# resume any other pending connections (which will
|
58
|
+
# immediately try to run acquire on the pool)
|
59
|
+
def release(fiber)
|
60
|
+
@idle.push(@active.delete(fiber.object_id))
|
61
|
+
|
62
|
+
if pending = @pending.shift
|
63
|
+
pending.resume
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "thrift_client/pool"
|
2
|
+
|
3
|
+
class ThreadConnectionPool < ConnectionPool
|
4
|
+
|
5
|
+
def initialize(opts, &block)
|
6
|
+
super
|
7
|
+
@mutex = Mutex.new
|
8
|
+
@condition = ConditionVariable.new
|
9
|
+
end
|
10
|
+
|
11
|
+
# Choose first idle connection and pass it to the supplied
|
12
|
+
# block. This will block indefinitely until there is an idle
|
13
|
+
# connection to service the request.
|
14
|
+
def execute
|
15
|
+
t = Thread.current
|
16
|
+
begin
|
17
|
+
conn = acquire(t)
|
18
|
+
yield conn
|
19
|
+
ensure
|
20
|
+
release(t)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Destroy all connections in this pool. It will waiting until
|
25
|
+
# all connections are idle and than close them one by one.
|
26
|
+
def destroy
|
27
|
+
while @idle.size < @size
|
28
|
+
@mutex.synchronize do
|
29
|
+
@condition.wait(@mutex)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
@idle.each do | conn |
|
33
|
+
begin
|
34
|
+
conn.disconnect
|
35
|
+
rescue Exception => e
|
36
|
+
puts "close connection error! #{e.backtrace}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Acquire a lock on a connection and assign it to executing thread
|
44
|
+
# - if connection is idle, pass it back to the calling block
|
45
|
+
# - if pool is full, stop the current thread until connection is idle
|
46
|
+
def acquire(thread)
|
47
|
+
@mutex.synchronize do
|
48
|
+
if conn = @idle.pop
|
49
|
+
@active[thread.object_id] = conn
|
50
|
+
return conn
|
51
|
+
else
|
52
|
+
@pending.push thread
|
53
|
+
@condition.wait(@mutex)
|
54
|
+
@pending.shift
|
55
|
+
end
|
56
|
+
end
|
57
|
+
acquire(thread)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Release connection assigned to the supplied thread and
|
61
|
+
# wakeup any other pending connections (which will
|
62
|
+
# immediately try to run acquire on the pool)
|
63
|
+
def release(thread)
|
64
|
+
@mutex.synchronize do
|
65
|
+
@idle.push(@active.delete(thread.object_id))
|
66
|
+
@condition.broadcast
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|