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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/thrift_client.rb +6 -5
  3. data/lib/thrift_client/abstract_thrift_client.rb +106 -69
  4. data/lib/thrift_client/pool.rb +34 -0
  5. data/lib/thrift_client/pool/fiber_connection_pool.rb +67 -0
  6. data/lib/thrift_client/pool/thread_connection_pool.rb +69 -0
  7. data/lib/thrift_client/{abstract_server.rb → server.rb} +62 -60
  8. data/lib/thrift_client/thrift/client.rb +15 -0
  9. data/lib/thrift_client/thrift/processor.rb +44 -0
  10. data/lib/thrift_client/{thrift.rb → thrift/protocol.rb} +1 -72
  11. data/lib/thrift_client/thrift/struct.rb +15 -0
  12. data/lib/thrift_client/thrift/transport.rb +208 -0
  13. data/test/em_test.rb +1 -0
  14. data/test/foobar/bar_service.rb +133 -0
  15. data/test/foobar/common_service.rb +388 -0
  16. data/test/foobar/foo_service.rb +133 -0
  17. data/test/{foobar_constants.rb → foobar/foobar_constants.rb} +3 -3
  18. data/test/{foobar_types.rb → foobar/foobar_types.rb} +12 -10
  19. data/test/foobar/handler.rb +74 -0
  20. data/test/helper.rb +50 -0
  21. data/test/passport.rb +51 -0
  22. data/test/start_server.rb +8 -0
  23. data/test/test.rb +27 -0
  24. data/test/test_client.rb +111 -0
  25. data/test/test_config.rb +54 -0
  26. data/test/test_connection_pool.rb +43 -0
  27. data/test/test_eventmachine_transprot.rb +146 -0
  28. data/test/test_multiplexed.rb +62 -0
  29. metadata +47 -24
  30. data/lib/thrift_client/multi_client_server.rb +0 -59
  31. data/lib/thrift_client/single_client_server.rb +0 -37
  32. data/test/client_test.rb +0 -172
  33. data/test/foobar_service.rb +0 -282
  34. data/test/multiplexed_protocol_test.rb +0 -60
  35. data/test/multiplexed_server.rb +0 -102
  36. data/test/server.rb +0 -97
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 866baefd0e4535af10cb51678e8612fde1bfba91
4
- data.tar.gz: 2fbdff18a5e1b97b7fa0e628d2eb9c245b3df426
3
+ metadata.gz: 1480c3fe436b9ab1ac5fd52d5d97216695684994
4
+ data.tar.gz: 5821642a275b1d38d9d611eef88d0afb1cf4a30a
5
5
  SHA512:
6
- metadata.gz: d3561f60edb1995911ead3c4f59179b972da8144c70cdb92bbab39e5fdb12a0bf0fc27b4387a15381470be98fe93df31fd10bc75224dba0567c64a70515b6819
7
- data.tar.gz: 3141bf30248e2a6519b1f1484b18bfc223e1449af9897cff034502482d5b1cc70e76c9a2ba175d5b0171ff6854c3b1edae6fc00fa9c56defaab6a290d2041dbc
6
+ metadata.gz: e40434bf095d9fcafcda355cf2fc8ed49000b8877955e7e60e1e804c4a0be6e5be9bdc838e50c3a5c78df9cb858cb6e5da10a3d13be5fa3a615d51886baa6406
7
+ data.tar.gz: c3f9a470486255f5d1420a5deb4b5c7f2e48c1a54a23d3ccf0bdd5bc523c7ca782b2ac82985b047e19dad7e88f9bee7a4ff4c6392f95beb59b320602a3ea2cd0
@@ -1,12 +1,13 @@
1
1
  require 'thrift'
2
- require 'thrift_client/thrift'
3
- require 'thrift_client/abstract_server'
4
- require 'thrift_client/single_client_server'
5
- require 'thrift_client/multi_client_server'
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
- @options[:server_class].new(s, @options)
54
+ Server.new(s, @options)
59
55
  end.sort_by { rand }
60
56
 
61
57
  @callbacks = {}
62
- # @callbacks[:before_method] = []
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
- def handle_method(method_name, *args)
91
- begin
92
- server = next_server
93
- client = server.get_client
94
- no_available_server unless client
95
- do_callbacks(:before_method, method_name)
96
- result = client.send(method_name, *args)
97
- do_callbacks(:after_method, method_name)
98
- return result
99
- rescue *@options[:application_exception_classes] => e
100
- #TODO whether or not wrapper these exceptions
101
- if(@callbacks[:on_exception] && @callbacks[:on_exception].length > 0)
102
- do_callbacks(:on_exception, method_name, e)
103
- else
104
- raise e
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
- rescue *@options[:disconnect_exception_classes] => e
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.disconnect(client) if server # tag the server is inactive
126
+ server.destroy if server
109
127
  rescue Exception => e
128
+ puts "destroy server #{server} error, #{e.message}, #{e.backtrace}"
110
129
  end
111
- #TODO whether or not wrapper these exceptions
112
- if(@callbacks[:on_exception] && @callbacks[:on_exception].length > 0)
113
- do_callbacks(:on_exception, method_name, e)
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 e
149
+ raise
116
150
  end
117
- ensure
118
- server.return_client(client) if server
119
151
  end
120
152
  end
121
153
 
122
- def destroy
123
- @server_list.each {|server| server.destroy if server}
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
- client_class = eval(options["client_class"])
155
- if client_class.ancestors.include?(Thrift::Client)
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::UNIXSockets
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["transport_wrapper"] != nil
191
- case options["transport_wrapper"]
192
- when "framed"
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["pool_timeout"] != nil
243
- user_options.store(:pool_timeout, options["pool_timeout"].to_i)
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
- # puts user_options[:test_on_borrow]
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