protobuf 2.0.0.rc3 → 2.0.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/lib/protobuf/cli.rb +1 -2
  2. data/lib/protobuf/{common/exceptions.rb → exceptions.rb} +0 -0
  3. data/lib/protobuf/field/base_field.rb +1 -1
  4. data/lib/protobuf/{common/logger.rb → logger.rb} +21 -0
  5. data/lib/protobuf/message/decoder.rb +2 -2
  6. data/lib/protobuf/message/encoder.rb +6 -4
  7. data/lib/protobuf/rpc/buffer.rb +2 -2
  8. data/lib/protobuf/rpc/client.rb +18 -18
  9. data/lib/protobuf/rpc/connectors/base.rb +3 -8
  10. data/lib/protobuf/rpc/connectors/common.rb +29 -28
  11. data/lib/protobuf/rpc/connectors/em_client.rb +9 -9
  12. data/lib/protobuf/rpc/connectors/eventmachine.rb +11 -9
  13. data/lib/protobuf/rpc/connectors/socket.rb +13 -17
  14. data/lib/protobuf/rpc/connectors/zmq.rb +13 -17
  15. data/lib/protobuf/rpc/error.rb +3 -3
  16. data/lib/protobuf/rpc/server.rb +41 -93
  17. data/lib/protobuf/rpc/servers/evented/server.rb +7 -9
  18. data/lib/protobuf/rpc/servers/evented_runner.rb +0 -11
  19. data/lib/protobuf/rpc/servers/socket/server.rb +8 -7
  20. data/lib/protobuf/rpc/servers/socket/worker.rb +22 -15
  21. data/lib/protobuf/rpc/servers/zmq/server.rb +3 -3
  22. data/lib/protobuf/rpc/servers/zmq/util.rb +1 -1
  23. data/lib/protobuf/rpc/servers/zmq/worker.rb +6 -15
  24. data/lib/protobuf/rpc/service.rb +145 -228
  25. data/lib/protobuf/rpc/service_dispatcher.rb +114 -0
  26. data/lib/protobuf/rpc/stat.rb +46 -33
  27. data/lib/protobuf/version.rb +1 -1
  28. data/lib/protobuf/{common/wire_type.rb → wire_type.rb} +0 -0
  29. data/spec/benchmark/tasks.rb +18 -18
  30. data/spec/functional/evented_server_spec.rb +3 -4
  31. data/spec/functional/socket_server_spec.rb +3 -3
  32. data/spec/functional/zmq_server_spec.rb +3 -3
  33. data/spec/lib/protobuf/{common/logger_spec.rb → logger_spec.rb} +46 -36
  34. data/spec/lib/protobuf/rpc/client_spec.rb +10 -58
  35. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +1 -39
  36. data/spec/lib/protobuf/rpc/connectors/common_spec.rb +3 -6
  37. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +0 -12
  38. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +1 -6
  39. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +94 -0
  40. data/spec/lib/protobuf/rpc/service_spec.rb +132 -45
  41. data/spec/spec_helper.rb +4 -3
  42. data/spec/support/server.rb +8 -4
  43. metadata +41 -35
@@ -8,43 +8,39 @@ module Protobuf
8
8
  include Protobuf::Logger::LogMethods
9
9
 
10
10
  def send_request
11
- check_async
12
11
  setup_connection
13
12
  connect_to_rpc_server
14
13
  post_init
15
14
  read_response
16
15
  end
17
16
 
18
- private
19
-
20
- def check_async
21
- if async?
22
- log_error { "[client-#{self.class}] Cannot run in async mode" }
23
- raise "Cannot use Socket client in async mode"
24
- end
17
+ def log_signature
18
+ @_log_signature ||= "[client-#{self.class}]"
25
19
  end
26
20
 
21
+ private
22
+
27
23
  def close_connection
28
24
  @socket.close
29
- log_debug { "[client-#{self.class}] Connector closed" }
25
+ log_debug { sign_message('Connector closed') }
30
26
  end
31
27
 
32
28
  def connect_to_rpc_server
33
29
  @socket = TCPSocket.new(options[:host], options[:port])
34
- log_debug { "[client-#{self.class}] Connection established #{options[:host]}:#{options[:port]}" }
30
+ log_debug { sign_message("Connection established #{options[:host]}:#{options[:port]}") }
35
31
  end
36
32
 
37
33
  # Method to determine error state, must be used with Connector api
38
34
  def error?
39
- return true if(@error)
40
- log_debug { "[client-#{self.class}] Error state : #{@socket.closed?}" }
35
+ return true if @error
36
+ log_debug { sign_message("Error state : #{@socket.closed?}") }
41
37
  @socket.closed?
42
38
  end
43
39
 
44
40
  def read_data
45
41
  size_io = StringIO.new
46
42
 
47
- while((size_reader = @socket.getc) != "-")
43
+ until (size_reader = @socket.getc) == "-"
48
44
  size_io << size_reader
49
45
  end
50
46
  str_size_io = size_io.string
@@ -53,8 +49,8 @@ module Protobuf
53
49
  end
54
50
 
55
51
  def read_response
56
- log_debug { "[client-#{self.class}] error? is #{error?}" }
57
- return if(error?)
52
+ log_debug { sign_message("error? is #{error?}") }
53
+ return if error?
58
54
  response_buffer = ::Protobuf::Rpc::Buffer.new(:read)
59
55
  response_buffer << read_data
60
56
  @stats.response_size = response_buffer.size
@@ -63,12 +59,12 @@ module Protobuf
63
59
  end
64
60
 
65
61
  def send_data
66
- return if(error?)
62
+ return if error?
67
63
  request_buffer = ::Protobuf::Rpc::Buffer.new(:write)
68
64
  request_buffer.set_data(@request_data)
69
65
  @socket.write(request_buffer.write)
70
66
  @socket.flush
71
- log_debug { "[client-#{self.class}] write closed" }
67
+ log_debug { sign_message("write closed") }
72
68
  end
73
69
 
74
70
  end
@@ -8,7 +8,6 @@ module Protobuf
8
8
  include Protobuf::Logger::LogMethods
9
9
 
10
10
  def send_request
11
- check_async
12
11
  setup_connection
13
12
  connect_to_rpc_server
14
13
  post_init
@@ -19,29 +18,26 @@ module Protobuf
19
18
  @zmq_context = nil
20
19
  end
21
20
 
22
- private
23
-
24
- def check_async
25
- if async?
26
- log_error { "[client-#{self.class}] Cannot run in async mode" }
27
- raise "Cannot use Zmq client in async mode"
28
- end
21
+ def log_signature
22
+ @_log_signature ||= "[client-#{self.class}]"
29
23
  end
30
24
 
25
+ private
26
+
31
27
  def close_connection
32
- return if(@error)
28
+ return if @error
33
29
  zmq_error_check(@socket.close)
34
30
  zmq_error_check(@zmq_context.terminate)
35
- log_debug { "[client-#{self.class}] Connector closed" }
31
+ log_debug { sign_message("Connector closed") }
36
32
  end
37
33
 
38
34
  def connect_to_rpc_server
39
- return if(@error)
40
- log_debug { "[client-#{self.class} Establishing connection: #{options[:host]}:#{options[:port]}" }
35
+ return if @error
36
+ log_debug { sign_message("Establishing connection: #{options[:host]}:#{options[:port]}") }
41
37
  @zmq_context = ::ZMQ::Context.new
42
38
  @socket = @zmq_context.socket(::ZMQ::REQ)
43
39
  zmq_error_check(@socket.connect("tcp://#{options[:host]}:#{options[:port]}"))
44
- log_debug { "[client-#{self.class}] Connection established #{options[:host]}:#{options[:port]}" }
40
+ log_debug { sign_message("Connection established #{options[:host]}:#{options[:port]}") }
45
41
  end
46
42
 
47
43
  # Method to determine error state, must be used with Connector api
@@ -50,18 +46,18 @@ module Protobuf
50
46
  end
51
47
 
52
48
  def read_response
53
- return if(@error)
49
+ return if @error
54
50
  @response_data = ''
55
51
  zmq_error_check(@socket.recv_string(@response_data))
56
52
  parse_response
57
53
  end
58
54
 
59
55
  def send_data
60
- return if(@error)
61
- log_debug { "[#{log_signature}] Sending Request: %s" % @request_data }
56
+ return if @error
57
+ log_debug { sign_message("Sending Request: #{@request_data}") }
62
58
  @stats.request_size = @request_data.size
63
59
  zmq_error_check(@socket.send_string(@request_data))
64
- log_debug { "[client-#{self.class}] write closed" }
60
+ log_debug { sign_message("write closed") }
65
61
  end
66
62
 
67
63
  def zmq_error_check(return_code)
@@ -3,16 +3,16 @@ require 'protobuf/rpc/rpc.pb'
3
3
  module Protobuf
4
4
  module Rpc
5
5
  ClientError = Struct.new("ClientError", :code, :message)
6
-
6
+
7
7
  # Base PbError class for client and server errors
8
8
  class PbError < StandardError
9
9
  attr_reader :error_type
10
-
10
+
11
11
  def initialize message='An unknown RpcError occurred', error_type='RPC_ERROR'
12
12
  @error_type = error_type.is_a?(String) ? ::Protobuf::Socketrpc::ErrorReason.const_get(error_type) : error_type
13
13
  super message
14
14
  end
15
-
15
+
16
16
  def to_response response
17
17
  response.error = message
18
18
  response.error_reason = @error_type
@@ -1,140 +1,88 @@
1
- require 'protobuf/common/logger'
1
+ require 'protobuf/logger'
2
2
  require 'protobuf/rpc/rpc.pb'
3
3
  require 'protobuf/rpc/buffer'
4
4
  require 'protobuf/rpc/error'
5
5
  require 'protobuf/rpc/stat'
6
+ require 'protobuf/rpc/service_dispatcher'
6
7
 
7
8
  module Protobuf
8
9
  module Rpc
9
10
  module Server
10
11
 
12
+ def initialize_request!
13
+ log_debug { sign_message('Post init') }
14
+ @request = ::Protobuf::Socketrpc::Request.new
15
+ @response = ::Protobuf::Socketrpc::Response.new
16
+ @stats = Protobuf::Rpc::Stat.new(:SERVER)
17
+ set_peer
18
+ end
19
+
20
+ def disable_gc!
21
+ ::GC.disable if ::Protobuf.gc_pause_server_request?
22
+ end
23
+
24
+ def enable_gc!
25
+ ::GC.enable if ::Protobuf.gc_pause_server_request?
26
+ end
27
+
28
+ # no-op, implemented by including class if desired.
29
+ def set_peer; end
30
+
11
31
  # Invoke the service method dictated by the proto wrapper request object
12
32
  def handle_client
13
- # Parse the protobuf request from the socket
14
- log_debug { "[#{log_signature}] Parsing request from client" }
15
33
  parse_request_from_buffer
34
+ @dispatcher = ::Protobuf::Rpc::ServiceDispatcher.new(@request)
35
+ @stats.dispatcher = @dispatcher
16
36
 
17
- # Determine the service class and method name from the request
18
- log_debug { "[#{log_signature}] Extracting procedure call info from request" }
19
- parse_service_info
20
-
21
- # Call the service method
22
- log_debug { "[#{log_signature}] Dispatching client request to service" }
23
- ::GC.disable if ::Protobuf.gc_pause_server_request?
24
- invoke_rpc_method
37
+ disable_gc!
38
+ @dispatcher.invoke!
39
+ if @dispatcher.success?
40
+ @response.response_proto = @dispatcher.response
41
+ else
42
+ handle_error(@dispatcher.error)
43
+ end
25
44
  rescue => error
26
- # Ensure we're handling any errors that try to slip out the back door
27
- log_error(error.message)
28
- log_error(error.backtrace.join("\n"))
45
+ log_exception(error)
29
46
  handle_error(error)
47
+ ensure
30
48
  send_response
31
49
  end
32
50
 
33
51
  # Client error handler. Receives an exception object and writes it into the @response
34
52
  def handle_error(error)
35
- log_debug { "[#{log_signature}] handle_error: %s" % error.inspect }
53
+ log_debug { sign_message("handle_error: #{error.inspect}") }
36
54
  if error.respond_to?(:to_response)
37
55
  error.to_response(@response)
38
56
  else
39
57
  message = error.respond_to?(:message) ? error.message : error.to_s
40
- code = error.respond_to?(:code) ? error.code.to_s : "RPC_ERROR"
58
+ code = error.respond_to?(:code) ? error.code : 'RPC_ERROR'
41
59
  ::Protobuf::Rpc::PbError.new(message, code).to_response(@response)
42
60
  end
43
61
  end
44
62
 
45
- # Assuming all things check out, we can call the service method
46
- def invoke_rpc_method
47
- # Get a new instance of the service
48
- @service = @klass.new
49
-
50
- # Define our response callback to perform the "successful" response to our client
51
- # This decouples the service's rpc method from our response to the client,
52
- # allowing the service to be the dictator for when the response should be sent back.
53
- #
54
- # In other words, we don't send the response once the service method finishes executing
55
- # since the service may perform it's own operations asynchronously.
56
- @service.on_send_response do |response|
57
- unless @did_respond
58
- parse_response_from_service(response)
59
- send_response
60
- end
61
- end
62
-
63
- @service.on_rpc_failed do |error|
64
- unless @did_respond
65
- handle_error(error)
66
- send_response
67
- end
68
- end
69
-
70
- # Call the service method
71
- log_debug { "[#{log_signature}] Invoking %s#%s with request %s" % [@klass.name, @method, @request.inspect] }
72
- @service.__send__(@method, @request)
73
- end
74
-
75
63
  def log_signature
76
- @log_signature ||= "server-#{self.class}"
64
+ @_log_signature ||= "[server-#{self.class.name}]"
77
65
  end
78
66
 
79
67
  # Parse the incoming request object into our expected request object
80
68
  def parse_request_from_buffer
81
- log_debug { "[#{log_signature}] parsing request from buffer: %s" % @request_data }
69
+ log_debug { sign_message("Parsing request from buffer: #{@request_data}") }
82
70
  @request.parse_from_string(@request_data)
83
71
  rescue => error
84
- exc = ::Protobuf::Rpc::BadRequestData.new 'Unable to parse request: %s' % error.message
72
+ exc = ::Protobuf::Rpc::BadRequestData.new("Unable to parse request: #{error.message}")
85
73
  log_error { exc.message }
86
74
  raise exc
87
75
  end
88
76
 
89
- # Read out the response from the service method,
90
- # setting it on the pb request, and serializing the
91
- # response to the protobuf response wrapper
92
- def parse_response_from_service(response)
93
- expected = @klass.rpcs[@klass][@method].response_type
94
-
95
- # Cannibalize the response if it's a Hash
96
- response = expected.new(response) if response.is_a?(Hash)
97
- actual = response.class
98
- log_debug { "[#{log_signature}] response (should/actual): %s/%s" % [expected.name, actual.name] }
99
-
100
- # Determine if the service tried to change response types on us
101
- if expected != actual
102
- raise ::Protobuf::Rpc::BadResponseProto, 'Response proto changed from %s to %s' % [expected.name, actual.name]
103
- else
104
- @response.response_proto = response
105
- end
106
- rescue => error
107
- log_error error.message
108
- log_error error.backtrace.join("\n")
109
- handle_error(error)
110
- end
111
-
112
- # Parses and returns the service and method name from the request wrapper proto
113
- def parse_service_info
114
- @klass = @request.service_name.constantize
115
- @method = @request.method_name.underscore.to_sym
116
-
117
- unless @klass.instance_methods.include?(@method)
118
- raise MethodNotFound, "Service method #{@request.method_name} is not defined by the service"
119
- end
120
-
121
- @stats.service = @klass.name
122
- @stats.method = @method
123
- rescue NameError
124
- raise ServiceNotFound, "Service class #{@request.service_name} is not found"
125
- end
126
-
127
77
  # Write the response wrapper to the client
128
78
  def send_response
129
- raise 'Response already sent to client' if @did_respond
130
- log_debug { "[#{log_signature}] Sending response to client: %s" % @response.inspect }
79
+ log_debug { sign_message("Sending response to client: #{@response.inspect}") }
131
80
  send_data
132
- @stats.end
133
- @stats.log_stats
134
- @did_respond = true
81
+ @stats.stop && log_info { @stats.to_s }
135
82
  ensure
136
- ::GC.enable if ::Protobuf.gc_pause_server_request?
83
+ enable_gc!
137
84
  end
138
85
  end
139
86
  end
140
87
  end
88
+
@@ -9,19 +9,13 @@ module Protobuf
9
9
 
10
10
  # Initialize a new read buffer for storing client request info
11
11
  def post_init
12
- log_debug { '[server] Post init, new read buffer created' }
13
- @stats = Protobuf::Rpc::Stat.new(:SERVER, true)
14
- @stats.client = ::Socket.unpack_sockaddr_in(get_peername)
12
+ initialize_request!
15
13
  @request_buffer = Protobuf::Rpc::Buffer.new(:read)
16
- @request = ::Protobuf::Socketrpc::Request.new
17
- @response = ::Protobuf::Socketrpc::Response.new
18
-
19
- @did_respond = false
20
14
  end
21
15
 
22
16
  # Receive a chunk of data, potentially flushed to handle_client
23
17
  def receive_data(data)
24
- log_debug { '[server] receive_data: %s' % data }
18
+ log_debug { sign_message("receive_data: #{data}") }
25
19
 
26
20
  @request_buffer << data
27
21
  @request_data = @request_buffer.data
@@ -34,9 +28,13 @@ module Protobuf
34
28
  response_buffer = Protobuf::Rpc::Buffer.new(:write)
35
29
  response_buffer.set_data(@response)
36
30
  @stats.response_size = response_buffer.size
37
- log_debug { "[#{log_signature}] sending data: #{response_buffer.inspect}" }
31
+ log_debug { sign_message("sending data: #{response_buffer.inspect}") }
38
32
  super(response_buffer.write)
39
33
  end
34
+
35
+ def set_peer
36
+ @stats.client = ::Socket.unpack_sockaddr_in(get_peername)
37
+ end
40
38
  end
41
39
  end
42
40
  end
@@ -7,17 +7,6 @@ module Protobuf
7
7
  end
8
8
 
9
9
  def self.run(server)
10
- # Ensure errors thrown within EM are caught and logged appropriately
11
- #EventMachine.error_handler do |error|
12
- # raise error
13
- # if error.message == 'no acceptor'
14
- # raise 'Failed binding to %s:%d (%s)' % [server.host, server.port, error.message]
15
- # else
16
- # Protobuf::Logger.error(error.message)
17
- # Protobuf::Logger.error(error.backtrace.join("\n"))
18
- # end
19
- #end
20
-
21
10
  # Startup and run the rpc server
22
11
  ::EventMachine.schedule do
23
12
  ::EventMachine.start_server(server.host, server.port, ::Protobuf::Rpc::Evented::Server)
@@ -1,5 +1,6 @@
1
1
  require 'protobuf/rpc/server'
2
2
  require 'protobuf/rpc/servers/socket/worker'
3
+
3
4
  module Protobuf
4
5
  module Rpc
5
6
  module Socket
@@ -14,7 +15,7 @@ module Protobuf
14
15
  end
15
16
 
16
17
  def self.cleanup_threads
17
- log_debug { "[#{log_signature}] Thread cleanup - #{@threads.size} - start" }
18
+ log_debug { sign_message("Thread cleanup - #{@threads.size} - start") }
18
19
 
19
20
  @threads = @threads.select do |t|
20
21
  if t[:thread].alive?
@@ -26,11 +27,11 @@ module Protobuf
26
27
  end
27
28
  end
28
29
 
29
- log_debug { "[#{log_signature}] Thread cleanup - #{@threads.size} - complete" }
30
+ log_debug { sign_message("Thread cleanup - #{@threads.size} - complete") }
30
31
  end
31
32
 
32
33
  def self.log_signature
33
- @log_signature ||= "server-#{self}"
34
+ @_log_signature ||= "server-#{self.class.name}"
34
35
  end
35
36
 
36
37
  def self.new_worker(socket)
@@ -42,7 +43,7 @@ module Protobuf
42
43
  end
43
44
 
44
45
  def self.run(opts = {})
45
- log_debug { "[#{log_signature}] Run" }
46
+ log_debug { sign_message("Run") }
46
47
  host = opts.fetch(:host, "127.0.0.1")
47
48
  port = opts.fetch(:port, 9399)
48
49
  backlog = opts.fetch(:backlog, 100)
@@ -59,7 +60,7 @@ module Protobuf
59
60
  @running = true
60
61
 
61
62
  while running?
62
- log_debug { "[#{log_signature}] Waiting for connections" }
63
+ log_debug { sign_message("Waiting for connections") }
63
64
 
64
65
  if ready_cnxns = IO.select(@listen_fds, [], [], auto_collect_timeout)
65
66
  cnxns = ready_cnxns.first
@@ -68,13 +69,13 @@ module Protobuf
68
69
  when !running? then
69
70
  # no-op
70
71
  when client == @server then
71
- log_debug { "[#{log_signature}] Accepted new connection" }
72
+ log_debug { sign_message("Accepted new connection") }
72
73
  client, sockaddr = @server.accept
73
74
  @listen_fds << client
74
75
  else
75
76
  if !@working.include?(client)
76
77
  @working << @listen_fds.delete(client)
77
- log_debug { "[#{log_signature}] Working" }
78
+ log_debug { sign_message("Working") }
78
79
  @threads << { :thread => new_worker(client), :socket => client }
79
80
 
80
81
  cleanup_threads if cleanup?