thrift-client 0.0.7

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 02cbfa3837919e23cc7f2d62998025440cde2ab7
4
+ data.tar.gz: 7fad55e8761f1a9154abe35b005aaad88085ad11
5
+ SHA512:
6
+ metadata.gz: 433bc811fc642c33ffa21244d69e5ef2cf88aea4610efeb21f7b646d0743fbd4f040c1c2d961a1fe6d2345781a841ec036b2173246ac7a272a33e40f3bf6f0cc
7
+ data.tar.gz: cd261fa3ddeb856f19cddef85a2af7ff7f14d580326fdb6ba13769dc68037cf2de9cd544be15d4cf6d3d5db00907c32944909297db8e0b628b857e901c331bf2
@@ -0,0 +1,12 @@
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'
6
+ require 'thrift_client/abstract_thrift_client'
7
+
8
+ class ThriftClient < AbstractThriftClient
9
+ def initialize(options = {})
10
+ super
11
+ end
12
+ end
@@ -0,0 +1,60 @@
1
+ =begin
2
+ abstract thrift server information encapsulation
3
+ hold client connection
4
+ =end
5
+ class AbstractServer
6
+
7
+ attr_reader :host, :port, :client
8
+
9
+ def initialize(connect_string, options = {})
10
+ @host, @port = parse_connect_string(connect_string)
11
+ @options = options
12
+ @test_on_borrow = @options[:test_on_borrow]
13
+ @test_on_return = @options[:test_on_return]
14
+ end
15
+
16
+ def active?
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def destroy
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def disconnect(client)
25
+ client.close if client
26
+ end
27
+
28
+ def to_s
29
+ "#{host}:#{port}"
30
+ end
31
+
32
+ protected
33
+ def check_client(client)
34
+ begin
35
+ client.open unless client.open?
36
+ true
37
+ rescue Exception => e
38
+ false
39
+ end
40
+ end
41
+
42
+ def create_client
43
+ transport = @options[:transport].new(host, port, @options[:timeout])
44
+ transport = @options[:transport_wrapper].new(transport) if @options[:transport_wrapper]
45
+ protocol = @options[:protocol].new(transport)
46
+ if @options[:multiplexed]
47
+ protocol = Thrift::MultiplexedProtocol.new(protocol,@options[:client_class].name.split("::")[-2].downcase.gsub("service",""))
48
+ end
49
+ client = @options[:client_class].new(protocol)
50
+ client.open
51
+ return client
52
+ end
53
+
54
+ def parse_connect_string(connect_string)
55
+ host, port = connect_string.split(":")
56
+ raise ArgumentError, 'Server must be in the format "host:port"' unless host and port
57
+ [host, port]
58
+ end
59
+
60
+ end
@@ -0,0 +1,261 @@
1
+ require "thrift"
2
+ =begin
3
+ abstract thrift client proxy, contains:
4
+ 1.exception handle
5
+ 2.method aspect
6
+ 3.client create common logic encapsulation
7
+ =end
8
+ class NoAvailableServerError < RuntimeError; end
9
+ class AbstractThriftClient
10
+ DISCONNECT_ERRORS = [
11
+ IOError,
12
+ Thrift::ProtocolException,
13
+ Thrift::TransportException
14
+ ]
15
+
16
+ APPLICATION_ERRORS =[
17
+ Thrift::ApplicationException
18
+ ]
19
+
20
+ # DEFAULT_BEFORE_METHOD = Proc.new {|method_name| puts "prepare call #{method_name} method."}
21
+ # DEFAULT_AFTER_METHOD = Proc.new {|method_name| puts "called #{method_name} method."}
22
+ # DEFAULT_ON_EXCEPTION = Proc.new {|method_name, e| puts "call #{method_name} method ouccur exception."; raise e}
23
+
24
+ DEFAULTS = {
25
+ :protocol => Thrift::CompactProtocol,
26
+ :transport => Thrift::Socket,
27
+ :transport_wrapper => Thrift::FramedTransport,
28
+ :disconnect_exception_classes => DISCONNECT_ERRORS,
29
+ :application_exception_classes => APPLICATION_ERRORS,
30
+ :size => 1,
31
+ :timeout => nil,
32
+ :client_class => nil,
33
+ :test_on_borrow => true,
34
+ :pool_timeout => 0,
35
+ :servers => "127.0.0.1:9090",
36
+ :multiplexed => false,
37
+ :before_method => nil,
38
+ :after_method => nil,
39
+ :on_exception => nil
40
+ }
41
+
42
+ def initialize(options = {})
43
+ user_options = initialize_options(options)
44
+
45
+ servers = parse_servers(options["servers"])
46
+
47
+ #initialize options
48
+ @options = DEFAULTS.merge(user_options)
49
+
50
+ if(@options[:size] > 1)
51
+ @options[:server_class] = MultiClientServer
52
+ else
53
+ @options[:server_class] = SingleClientServer
54
+ end
55
+
56
+ #initialize servers info
57
+ @server_list = servers.collect do |s|
58
+ @options[:server_class].new(s, @options)
59
+ end.sort_by { rand }
60
+
61
+ @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
+
69
+ #initialize client methods
70
+ @client_methods = []
71
+ @options[:client_class].instance_methods.each do |method_name|
72
+ if method_name != 'send_message' && method_name =~ /^send_(.*)$/
73
+ instance_eval("def #{$1}(*args); handle_method(:'#{$1}', *args); end", __FILE__, __LINE__)
74
+ @client_methods << $1
75
+ end
76
+ end
77
+ end
78
+
79
+ def add_callback(callback_type, &block)
80
+ case callback_type
81
+ when :before_method, :after_method, :on_exception
82
+ @callbacks[callback_type] ||= []
83
+ @callbacks[callback_type].push(block)
84
+ else
85
+ raise ArgumentError.new("Unknown callback type #{callback_type},only support before_method|after_method|on_exception")
86
+ end
87
+ return self
88
+ end
89
+
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
105
+ end
106
+ rescue *@options[:disconnect_exception_classes] => e
107
+ begin
108
+ server.disconnect(client) if server # tag the server is inactive
109
+ rescue Exception => e
110
+ 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)
114
+ else
115
+ raise e
116
+ end
117
+ ensure
118
+ server.return_client(client) if server
119
+ end
120
+ end
121
+
122
+ def destroy
123
+ @server_list.each {|server| server.destroy if server}
124
+ end
125
+
126
+ private
127
+ def next_server
128
+ @current_server_index ||= 0
129
+ @server_list.length.times do |i|
130
+ index = (1 + @current_server_index + i) % @server_list.length
131
+ @current_server_index = index
132
+ return @server_list[index]
133
+ end
134
+ end
135
+
136
+ def no_available_server
137
+ servers = @server_list.map { |s| s.to_s }.join(',')
138
+ raise NoAvailableServerError, "No available servers in [#{servers}]."
139
+ end
140
+
141
+ # Calls all callbacks of the specified type with the given args
142
+ def do_callbacks(callback_type_sym, *args)
143
+ return unless @callbacks[callback_type_sym]
144
+ @callbacks[callback_type_sym].each do |callback|
145
+ callback.call(*args)
146
+ end
147
+ end
148
+
149
+ def initialize_options(options)
150
+ user_options = {}
151
+ # puts options
152
+
153
+ if options["client_class"] != nil
154
+ client_class = eval(options["client_class"])
155
+ if client_class.ancestors.include?(Thrift::Client)
156
+ user_options.store(:client_class, client_class)
157
+ else
158
+ raise ArgumentError, "client class [#{client_class}] is not a subclass of Thrift::Client"
159
+ end
160
+ else
161
+ raise ArgumentError, "client class is nil"
162
+ end
163
+
164
+ if options["protocol"] != nil
165
+ case options["protocol"]
166
+ when "compact"
167
+ protocol_class = Thrift::CompactProtocol
168
+ when "binary"
169
+ protocol_class = Thrift::BinaryProtocol
170
+ when "json"
171
+ protocol_class = Thrift::JsonProtocol
172
+ else
173
+ raise ArgumentError, "Unknown protocol type [#{options["protocol"]}], use compact|binary|json"
174
+ end
175
+ user_options.store(:protocol, protocol_class)
176
+ end
177
+
178
+ if options["transport"] != nil
179
+ case options["transport"]
180
+ when "socket"
181
+ transport_class = Thrift::Socket
182
+ when "unix_socket"
183
+ transport_class = Thrift::UNIXSockets
184
+ else
185
+ raise ArgumentError, "Unknown transport type [#{options["transport"]}], use socket|unix_socket"
186
+ end
187
+ user_options.store(:transport, transport_class)
188
+ end
189
+
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"
198
+ end
199
+ user_options.store(:transport_wrapper, transport_wrapper_class)
200
+ end
201
+
202
+ if options["disconnect_exception_classes"] != nil
203
+ user_define_disconnect_exceptions = options["disconnect_exception_classes"].split(",")
204
+ user_define_disconnect_exceptions.each do |exception|
205
+ exception_class = eval(exception)
206
+ if exception_class.ancestors.include?(Exception)
207
+ DISCONNECT_ERRORS.push(exception_class)
208
+ else
209
+ raise ArgumentError, "exception class [#{exception_class}] is not a subclass of Exception"
210
+ end
211
+ end
212
+ DISCONNECT_ERRORS.uniq!
213
+ end
214
+ user_options.store(:disconnect_exception_classes, DISCONNECT_ERRORS)
215
+
216
+ if options["application_exception_classes"] != nil
217
+ user_define_application_exceptions = options["application_exception_classes"].split(",")
218
+ user_define_application_exceptions.each do |exception|
219
+ exception_class = eval(exception)
220
+ if exception_class.ancestors.include?(Exception)
221
+ APPLICATION_ERRORS.push(exception_class)
222
+ else
223
+ raise ArgumentError, "exception class [#{exception_class}] is not a subclass of Exception"
224
+ end
225
+ end
226
+ APPLICATION_ERRORS.uniq!
227
+ end
228
+ user_options.store(:application_exception_classes, APPLICATION_ERRORS)
229
+
230
+ if options["size"] != nil
231
+ user_options.store(:size, options["size"].to_i)
232
+ end
233
+
234
+ if options["timeout"] != nil
235
+ user_options.store(:timeout, options["timeout"].to_i)
236
+ end
237
+
238
+ if options["multiplexed"] != nil
239
+ user_options.store(:multiplexed, options["multiplexed"] || options["multiplexed"] == 'true')
240
+ end
241
+
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')
248
+ end
249
+ # puts user_options[:test_on_borrow]
250
+ return user_options
251
+ end
252
+
253
+ def parse_servers(servers_string)
254
+ if servers_string != nil
255
+ return servers_string.split(",")
256
+ else
257
+ raise ArgumentError, "servers info is nil"
258
+ end
259
+ end
260
+
261
+ end
@@ -0,0 +1,59 @@
1
+ require 'thread'
2
+ require 'timeout'
3
+ =begin
4
+ multi client connection thrift server encapsulation
5
+ =end
6
+ class MultiClientServer < AbstractServer
7
+
8
+ def initialize(connect_string, options = {})
9
+ super
10
+ @pool_timeout = @options[:pool_timeout]
11
+ @pool = Array.new(@options[:size]) { client = create_client }
12
+ @mutex = Mutex.new
13
+ @resource = ConditionVariable.new
14
+ end
15
+
16
+ def active?
17
+ active = false
18
+ @pool.each {|client| active = client.open?; break if active}
19
+ return active
20
+ end
21
+
22
+ def destroy
23
+ @pool.each {|client| client.close if client}
24
+ end
25
+
26
+ def get_client
27
+ client = pop
28
+ if @test_on_borrow
29
+ if check_client(client)
30
+ return client
31
+ else
32
+ return nil
33
+ end
34
+ else
35
+ client
36
+ end
37
+ end
38
+
39
+ def return_client(client)
40
+ client = create_client unless client.open?
41
+ @mutex.synchronize do
42
+ @pool.push client
43
+ @resource.broadcast
44
+ end
45
+ end
46
+
47
+ private
48
+ def pop
49
+ deadline = Time.now + @pool_timeout
50
+ @mutex.synchronize do
51
+ loop do
52
+ return @pool.pop unless @pool.empty?
53
+ to_wait = deadline - Time.now
54
+ raise Timeout::Error, "Waited #{@pool_timeout} seconds" if to_wait <= 0 and @pool_timeout > 0
55
+ @resource.wait(@mutex, to_wait)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,37 @@
1
+ =begin
2
+ single client connection thrift server encapsulation
3
+ =end
4
+ class SingleClientServer < AbstractServer
5
+
6
+ def initialize(connect_string, options = {})
7
+ super
8
+ @client = create_client
9
+ end
10
+
11
+ def active?
12
+ @client.open?
13
+ end
14
+
15
+ def destroy
16
+ @client.close if @client
17
+ @client = nil
18
+ end
19
+
20
+ def get_client
21
+ @client = create_client unless @client
22
+ if @test_on_borrow
23
+ if check_client(@client)
24
+ return @client
25
+ else
26
+ return nil
27
+ end
28
+ else
29
+ @client
30
+ end
31
+ end
32
+
33
+ def return_client(client)
34
+ @client = nil unless @client.open?
35
+ end
36
+
37
+ end
@@ -0,0 +1,239 @@
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
+ class MultiplexedProcessor
17
+ def initialize
18
+ @actual_processors = Hash.new
19
+ end
20
+
21
+ def register(service_name, processor)
22
+ processor.instance_variable_set(:@service_name, service_name)
23
+ @actual_processors[service_name] = processor
24
+ end
25
+
26
+ def process(iprot, oprot)
27
+ name, type, seqid = iprot.read_message_begin
28
+ service_name, method = name.split(':')
29
+ actual_processor = @actual_processors[service_name]
30
+ if actual_processor.respond_to?("process_#{method}")
31
+ actual_processor.send("process_#{method}", seqid, iprot, oprot)
32
+ true
33
+ else
34
+ iprot.skip(Types::STRUCT)
35
+ iprot.read_message_end
36
+ x = ApplicationException.new(ApplicationException::UNKNOWN_METHOD, 'Unknown function ' + name)
37
+ oprot.write_message_begin(name, MessageTypes::EXCEPTION, seqid)
38
+ x.write(oprot)
39
+ oprot.write_message_end
40
+ oprot.trans.flush
41
+ false
42
+ end
43
+ end
44
+ end
45
+
46
+ module Processor
47
+ def write_result(result, oprot, name, seqid)
48
+ if @service
49
+ oprot.write_message_begin(@service + ':' + name, MessageTypes::REPLY, seqid)
50
+ else
51
+ oprot.write_message_begin(name, MessageTypes::REPLY, seqid)
52
+ end
53
+ result.write(oprot)
54
+ oprot.write_message_end
55
+ oprot.trans.flush
56
+ end
57
+ end
58
+
59
+ class MultiplexedProtocol < BaseProtocol
60
+
61
+ def initialize(protocol,service_name)
62
+ @protocol = protocol
63
+ @service_name = service_name
64
+ end
65
+
66
+ def trans
67
+ @protocol.trans
68
+ end
69
+
70
+ def write_message_begin(name, type, seqid)
71
+ case type
72
+ when ::Thrift::MessageTypes.CALL, ::Thrift::MessageTypes.ONEWAY
73
+ @protocol.write_message_begin(@service_name + ":" + name, type, seqid)
74
+ else
75
+ @protocol.write_message_begin(name, type, seqid)
76
+ end
77
+ end
78
+
79
+ def write_message_end
80
+ @protocol.write_message_end
81
+ end
82
+
83
+ def write_struct_begin(name)
84
+ @protocol.write_struct_begin(name)
85
+ end
86
+
87
+ def write_struct_end
88
+ @protocol.write_struct_end
89
+ end
90
+
91
+ def write_field_begin(name, type, id)
92
+ @protocol.write_field_begin(name, type, id)
93
+ end
94
+
95
+ def write_field_end
96
+ @protocol.write_field_end
97
+ end
98
+
99
+ def write_field_stop
100
+ @protocol.write_field_stop
101
+ end
102
+
103
+ def write_map_begin(ktype, vtype, size)
104
+ @protocol.write_map_begin(ktype, vtype, size)
105
+ end
106
+
107
+ def write_map_end
108
+ @protocol.write_map_end
109
+ end
110
+
111
+ def write_list_begin(etype, size)
112
+ @protocol.write_list_begin(etype, size)
113
+ end
114
+
115
+ def write_list_end
116
+ @protocol.write_list_end
117
+ end
118
+
119
+ def write_set_begin(etype, size)
120
+ @protocol.write_set_begin(etype, size)
121
+ end
122
+
123
+ def write_set_end
124
+ @protocol.write_set_end
125
+ end
126
+
127
+ def write_bool(bool)
128
+ @protocol.write_bool(bool)
129
+ end
130
+
131
+ def write_byte(byte)
132
+ @protocol.write_byte(byte)
133
+ end
134
+
135
+ def write_i16(i16)
136
+ @protocol.write_i16(i16)
137
+ end
138
+
139
+ def write_i32(i32)
140
+ @protocol.write_i32(i32)
141
+ end
142
+
143
+ def write_i64(i64)
144
+ @protocol.write_i64(i64)
145
+ end
146
+
147
+ def write_double(dub)
148
+ @protocol.write_double(dub)
149
+ end
150
+
151
+ def write_string(str)
152
+ @protocol.write_string(str)
153
+ end
154
+
155
+ def write_binary(buf)
156
+ @protocol.write_binary(buf)
157
+ end
158
+
159
+ def read_message_begin
160
+ @protocol.read_message_begin
161
+ end
162
+
163
+ def read_message_end
164
+ @protocol.read_message_end
165
+ end
166
+
167
+ def read_struct_begin
168
+ @protocol.read_struct_begin
169
+ end
170
+
171
+ def read_struct_end
172
+ @protocol.read_struct_end
173
+ end
174
+
175
+ def read_field_begin
176
+ @protocol.read_field_begin
177
+ end
178
+
179
+ def read_field_end
180
+ @protocol.read_field_end
181
+ end
182
+
183
+ def read_map_begin
184
+ @protocol.read_map_begin
185
+ end
186
+
187
+ def read_map_end
188
+ @protocol.read_map_end
189
+ end
190
+
191
+ def read_list_begin
192
+ @protocol.read_list_begin
193
+ end
194
+
195
+ def read_list_end
196
+ @protocol.read_list_end
197
+ end
198
+
199
+ def read_set_begin
200
+ @protocol.read_set_begin
201
+ end
202
+
203
+ def read_set_end
204
+ @protocol.read_set_end
205
+ end
206
+
207
+ def read_bool
208
+ @protocol.read_bool
209
+ end
210
+
211
+ def read_byte
212
+ @protocol.read_byte
213
+ end
214
+
215
+ def read_i16
216
+ @protocol.read_i16
217
+ end
218
+
219
+ def read_i32
220
+ @protocol.read_i32
221
+ end
222
+
223
+ def read_i64
224
+ @protocol.read_i64
225
+ end
226
+
227
+ def read_double
228
+ @protocol.read_double
229
+ end
230
+
231
+ def read_string
232
+ @protocol.read_string
233
+ end
234
+
235
+ def read_binary
236
+ @protocol.read_binary
237
+ end
238
+ end
239
+ end