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