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
@@ -1,60 +1,62 @@
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
1
+ class Server
2
+
3
+ def initialize(connect_string, options = {})
4
+ @active = false
5
+ @host, @port = parse_connect_string(connect_string)
6
+ @options = options
7
+ pool_class = nil
8
+ if @options[:transport] == Thrift::EventMachineTransport
9
+ require "thrift_client/pool/fiber_connection_pool"
10
+ pool_class = FiberConnectionPool
11
+ else
12
+ require "thrift_client/pool/thread_connection_pool"
13
+ pool_class = ThreadConnectionPool
14
+ end
15
+ @connection_pool = pool_class.new(:size => @options[:size]){
16
+ create_client
17
+ }
18
+ @active = true
19
+ end
20
+
21
+ def active?
22
+ @active
23
+ end
24
+
25
+ def destroy
26
+ @active = false
27
+ @connection_pool.destroy
28
+ end
29
+
30
+ def with
31
+ @connection_pool.execute { | connection |
32
+ if connection == nil
33
+ puts "fetal error, connection is nil"
34
+ end
35
+ yield connection
36
+ }
37
+ end
38
+
39
+ def to_s
40
+ "#{@host}:#{@port}"
41
+ end
42
+
43
+ protected
44
+ def create_client
45
+ transport = @options[:transport].new(@host, @port, @options[:timeout])
46
+ transport = @options[:transport_wrapper].new(transport) if @options[:transport_wrapper]
47
+ protocol = @options[:protocol].new(transport)
48
+ if @options[:multiplexed]
49
+ protocol = Thrift::MultiplexedProtocol.new(protocol,@options[:client_class].name.split("::")[-2].downcase.gsub("service",""))
50
+ end
51
+ client = @options[:client_class].new(protocol)
52
+ # client.connect
53
+ client
54
+ end
55
+
56
+ def parse_connect_string(connect_string)
57
+ host, port = connect_string.split(":")
58
+ raise ArgumentError, 'Server must be in the format "host:port"' unless host and port
59
+ [host, port]
60
+ end
61
+
62
+ end
@@ -0,0 +1,15 @@
1
+ module Thrift
2
+ module Client
3
+ def disconnect
4
+ @iprot.trans.close
5
+ end
6
+
7
+ def connect
8
+ @iprot.trans.open
9
+ end
10
+
11
+ def connected?
12
+ @iprot.trans.open?
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,44 @@
1
+ module Thrift
2
+ module Processor
3
+ def write_result(result, oprot, name, seqid)
4
+ if @service
5
+ oprot.write_message_begin(@service + ':' + name, MessageTypes::REPLY, seqid)
6
+ else
7
+ oprot.write_message_begin(name, MessageTypes::REPLY, seqid)
8
+ end
9
+ result.write(oprot)
10
+ oprot.write_message_end
11
+ oprot.trans.flush
12
+ end
13
+ end
14
+
15
+ class MultiplexedProcessor
16
+ def initialize
17
+ @actual_processors = Hash.new
18
+ end
19
+
20
+ def register(service_name, processor)
21
+ processor.instance_variable_set(:@service_name, service_name)
22
+ @actual_processors[service_name] = processor
23
+ end
24
+
25
+ def process(iprot, oprot)
26
+ name, type, seqid = iprot.read_message_begin
27
+ service_name, method = name.split(':')
28
+ actual_processor = @actual_processors[service_name]
29
+ if actual_processor.respond_to?("process_#{method}")
30
+ actual_processor.send("process_#{method}", seqid, iprot, oprot)
31
+ true
32
+ else
33
+ iprot.skip(Types::STRUCT)
34
+ iprot.read_message_end
35
+ x = ApplicationException.new(ApplicationException::UNKNOWN_METHOD, 'Unknown function ' + name)
36
+ oprot.write_message_begin(name, MessageTypes::EXCEPTION, seqid)
37
+ x.write(oprot)
38
+ oprot.write_message_end
39
+ oprot.trans.flush
40
+ false
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,75 +1,4 @@
1
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
- module Struct
17
- def to_hash
18
- hash = {}
19
- each_field do |fid, field_info|
20
- name = field_info[:name]
21
- value = instance_variable_get("@#{name}")
22
- unless field_info[:optional] && value.nil?
23
- hash.store(name, value)
24
- end
25
- end
26
- return hash
27
- end
28
- end
29
-
30
- class MultiplexedProcessor
31
- def initialize
32
- @actual_processors = Hash.new
33
- end
34
-
35
- def register(service_name, processor)
36
- processor.instance_variable_set(:@service_name, service_name)
37
- @actual_processors[service_name] = processor
38
- end
39
-
40
- def process(iprot, oprot)
41
- name, type, seqid = iprot.read_message_begin
42
- service_name, method = name.split(':')
43
- actual_processor = @actual_processors[service_name]
44
- if actual_processor.respond_to?("process_#{method}")
45
- actual_processor.send("process_#{method}", seqid, iprot, oprot)
46
- true
47
- else
48
- iprot.skip(Types::STRUCT)
49
- iprot.read_message_end
50
- x = ApplicationException.new(ApplicationException::UNKNOWN_METHOD, 'Unknown function ' + name)
51
- oprot.write_message_begin(name, MessageTypes::EXCEPTION, seqid)
52
- x.write(oprot)
53
- oprot.write_message_end
54
- oprot.trans.flush
55
- false
56
- end
57
- end
58
- end
59
-
60
- module Processor
61
- def write_result(result, oprot, name, seqid)
62
- if @service
63
- oprot.write_message_begin(@service + ':' + name, MessageTypes::REPLY, seqid)
64
- else
65
- oprot.write_message_begin(name, MessageTypes::REPLY, seqid)
66
- end
67
- result.write(oprot)
68
- oprot.write_message_end
69
- oprot.trans.flush
70
- end
71
- end
72
-
73
2
  class MultiplexedProtocol < BaseProtocol
74
3
 
75
4
  def initialize(protocol,service_name)
@@ -87,7 +16,7 @@ module Thrift
87
16
  @protocol.write_message_begin(@service_name + ":" + name, type, seqid)
88
17
  else
89
18
  @protocol.write_message_begin(name, type, seqid)
90
- end
19
+ end
91
20
  end
92
21
 
93
22
  def write_message_end
@@ -0,0 +1,15 @@
1
+ module Thrift
2
+ module Struct
3
+ def to_hash
4
+ hash = {}
5
+ each_field do |fid, field_info|
6
+ name = field_info[:name]
7
+ value = instance_variable_get("@#{name}")
8
+ unless field_info[:optional] && value.nil?
9
+ hash.store(name, value)
10
+ end
11
+ end
12
+ return hash
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,208 @@
1
+ require 'eventmachine'
2
+ require 'fiber'
3
+
4
+ module Thrift
5
+ class TransportException
6
+ ALREADY_CLOSE = 5
7
+ end
8
+ class EventMachineTransport < BaseTransport
9
+ def initialize(host, port=9090, timeout=nil)
10
+ @host, @port, @timeout = host, port, timeout
11
+ @connection = nil
12
+ end
13
+
14
+ def open?
15
+ @connection && @connection.connected?
16
+ end
17
+
18
+ def open
19
+ @connection = EventMachine.connect(@host, @port, EventMachineConnection) do |conn|
20
+ conn.pending_connect_timeout = @timeout
21
+ end
22
+
23
+ fiber = Fiber.current
24
+
25
+ @connection.callback { |arg|
26
+ if fiber == Fiber.current
27
+ return arg
28
+ else
29
+ fiber.resume(arg)
30
+ end
31
+ }
32
+ @connection.errback { |arg|
33
+ if fiber == Fiber.current
34
+ return arg
35
+ else
36
+ fiber.resume(arg)
37
+ end
38
+ }
39
+
40
+ Fiber.yield
41
+
42
+ raise TransportException, TransportException::NOT_OPEN, "Unable to connect to #{@host}:#{@port}" unless @connection.connected?
43
+ @connection
44
+ end
45
+
46
+ def close
47
+ @connection.close if @connection && @connection.connected?
48
+ end
49
+
50
+ def read(sz)
51
+ raise IOError, "read failed, closed stream." unless open?
52
+ @connection.read(sz,@timeout)
53
+ end
54
+
55
+ def write(buf)
56
+ raise IOError, "write failed, closed stream." unless open?
57
+ @connection.write(buf,@timeout)
58
+ end
59
+ end
60
+
61
+ class EventMachineConnection < EventMachine::Connection
62
+ include EventMachine::Deferrable
63
+ BUFFER_SIZE = 4096 # 4kB
64
+
65
+ def post_init
66
+ @connected = false
67
+ @deferrable = nil
68
+ @rbuf = ''
69
+ @index = 0
70
+ end
71
+
72
+ def connected?
73
+ @connected
74
+ end
75
+
76
+ def connection_completed
77
+ @connected = true
78
+ succeed
79
+ end
80
+
81
+ def unbind
82
+ connected = @connected
83
+ @connected = false
84
+ deferrable = @deferrable
85
+ if connected
86
+ if deferrable
87
+ @deferrable = nil
88
+ deferrable.fail(:unbind)
89
+ else
90
+ puts "connection closed by server, but no deferrable was found."
91
+ fail
92
+ end
93
+ else
94
+ puts "connection closed"
95
+ fail
96
+ end
97
+ end
98
+
99
+ def on_read_timeout
100
+ trap do
101
+ deferrable = @deferrable
102
+ if deferrable
103
+ @deferrable = nil
104
+ deferrable.fail(:read)
105
+ else
106
+ puts "read timeout, but no deferrable was found."
107
+ fail
108
+ end
109
+ end
110
+ end
111
+
112
+ def close
113
+ trap do
114
+ if @connected
115
+ @connected = false
116
+ close_connection(true)
117
+ end
118
+ end
119
+ end
120
+
121
+ def write(buf,timeout)
122
+ callback {
123
+ send_data(buf)
124
+ }
125
+ end
126
+
127
+ def read(size,timeout)
128
+ timer = nil
129
+ if timeout and timeout > 0
130
+ timer = EventMachine.add_timer(timeout){
131
+ on_read_timeout
132
+ }
133
+ end
134
+ begin
135
+ data = nil
136
+ if can_read?(size)
137
+ data = yank(size)
138
+ else
139
+ @size = size
140
+ @deferrable ||= EventMachine::DefaultDeferrable.new
141
+ data = EventMachine::Synchrony.sync @deferrable
142
+ if data == :unbind
143
+ raise TransportException.new(TransportException::ALREADY_CLOSE, "connection already closed.")
144
+ elsif data == :read
145
+ raise TransportException.new(TransportException::TIMED_OUT, "read timeout")
146
+ elsif data == :readerror
147
+ raise TransportException.new(TransportException::UNKNOWN, "read unknown error")
148
+ end
149
+ end
150
+ ensure
151
+ if timer
152
+ EventMachine.cancel_timer(timer)
153
+ end
154
+ end
155
+ data
156
+ end
157
+
158
+ def receive_data(data)
159
+ trap do
160
+ puts data.length
161
+ @rbuf << data
162
+ if @deferrable and can_read?(@size)
163
+ data = yank(@size)
164
+ @size = nil
165
+ deferrable = @deferrable
166
+ @deferrable = nil
167
+ deferrable.succeed(data)
168
+ end
169
+ end
170
+ end
171
+
172
+ private
173
+
174
+ def can_read?(size)
175
+ @rbuf.size >= @index + size
176
+ end
177
+
178
+ def yank(len)
179
+ data = @rbuf.slice(@index, len)
180
+ @index += len
181
+ @index = @rbuf.size if @index > @rbuf.size
182
+ if @index >= BUFFER_SIZE
183
+ @rbuf = @rbuf.slice(@index..-1)
184
+ @index = 0
185
+ end
186
+ data
187
+ end
188
+
189
+ def trap
190
+ begin
191
+ yield
192
+ rescue Exception => e
193
+ puts e.message
194
+ puts e.backtrace.join("\n")
195
+ # deferrable = @deferrable
196
+ # if deferrable
197
+ # @deferrable = nil
198
+ # deferrable.fail(:readerror)
199
+ # else
200
+ # puts "read data error, but no deferrable was found."
201
+ # fail
202
+ # end
203
+ end
204
+ end
205
+
206
+ end
207
+
208
+ end