protobuf 2.7.11-java → 2.8.0.beta1-java
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/README.md +39 -2
- data/lib/protobuf.rb +17 -26
- data/lib/protobuf/cli.rb +106 -86
- data/lib/protobuf/field/float_field.rb +5 -1
- data/lib/protobuf/rpc/connectors/base.rb +1 -1
- data/lib/protobuf/rpc/connectors/zmq.rb +157 -29
- data/lib/protobuf/rpc/dynamic_discovery.pb.rb +49 -0
- data/lib/protobuf/rpc/error/client_error.rb +5 -5
- data/lib/protobuf/rpc/error/server_error.rb +7 -7
- data/lib/protobuf/rpc/rpc.pb.rb +13 -12
- data/lib/protobuf/rpc/servers/evented_runner.rb +11 -6
- data/lib/protobuf/rpc/servers/socket/server.rb +19 -15
- data/lib/protobuf/rpc/servers/socket_runner.rb +21 -18
- data/lib/protobuf/rpc/servers/zmq/broker.rb +104 -94
- data/lib/protobuf/rpc/servers/zmq/server.rb +263 -43
- data/lib/protobuf/rpc/servers/zmq/util.rb +18 -6
- data/lib/protobuf/rpc/servers/zmq/worker.rb +102 -39
- data/lib/protobuf/rpc/servers/zmq_runner.rb +31 -20
- data/lib/protobuf/rpc/service.rb +24 -12
- data/lib/protobuf/rpc/service_directory.rb +206 -0
- data/lib/protobuf/rpc/stat.rb +1 -1
- data/lib/protobuf/version.rb +1 -1
- data/proto/dynamic_discovery.proto +44 -0
- data/spec/benchmark/tasks.rb +1 -3
- data/spec/functional/socket_server_spec.rb +6 -5
- data/spec/functional/zmq_server_spec.rb +59 -30
- data/spec/lib/protobuf/cli_spec.rb +49 -54
- data/spec/lib/protobuf/enum_spec.rb +1 -1
- data/spec/lib/protobuf/rpc/client_spec.rb +1 -1
- data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +43 -1
- data/spec/lib/protobuf/rpc/servers/evented_server_spec.rb +2 -1
- data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +9 -8
- data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +24 -19
- data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +5 -5
- data/spec/lib/protobuf/rpc/service_directory_spec.rb +183 -0
- data/spec/support/server.rb +21 -12
- data/spec/support/test/resource.pb.rb +6 -0
- data/spec/support/test/resource.proto +5 -0
- data/spec/support/test/resource_service.rb +7 -0
- metadata +11 -11
- data/spec/lib/protobuf/rpc/servers/zmq/broker_spec.rb +0 -31
@@ -1,29 +1,41 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
|
1
3
|
module Protobuf
|
2
4
|
module Rpc
|
3
5
|
module Zmq
|
4
6
|
|
5
|
-
WORKER_READY_MESSAGE = "
|
7
|
+
WORKER_READY_MESSAGE = "\1"
|
6
8
|
|
7
9
|
module Util
|
8
10
|
include ::Protobuf::Logger::LogMethods
|
11
|
+
|
9
12
|
def self.included(base)
|
10
13
|
base.extend(::Protobuf::Rpc::Zmq::Util)
|
11
14
|
end
|
12
15
|
|
13
|
-
def zmq_error_check(return_code)
|
14
|
-
|
16
|
+
def zmq_error_check(return_code, source = nil)
|
17
|
+
unless ::ZMQ::Util.resultcode_ok?(return_code)
|
18
|
+
raise <<-ERROR
|
19
|
+
Last ZMQ API call #{source ? "to #{source}" : ""} failed with "#{::ZMQ::Util.error_string}".
|
20
|
+
|
21
|
+
#{caller(1).join($/)}
|
22
|
+
ERROR
|
23
|
+
end
|
15
24
|
end
|
16
25
|
|
17
26
|
def log_signature
|
18
|
-
@_log_signature
|
27
|
+
unless @_log_signature
|
28
|
+
name = (self.class == Class ? self.name : self.class.name)
|
29
|
+
@_log_signature = "[server-#{name}-#{object_id}]"
|
30
|
+
end
|
31
|
+
|
32
|
+
@_log_signature
|
19
33
|
end
|
20
34
|
|
21
35
|
def resolve_ip(hostname)
|
22
36
|
::Resolv.getaddress(hostname)
|
23
37
|
end
|
24
|
-
|
25
38
|
end
|
26
|
-
|
27
39
|
end
|
28
40
|
end
|
29
41
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'protobuf/rpc/server'
|
2
2
|
require 'protobuf/rpc/servers/zmq/util'
|
3
|
+
|
3
4
|
module Protobuf
|
4
5
|
module Rpc
|
5
6
|
module Zmq
|
6
|
-
|
7
7
|
class Worker
|
8
8
|
include ::Protobuf::Rpc::Server
|
9
9
|
include ::Protobuf::Rpc::Zmq::Util
|
@@ -11,63 +11,126 @@ module Protobuf
|
|
11
11
|
##
|
12
12
|
# Constructor
|
13
13
|
#
|
14
|
-
def initialize(
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@poller = ::ZMQ::Poller.new
|
23
|
-
@poller.register(@socket, ::ZMQ::POLLIN)
|
24
|
-
|
25
|
-
# Send request to broker telling it we are ready
|
26
|
-
zmq_error_check(@socket.send_string(::Protobuf::Rpc::Zmq::WORKER_READY_MESSAGE))
|
14
|
+
def initialize(server)
|
15
|
+
@server = server
|
16
|
+
init_zmq_context
|
17
|
+
init_backend_socket
|
18
|
+
init_shutdown_socket
|
19
|
+
rescue
|
20
|
+
teardown
|
21
|
+
raise
|
27
22
|
end
|
28
23
|
|
29
24
|
##
|
30
25
|
# Instance Methods
|
31
26
|
#
|
32
|
-
def
|
33
|
-
|
34
|
-
|
27
|
+
def alive?
|
28
|
+
@thread.try(:alive?) || false
|
29
|
+
end
|
35
30
|
|
36
|
-
|
37
|
-
@
|
38
|
-
|
31
|
+
def join
|
32
|
+
@thread.try(:join)
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_request
|
36
|
+
@client_address, empty, @request_data = read_from_backend
|
37
|
+
|
38
|
+
unless @request_data.nil?
|
39
|
+
log_debug { sign_message("handling request") }
|
40
|
+
handle_client
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def read_from_backend
|
45
|
+
[].tap do |frames|
|
46
|
+
zmq_error_check(@backend_socket.recv_strings(frames))
|
47
|
+
end
|
39
48
|
end
|
40
49
|
|
41
50
|
def run
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
51
|
+
poller = ::ZMQ::Poller.new
|
52
|
+
poller.register_readable(@backend_socket)
|
53
|
+
poller.register_readable(@shutdown_socket)
|
54
|
+
|
55
|
+
# Send request to broker telling it we are ready
|
56
|
+
write_to_backend([::Protobuf::Rpc::Zmq::WORKER_READY_MESSAGE])
|
57
|
+
|
58
|
+
catch(:shutdown) do
|
59
|
+
while poller.poll > 0
|
60
|
+
poller.readables.each do |readable|
|
61
|
+
case readable
|
62
|
+
when @backend_socket
|
63
|
+
initialize_request!
|
64
|
+
process_request
|
65
|
+
when @shutdown_socket
|
66
|
+
throw :shutdown
|
67
|
+
end
|
68
|
+
end
|
50
69
|
end
|
51
70
|
end
|
52
71
|
ensure
|
53
|
-
|
54
|
-
@zmq_context.terminate
|
72
|
+
teardown
|
55
73
|
end
|
56
74
|
|
57
75
|
def send_data
|
58
|
-
|
76
|
+
data = @response.serialize_to_string
|
59
77
|
|
60
|
-
|
61
|
-
@client_address, # client uuid address
|
62
|
-
"",
|
63
|
-
response_data
|
64
|
-
]
|
78
|
+
@stats.response_size = data.size
|
65
79
|
|
66
|
-
@
|
67
|
-
|
80
|
+
write_to_backend([@client_address, "", data])
|
81
|
+
end
|
82
|
+
|
83
|
+
def shutdown_uri
|
84
|
+
"inproc://#{object_id}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def signal_shutdown
|
88
|
+
socket = @zmq_context.socket ZMQ::PAIR
|
89
|
+
zmq_error_check(socket.connect(shutdown_uri))
|
90
|
+
zmq_error_check(socket.send_string("."))
|
91
|
+
zmq_error_check(socket.close)
|
92
|
+
end
|
93
|
+
|
94
|
+
def start
|
95
|
+
@thread = Thread.new do
|
96
|
+
begin
|
97
|
+
self.run
|
98
|
+
rescue => e
|
99
|
+
message = "Worker failed: #{e.inspect}\n #{e.backtrace.join($/)}"
|
100
|
+
$stderr.puts(message)
|
101
|
+
log_error { message }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
def teardown
|
109
|
+
@backend_socket.try(:close)
|
110
|
+
@shutdown_socket.try(:close)
|
111
|
+
@zmq_context.try(:terminate)
|
68
112
|
end
|
69
|
-
end
|
70
113
|
|
114
|
+
def write_to_backend(frames)
|
115
|
+
zmq_error_check(@backend_socket.send_strings(frames))
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def init_zmq_context
|
121
|
+
@zmq_context = ZMQ::Context.new
|
122
|
+
end
|
123
|
+
|
124
|
+
def init_backend_socket
|
125
|
+
@backend_socket = @zmq_context.socket(ZMQ::REQ)
|
126
|
+
zmq_error_check(@backend_socket.connect(@server.backend_uri))
|
127
|
+
end
|
128
|
+
|
129
|
+
def init_shutdown_socket
|
130
|
+
@shutdown_socket = @zmq_context.socket(ZMQ::PAIR)
|
131
|
+
zmq_error_check(@shutdown_socket.bind(shutdown_uri))
|
132
|
+
end
|
133
|
+
end
|
71
134
|
end
|
72
135
|
end
|
73
136
|
end
|
@@ -1,35 +1,46 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
1
3
|
module Protobuf
|
2
4
|
module Rpc
|
3
5
|
class ZmqRunner
|
4
6
|
include ::Protobuf::Logger::LogMethods
|
5
7
|
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
def initialize(options)
|
9
|
+
@options = case
|
10
|
+
when options.is_a?(OpenStruct) then
|
11
|
+
options.marshal_dump
|
12
|
+
when options.respond_to?(:to_hash) then
|
13
|
+
options.to_hash
|
14
|
+
else
|
15
|
+
raise "Cannot parser Zmq Server - server options"
|
16
|
+
end
|
13
17
|
|
14
|
-
|
15
|
-
server_config = case
|
16
|
-
when server.is_a?(OpenStruct) then
|
17
|
-
server.marshal_dump
|
18
|
-
when server.respond_to?(:to_hash) then
|
19
|
-
server.to_hash
|
20
|
-
else
|
21
|
-
raise "Cannot parser Zmq Server - server options"
|
22
|
-
end
|
18
|
+
end
|
23
19
|
|
24
|
-
|
20
|
+
def run
|
21
|
+
@server = ::Protobuf::Rpc::Zmq::Server.new(@options)
|
22
|
+
register_signals
|
23
|
+
yield if block_given?
|
24
|
+
@server.run
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
+
def running?
|
28
|
+
@server.try :running?
|
27
29
|
end
|
28
30
|
|
29
|
-
def
|
30
|
-
|
31
|
+
def stop
|
32
|
+
@server.try :stop
|
31
33
|
end
|
32
34
|
|
35
|
+
private
|
36
|
+
|
37
|
+
def register_signals
|
38
|
+
trap(:TTIN) do
|
39
|
+
log_info { "TTIN received: Starting new worker" }
|
40
|
+
@server.start_worker
|
41
|
+
log_info { "Worker count : #{::Protobuf::Rpc::Zmq::Server.threads.size}" }
|
42
|
+
end
|
43
|
+
end
|
33
44
|
end
|
34
45
|
end
|
35
46
|
end
|
data/lib/protobuf/rpc/service.rb
CHANGED
@@ -96,6 +96,18 @@ module Protobuf
|
|
96
96
|
rpcs.key?(name)
|
97
97
|
end
|
98
98
|
|
99
|
+
# An array of defined service classes that contain implementation
|
100
|
+
# code
|
101
|
+
def self.implemented_services
|
102
|
+
classes = (self.subclasses || []).select do |subclass|
|
103
|
+
subclass.rpcs.any? do |(name, method)|
|
104
|
+
subclass.method_defined? name
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
classes.map &:name
|
109
|
+
end
|
110
|
+
|
99
111
|
##
|
100
112
|
# Instance Methods
|
101
113
|
#
|
@@ -122,6 +134,18 @@ module Protobuf
|
|
122
134
|
@_response ||= response_type.new
|
123
135
|
end
|
124
136
|
|
137
|
+
# Request object for this rpc cycle. Not assignable.
|
138
|
+
#
|
139
|
+
def request
|
140
|
+
@_request ||= if @_request_bytes.present?
|
141
|
+
request_type.new.parse_from_string(@_request_bytes)
|
142
|
+
else
|
143
|
+
request_type.new
|
144
|
+
end
|
145
|
+
rescue => e
|
146
|
+
raise BadRequestProto, "Unable to parse request: #{e.message}"
|
147
|
+
end
|
148
|
+
|
125
149
|
# Convenience method to get back to class method.
|
126
150
|
#
|
127
151
|
def rpc_method?(name)
|
@@ -149,18 +173,6 @@ module Protobuf
|
|
149
173
|
@_response_type ||= rpcs[@method_name].response_type
|
150
174
|
end
|
151
175
|
|
152
|
-
# Request object for this rpc cycle. Not assignable.
|
153
|
-
#
|
154
|
-
def request
|
155
|
-
@_request ||= if @_request_bytes.present?
|
156
|
-
request_type.new.parse_from_string(@_request_bytes)
|
157
|
-
else
|
158
|
-
request_type.new
|
159
|
-
end
|
160
|
-
rescue => e
|
161
|
-
raise BadRequestProto, "Unable to parse request: #{e.message}"
|
162
|
-
end
|
163
|
-
|
164
176
|
def request_type
|
165
177
|
@_request_type ||= rpcs[@method_name].request_type
|
166
178
|
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'singleton'
|
3
|
+
require 'socket'
|
4
|
+
require 'thread'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
require 'protobuf/rpc/dynamic_discovery.pb'
|
8
|
+
|
9
|
+
module Protobuf
|
10
|
+
module Rpc
|
11
|
+
class ServiceDirectory
|
12
|
+
include ::Singleton
|
13
|
+
include ::Protobuf::Logger::LogMethods
|
14
|
+
|
15
|
+
DEFAULT_ADDRESS = "255.255.255.255"
|
16
|
+
DEFAULT_PORT = 53000
|
17
|
+
DEFAULT_TIMEOUT = 1
|
18
|
+
|
19
|
+
class Listing < Delegator
|
20
|
+
attr_reader :expires_at
|
21
|
+
|
22
|
+
def initialize(server)
|
23
|
+
@server = server
|
24
|
+
@expires_at = Time.now.to_i + ttl
|
25
|
+
end
|
26
|
+
|
27
|
+
def current?
|
28
|
+
!expired?
|
29
|
+
end
|
30
|
+
|
31
|
+
def expired?
|
32
|
+
Time.now.to_i >= @expires_at
|
33
|
+
end
|
34
|
+
|
35
|
+
def ttl
|
36
|
+
[super.to_i, 3].max
|
37
|
+
end
|
38
|
+
|
39
|
+
def __getobj__
|
40
|
+
@server
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Class Methods
|
45
|
+
#
|
46
|
+
class << self
|
47
|
+
attr_writer :address, :port
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.address
|
51
|
+
@address ||= DEFAULT_ADDRESS
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.port
|
55
|
+
@port ||= DEFAULT_PORT
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.start
|
59
|
+
yield(self) if block_given?
|
60
|
+
self.instance.start
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.stop
|
64
|
+
self.instance.stop
|
65
|
+
end
|
66
|
+
|
67
|
+
# Instance Methods
|
68
|
+
#
|
69
|
+
def initialize
|
70
|
+
@listings = {}
|
71
|
+
@mutex = Mutex.new
|
72
|
+
end
|
73
|
+
|
74
|
+
def add_listing_for(server)
|
75
|
+
if server && server.uuid
|
76
|
+
|
77
|
+
log_debug do
|
78
|
+
action = @listings[server.uuid] ? "Updating" : "Adding";
|
79
|
+
sign_message("#{action} server: #{server.inspect}")
|
80
|
+
end
|
81
|
+
|
82
|
+
@mutex.synchronize do
|
83
|
+
@listings[server.uuid] = Listing.new(server)
|
84
|
+
end
|
85
|
+
|
86
|
+
else
|
87
|
+
log_info { sign_message("Cannot add server without uuid: #{server.inspect}") }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def lookup(service)
|
92
|
+
@mutex.synchronize do
|
93
|
+
listings = @listings.values.select do |listing|
|
94
|
+
listing.services.any? do |listed_service|
|
95
|
+
listing.current? && listed_service == service.to_s
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
listings.sample
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def remove_expired_listings
|
104
|
+
@mutex.synchronize do
|
105
|
+
@listings.delete_if do |uuid, listing|
|
106
|
+
listing.expired?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def remove_listing_for(server)
|
112
|
+
if server && server.uuid
|
113
|
+
log_debug { sign_message("Removing server: #{server.inspect}") }
|
114
|
+
|
115
|
+
@mutex.synchronize do
|
116
|
+
@listings.delete(server.uuid)
|
117
|
+
end
|
118
|
+
|
119
|
+
else
|
120
|
+
log_info { sign_message("Cannot remove server without uuid: #{server.inspect}") }
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def restart
|
125
|
+
stop
|
126
|
+
start
|
127
|
+
end
|
128
|
+
|
129
|
+
def running?
|
130
|
+
!!@thread.try(:alive?)
|
131
|
+
end
|
132
|
+
|
133
|
+
def start
|
134
|
+
unless running?
|
135
|
+
init_socket
|
136
|
+
log_info { sign_message("listening to udp://#{self.class.address}:#{self.class.port}") }
|
137
|
+
@thread = Thread.new { self.send(:run) }
|
138
|
+
end
|
139
|
+
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
def stop
|
144
|
+
log_info { sign_message("Stopping directory") }
|
145
|
+
|
146
|
+
@mutex.synchronize do
|
147
|
+
@thread.try(:kill)
|
148
|
+
@thread = nil
|
149
|
+
@listings = {}
|
150
|
+
end
|
151
|
+
|
152
|
+
@socket.try(:close)
|
153
|
+
@socket = nil
|
154
|
+
end
|
155
|
+
|
156
|
+
def wait_for(service, timeout = DEFAULT_TIMEOUT)
|
157
|
+
log_debug { sign_message("waiting for #{service}") }
|
158
|
+
Timeout.timeout(timeout) do
|
159
|
+
sleep(timeout / 10.0) until listing = lookup(service)
|
160
|
+
listing
|
161
|
+
end
|
162
|
+
rescue
|
163
|
+
log_info { sign_message("no listing found for #{service}") }
|
164
|
+
nil
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def init_socket
|
170
|
+
@socket = UDPSocket.new
|
171
|
+
@socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, true)
|
172
|
+
@socket.bind(self.class.address, self.class.port.to_i)
|
173
|
+
end
|
174
|
+
|
175
|
+
def process_beacon(beacon)
|
176
|
+
case beacon.beacon_type
|
177
|
+
when ::Protobuf::Rpc::DynamicDiscovery::BeaconType::HEARTBEAT
|
178
|
+
add_listing_for(beacon.server)
|
179
|
+
when ::Protobuf::Rpc::DynamicDiscovery::BeaconType::FLATLINE
|
180
|
+
remove_listing_for(beacon.server)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def run
|
185
|
+
loop do
|
186
|
+
process_beacon(wait_for_beacon)
|
187
|
+
remove_expired_listings
|
188
|
+
end
|
189
|
+
rescue => e
|
190
|
+
log_debug { sign_message("error: (#{e.class}) #{e.message}") }
|
191
|
+
retry
|
192
|
+
end
|
193
|
+
|
194
|
+
def wait_for_beacon
|
195
|
+
data, addr = @socket.recvfrom(2048)
|
196
|
+
|
197
|
+
::Protobuf::Rpc::DynamicDiscovery::Beacon.new.tap do |beacon|
|
198
|
+
beacon.parse_from_string(data) rescue nil
|
199
|
+
|
200
|
+
# Favor the address captured by the socket
|
201
|
+
beacon.try(:server).try(:address=, addr[3])
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|