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
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
|