thrift-client 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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