protobuf 2.0.0.rc3 → 2.0.0.rc4
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.
- data/lib/protobuf/cli.rb +1 -2
- data/lib/protobuf/{common/exceptions.rb → exceptions.rb} +0 -0
- data/lib/protobuf/field/base_field.rb +1 -1
- data/lib/protobuf/{common/logger.rb → logger.rb} +21 -0
- data/lib/protobuf/message/decoder.rb +2 -2
- data/lib/protobuf/message/encoder.rb +6 -4
- data/lib/protobuf/rpc/buffer.rb +2 -2
- data/lib/protobuf/rpc/client.rb +18 -18
- data/lib/protobuf/rpc/connectors/base.rb +3 -8
- data/lib/protobuf/rpc/connectors/common.rb +29 -28
- data/lib/protobuf/rpc/connectors/em_client.rb +9 -9
- data/lib/protobuf/rpc/connectors/eventmachine.rb +11 -9
- data/lib/protobuf/rpc/connectors/socket.rb +13 -17
- data/lib/protobuf/rpc/connectors/zmq.rb +13 -17
- data/lib/protobuf/rpc/error.rb +3 -3
- data/lib/protobuf/rpc/server.rb +41 -93
- data/lib/protobuf/rpc/servers/evented/server.rb +7 -9
- data/lib/protobuf/rpc/servers/evented_runner.rb +0 -11
- data/lib/protobuf/rpc/servers/socket/server.rb +8 -7
- data/lib/protobuf/rpc/servers/socket/worker.rb +22 -15
- data/lib/protobuf/rpc/servers/zmq/server.rb +3 -3
- data/lib/protobuf/rpc/servers/zmq/util.rb +1 -1
- data/lib/protobuf/rpc/servers/zmq/worker.rb +6 -15
- data/lib/protobuf/rpc/service.rb +145 -228
- data/lib/protobuf/rpc/service_dispatcher.rb +114 -0
- data/lib/protobuf/rpc/stat.rb +46 -33
- data/lib/protobuf/version.rb +1 -1
- data/lib/protobuf/{common/wire_type.rb → wire_type.rb} +0 -0
- data/spec/benchmark/tasks.rb +18 -18
- data/spec/functional/evented_server_spec.rb +3 -4
- data/spec/functional/socket_server_spec.rb +3 -3
- data/spec/functional/zmq_server_spec.rb +3 -3
- data/spec/lib/protobuf/{common/logger_spec.rb → logger_spec.rb} +46 -36
- data/spec/lib/protobuf/rpc/client_spec.rb +10 -58
- data/spec/lib/protobuf/rpc/connectors/base_spec.rb +1 -39
- data/spec/lib/protobuf/rpc/connectors/common_spec.rb +3 -6
- data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +0 -12
- data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +1 -6
- data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +94 -0
- data/spec/lib/protobuf/rpc/service_spec.rb +132 -45
- data/spec/spec_helper.rb +4 -3
- data/spec/support/server.rb +8 -4
- 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
|
-
|
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 {
|
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 { "
|
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
|
40
|
-
log_debug { "
|
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
|
-
|
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 { "
|
57
|
-
return if
|
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
|
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 { "
|
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
|
-
|
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
|
28
|
+
return if @error
|
33
29
|
zmq_error_check(@socket.close)
|
34
30
|
zmq_error_check(@zmq_context.terminate)
|
35
|
-
log_debug { "
|
31
|
+
log_debug { sign_message("Connector closed") }
|
36
32
|
end
|
37
33
|
|
38
34
|
def connect_to_rpc_server
|
39
|
-
return if
|
40
|
-
log_debug { "
|
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 { "
|
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
|
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
|
61
|
-
log_debug { "
|
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 { "
|
60
|
+
log_debug { sign_message("write closed") }
|
65
61
|
end
|
66
62
|
|
67
63
|
def zmq_error_check(return_code)
|
data/lib/protobuf/rpc/error.rb
CHANGED
@@ -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
|
data/lib/protobuf/rpc/server.rb
CHANGED
@@ -1,140 +1,88 @@
|
|
1
|
-
require 'protobuf/
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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 { "
|
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
|
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
|
-
@
|
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 { "
|
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
|
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
|
-
|
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.
|
133
|
-
@stats.log_stats
|
134
|
-
@did_respond = true
|
81
|
+
@stats.stop && log_info { @stats.to_s }
|
135
82
|
ensure
|
136
|
-
|
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
|
-
|
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 {
|
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 { "
|
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 { "
|
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 { "
|
30
|
+
log_debug { sign_message("Thread cleanup - #{@threads.size} - complete") }
|
30
31
|
end
|
31
32
|
|
32
33
|
def self.log_signature
|
33
|
-
@
|
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 { "
|
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 { "
|
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 { "
|
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 { "
|
78
|
+
log_debug { sign_message("Working") }
|
78
79
|
@threads << { :thread => new_worker(client), :socket => client }
|
79
80
|
|
80
81
|
cleanup_threads if cleanup?
|