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