ruby_skynet 0.6.0 → 0.7.0
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +1 -1
- data/Rakefile +3 -3
- data/examples/echo_client.rb +8 -4
- data/examples/echo_server.rb +5 -4
- data/lib/ruby_skynet/base.rb +0 -18
- data/lib/ruby_skynet/client.rb +56 -15
- data/lib/ruby_skynet/connection.rb +82 -84
- data/lib/ruby_skynet/server.rb +2 -2
- data/lib/ruby_skynet/service_registry.rb +5 -5
- data/lib/ruby_skynet/version.rb +1 -1
- data/test/client_test.rb +30 -7
- data/test/service_registry_test.rb +14 -8
- data/test/service_test.rb +2 -4
- metadata +8 -9
- data/test/base_test.rb +0 -89
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 09c9a8e2a436c89fd46211dc103f0b55946fbfc2
|
4
|
+
data.tar.gz: e2683cdb8fae60e831c7793fca87e4f1ae573095
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87a5e433d14d3c986c44472f0c19baeebd54a87362e071478d046238217470072fae4712af21ada8b16e771c326c44aa5a7b69ac96b124a0f3bab7e7994f2226
|
7
|
+
data.tar.gz: 5baff9cf4822187512543ef599f7e6842a5dab2a95744da5a55461a9d711c820f738018b8783672b8f3da12d1903a66c70bfc955395217d2df83db8f8ac5fb15
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/Rakefile
CHANGED
@@ -24,11 +24,11 @@ task :gem do |t|
|
|
24
24
|
spec.files = FileList["./**/*"].exclude(/\.gem$/, /\.log$/,/nbproject/).map{|f| f.sub(/^\.\//, '')}
|
25
25
|
spec.license = "Apache License V2.0"
|
26
26
|
spec.has_rdoc = true
|
27
|
-
spec.add_dependency 'semantic_logger', '>= 2.
|
28
|
-
spec.add_dependency 'resilient_socket', '>= 0.
|
27
|
+
spec.add_dependency 'semantic_logger', '>= 2.1.0'
|
28
|
+
spec.add_dependency 'resilient_socket', '>= 0.5.0'
|
29
29
|
spec.add_dependency 'multi_json', '>= 1.6.1'
|
30
30
|
spec.add_dependency 'bson', '>= 1.5.2'
|
31
|
-
spec.add_dependency 'ruby_doozer', '>= 0.
|
31
|
+
spec.add_dependency 'ruby_doozer', '>= 0.6.0'
|
32
32
|
spec.add_dependency 'gene_pool', '>= 1.3.0'
|
33
33
|
end
|
34
34
|
Gem::Package.build gemspec
|
data/examples/echo_client.rb
CHANGED
@@ -3,8 +3,12 @@ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'ruby_skynet'
|
5
5
|
|
6
|
-
SemanticLogger
|
7
|
-
SemanticLogger
|
6
|
+
SemanticLogger.default_level = :info
|
7
|
+
SemanticLogger.add_appender(STDOUT)
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
class Echo < RubySkynet::Client
|
10
|
+
self.skynet_name = "EchoService"
|
11
|
+
end
|
12
|
+
|
13
|
+
client = Echo.new
|
14
|
+
p client.echo(:hello => 'world')
|
data/examples/echo_server.rb
CHANGED
@@ -3,18 +3,20 @@ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'ruby_skynet'
|
5
5
|
|
6
|
-
|
7
|
-
SemanticLogger
|
8
|
-
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('echo_server.log')
|
6
|
+
SemanticLogger.default_level = :info
|
7
|
+
SemanticLogger.add_appender(STDOUT)
|
9
8
|
|
10
9
|
# Just echo back any parameters received when the echo method is called
|
11
10
|
class EchoService
|
12
11
|
include RubySkynet::Service
|
13
12
|
|
13
|
+
skynet_name = "JoeService"
|
14
|
+
|
14
15
|
# Methods implemented by this service
|
15
16
|
# Must take a Hash as input
|
16
17
|
# Must Return a Hash response or nil for no response
|
17
18
|
def echo(params)
|
19
|
+
params['echo'] = true
|
18
20
|
params
|
19
21
|
end
|
20
22
|
end
|
@@ -25,4 +27,3 @@ RubySkynet::Server.start
|
|
25
27
|
puts "Press enter to shutdown server"
|
26
28
|
gets
|
27
29
|
|
28
|
-
RubySkynet::Server.stop
|
data/lib/ruby_skynet/base.rb
CHANGED
@@ -7,24 +7,6 @@ module RubySkynet
|
|
7
7
|
base.extend ClassMethods
|
8
8
|
base.class_eval do
|
9
9
|
include SemanticLogger::Loggable
|
10
|
-
include InstanceMethods
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
module InstanceMethods
|
15
|
-
# Implement methods that call the remote Service
|
16
|
-
def method_missing(method, *args, &block)
|
17
|
-
result = ruby_skynet_client.call(method, *args)
|
18
|
-
# Define the method if the call was successful and no-one else already
|
19
|
-
# created the method
|
20
|
-
if result[:exception].nil? && !self.class.method_defined?(method)
|
21
|
-
self.class.send(:define_method, method) {|*args| ruby_skynet_client.call(method, *args)}
|
22
|
-
end
|
23
|
-
result
|
24
|
-
end
|
25
|
-
|
26
|
-
def ruby_skynet_client
|
27
|
-
@ruby_skynet_client ||= RubySkynet::Client.new(self.class.skynet_name, self.class.skynet_version || '*', self.class.skynet_region)
|
28
10
|
end
|
29
11
|
end
|
30
12
|
|
data/lib/ruby_skynet/client.rb
CHANGED
@@ -8,6 +8,17 @@ require 'bson'
|
|
8
8
|
#
|
9
9
|
module RubySkynet
|
10
10
|
class Client
|
11
|
+
include Base
|
12
|
+
|
13
|
+
attr_reader :skynet_name, :skynet_version, :skynet_region
|
14
|
+
|
15
|
+
# Version of the Skynet service to use
|
16
|
+
# By default it will connect to the latest version
|
17
|
+
# Default: '*'
|
18
|
+
def self.skynet_version
|
19
|
+
@skynet_version ||= '*'
|
20
|
+
end
|
21
|
+
|
11
22
|
# Returns a new RubySkynet Client for the named service
|
12
23
|
#
|
13
24
|
# Calls to an instance of the Client are thread-safe and can be called
|
@@ -15,29 +26,45 @@ module RubySkynet
|
|
15
26
|
#
|
16
27
|
# Parameters:
|
17
28
|
# :skynet_name
|
29
|
+
# Only required when creating instance of RubySkynet::Client directly
|
30
|
+
# Otherwise it defaults to the name of the class
|
18
31
|
# Name of the service to look for and connect to on Skynet
|
19
32
|
#
|
20
|
-
# :
|
33
|
+
# :skynet_version
|
21
34
|
# Optional version number of the service in Skynet
|
22
35
|
# Default: '*' being the latest version of the service
|
23
36
|
#
|
24
|
-
# :
|
37
|
+
# :skynet_region
|
25
38
|
# Optional region for this service in Skynet
|
26
|
-
# Default:
|
39
|
+
# Default: RubySkynet.region
|
40
|
+
#
|
41
|
+
# Example using Client class
|
42
|
+
#
|
43
|
+
# require 'ruby_skynet'
|
44
|
+
# SemanticLogger.default_level = :info
|
45
|
+
# SemanticLogger.add_appender(STDOUT)
|
27
46
|
#
|
28
|
-
#
|
47
|
+
# class EchoService < RubySkynet::Client
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# echo_service = EchoService.new
|
51
|
+
# p echo_service.echo(:value => 5)
|
52
|
+
#
|
53
|
+
# Example using Ruby Client directly
|
29
54
|
#
|
30
55
|
# require 'ruby_skynet'
|
31
|
-
# SemanticLogger.default_level = :
|
32
|
-
# SemanticLogger.
|
56
|
+
# SemanticLogger.default_level = :info
|
57
|
+
# SemanticLogger.add_appender(STDOUT)
|
33
58
|
#
|
34
59
|
# tutorial_service = RubySkynet::Client.new('TutorialService')
|
35
60
|
# p tutorial_service.call('Add', :value => 5)
|
36
|
-
def initialize(skynet_name,
|
37
|
-
@skynet_name
|
38
|
-
@
|
39
|
-
@
|
40
|
-
|
61
|
+
def initialize(skynet_name=self.class.skynet_name, skynet_version=self.class.skynet_version, skynet_region=self.class.skynet_region)
|
62
|
+
@skynet_name = skynet_name
|
63
|
+
@skynet_version = skynet_version
|
64
|
+
@skynet_region = skynet_region
|
65
|
+
self.logger = SemanticLogger["#{self.class.name}: #{@skynet_name}/#{@skynet_version}/#{@skynet_region}"]
|
66
|
+
|
67
|
+
raise "skynet_name is mandatory when using RubySkynet::Client directly" if @skynet_name == RubySkynet::Client.name
|
41
68
|
end
|
42
69
|
|
43
70
|
# Performs a synchronous call to the Skynet Service
|
@@ -56,13 +83,13 @@ module RubySkynet
|
|
56
83
|
# Skynet requires BSON RPC Calls to have the following format:
|
57
84
|
# https://github.com/skynetservices/skynet/blob/master/protocol.md
|
58
85
|
request_id = BSON::ObjectId.new.to_s
|
59
|
-
|
60
|
-
|
86
|
+
logger.tagged request_id do
|
87
|
+
logger.benchmark_info "Called Skynet Service: #{skynet_name}.#{method_name}" do
|
61
88
|
retries = 0
|
62
89
|
# If it cannot connect to a server, try a different server
|
63
90
|
begin
|
64
|
-
Connection.with_connection(::RubySkynet.services.server_for(
|
65
|
-
connection.rpc_call(request_id,
|
91
|
+
Connection.with_connection(::RubySkynet.services.server_for(skynet_name, skynet_version, skynet_region), connection_params) do |connection|
|
92
|
+
connection.rpc_call(request_id, skynet_name, method_name, parameters)
|
66
93
|
end
|
67
94
|
rescue ResilientSocket::ConnectionFailure => exc
|
68
95
|
if (retries < 3) && exc.cause.is_a?(Errno::ECONNREFUSED)
|
@@ -75,5 +102,19 @@ module RubySkynet
|
|
75
102
|
end
|
76
103
|
end
|
77
104
|
|
105
|
+
# Implement methods that call the remote Service
|
106
|
+
def method_missing(method, *args, &block)
|
107
|
+
result = call(method, *args)
|
108
|
+
|
109
|
+
# #TODO if Service returns method undefined, call super
|
110
|
+
#
|
111
|
+
# Define the method if the call was successful and no other thread has
|
112
|
+
# already created the method
|
113
|
+
if result[:exception].nil? && !self.class.method_defined?(method)
|
114
|
+
self.class.send(:define_method, method) {|*args| call(method, *args)}
|
115
|
+
end
|
116
|
+
result
|
117
|
+
end
|
118
|
+
|
78
119
|
end
|
79
120
|
end
|
@@ -12,6 +12,7 @@ require 'sync_attr'
|
|
12
12
|
module RubySkynet
|
13
13
|
class Connection
|
14
14
|
include SyncAttr
|
15
|
+
include SemanticLogger::Loggable
|
15
16
|
|
16
17
|
# Returns the underlying socket being used by a Connection instance
|
17
18
|
attr_reader :socket
|
@@ -26,11 +27,6 @@ module RubySkynet
|
|
26
27
|
}
|
27
28
|
end
|
28
29
|
|
29
|
-
# Logging instance for the connection pool
|
30
|
-
sync_cattr_reader :logger do
|
31
|
-
SemanticLogger::Logger.new(self)
|
32
|
-
end
|
33
|
-
|
34
30
|
# For each server there is a connection pool keyed on the
|
35
31
|
# server address: 'host:port'
|
36
32
|
@@connection_pools = ThreadSafe::Hash.new
|
@@ -55,7 +51,7 @@ module RubySkynet
|
|
55
51
|
# Number of seconds between connection retry attempts after the first failed attempt
|
56
52
|
# Default: 0.5
|
57
53
|
def initialize(server, params = {})
|
58
|
-
|
54
|
+
self.logger = SemanticLogger["#{self.class.name} [#{server}]"]
|
59
55
|
|
60
56
|
# User configurable options
|
61
57
|
params[:read_timeout] ||= 60
|
@@ -71,7 +67,7 @@ module RubySkynet
|
|
71
67
|
# Reset user_data on each connection
|
72
68
|
socket.user_data = {
|
73
69
|
:seq => 0,
|
74
|
-
:logger =>
|
70
|
+
:logger => logger
|
75
71
|
}
|
76
72
|
|
77
73
|
# Receive Service Handshake
|
@@ -81,9 +77,9 @@ module RubySkynet
|
|
81
77
|
#
|
82
78
|
# ClientID string
|
83
79
|
# ClientID is a UUID that is used by the client to identify itself in RPC requests.
|
84
|
-
|
80
|
+
logger.debug "Waiting for Service Handshake"
|
85
81
|
service_handshake = Common.read_bson_document(socket)
|
86
|
-
|
82
|
+
logger.trace 'Service Handshake', service_handshake
|
87
83
|
|
88
84
|
# #TODO When a reconnect returns registered == false need to throw an exception
|
89
85
|
# so that this host connection is not used
|
@@ -93,8 +89,8 @@ module RubySkynet
|
|
93
89
|
|
94
90
|
# Send blank ClientHandshake
|
95
91
|
client_handshake = { 'clientid' => client_id }
|
96
|
-
|
97
|
-
|
92
|
+
logger.debug "Sending Client Handshake"
|
93
|
+
logger.trace 'Client Handshake', client_handshake
|
98
94
|
socket.write(BSON.serialize(client_handshake).to_s)
|
99
95
|
end
|
100
96
|
|
@@ -123,91 +119,92 @@ module RubySkynet
|
|
123
119
|
# Raises RubySkynet::ProtocolError
|
124
120
|
# Raises RubySkynet::SkynetException
|
125
121
|
def rpc_call(request_id, skynet_name, method_name, parameters, idempotent=false)
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
122
|
+
logger.benchmark_info "Called #{skynet_name}.#{method_name}" do
|
123
|
+
retry_count = 0
|
124
|
+
header = nil
|
125
|
+
response = nil
|
126
|
+
socket.retry_on_connection_failure do |socket|
|
127
|
+
header = {
|
128
|
+
'servicemethod' => "#{skynet_name}.Forward",
|
129
|
+
'seq' => socket.user_data[:seq]
|
130
|
+
}
|
135
131
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
132
|
+
logger.debug "Sending Header"
|
133
|
+
logger.trace 'Header', header
|
134
|
+
socket.write(BSON.serialize(header).to_s)
|
135
|
+
|
136
|
+
# The parameters are placed in the request object in BSON serialized form
|
137
|
+
request = {
|
138
|
+
'clientid' => socket.user_data[:client_id],
|
139
|
+
'in' => BSON::Binary.new(BSON.serialize(parameters).to_s),
|
140
|
+
'method' => method_name.to_s,
|
141
|
+
'requestinfo' => {
|
142
|
+
'requestid' => request_id,
|
143
|
+
# Increment retry count to indicate that the request may have been tried previously
|
144
|
+
'retrycount' => retry_count,
|
145
|
+
# TODO: this should be forwarded along in case of services also
|
146
|
+
# being a client and calling additional services. If empty it will
|
147
|
+
# be stuffed with connecting address
|
148
|
+
'originaddress' => ''
|
149
|
+
}
|
153
150
|
}
|
154
|
-
}
|
155
151
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
152
|
+
logger.debug "Sending Request"
|
153
|
+
logger.trace 'Request', request
|
154
|
+
logger.trace 'Parameters:', parameters
|
155
|
+
socket.write(BSON.serialize(request).to_s)
|
156
|
+
|
157
|
+
# Since Send does not affect state on the server we can also retry reads
|
158
|
+
if idempotent
|
159
|
+
logger.debug "Reading header from server"
|
160
|
+
header = Common.read_bson_document(socket)
|
161
|
+
logger.debug 'Response Header', header
|
162
|
+
|
163
|
+
# Read the BSON response document
|
164
|
+
logger.debug "Reading response from server"
|
165
|
+
response = Common.read_bson_document(socket)
|
166
|
+
logger.trace 'Response', response
|
167
|
+
end
|
168
|
+
end
|
160
169
|
|
161
|
-
#
|
162
|
-
|
163
|
-
|
170
|
+
# Perform the read outside the retry block since a successful write
|
171
|
+
# means that the servers state may have been changed
|
172
|
+
unless idempotent
|
173
|
+
# Read header first as a separate BSON document
|
174
|
+
logger.debug "Reading header from server"
|
164
175
|
header = Common.read_bson_document(socket)
|
165
|
-
|
176
|
+
logger.debug 'Response Header', header
|
166
177
|
|
167
178
|
# Read the BSON response document
|
168
|
-
|
179
|
+
logger.debug "Reading response from server"
|
169
180
|
response = Common.read_bson_document(socket)
|
170
|
-
|
181
|
+
logger.trace 'Response', response
|
171
182
|
end
|
172
|
-
end
|
173
183
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
@logger.debug 'Response Header', header
|
181
|
-
|
182
|
-
# Read the BSON response document
|
183
|
-
@logger.debug "Reading response from server"
|
184
|
-
response = Common.read_bson_document(socket)
|
185
|
-
@logger.trace 'Response', response
|
186
|
-
end
|
187
|
-
|
188
|
-
# Ensure the sequence number in the response header matches the
|
189
|
-
# sequence number sent in the request
|
190
|
-
seq_no = header['seq']
|
191
|
-
if seq_no != socket.user_data[:seq]
|
192
|
-
raise ProtocolError.new("Incorrect Response received, expected seq=#{socket.user_data[:seq]}, received: #{header.inspect}")
|
193
|
-
end
|
184
|
+
# Ensure the sequence number in the response header matches the
|
185
|
+
# sequence number sent in the request
|
186
|
+
seq_no = header['seq']
|
187
|
+
if seq_no != socket.user_data[:seq]
|
188
|
+
raise ProtocolError.new("Incorrect Response received, expected seq=#{socket.user_data[:seq]}, received: #{header.inspect}")
|
189
|
+
end
|
194
190
|
|
195
|
-
|
196
|
-
|
191
|
+
# Increment Sequence number only on successful response
|
192
|
+
socket.user_data[:seq] += 1
|
197
193
|
|
198
|
-
|
199
|
-
|
200
|
-
|
194
|
+
# If an error is returned from Skynet raise a Skynet exception
|
195
|
+
error = header['error']
|
196
|
+
raise SkynetException.new(error) if error.to_s.length > 0
|
201
197
|
|
202
|
-
|
203
|
-
|
204
|
-
|
198
|
+
# If an error is returned from the service raise a Service exception
|
199
|
+
error = response['error']
|
200
|
+
raise ServiceException.new(error) if error.to_s.length > 0
|
205
201
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
202
|
+
# Return Value
|
203
|
+
# The return value is inside the response object, it's a byte array of it's own and needs to be deserialized
|
204
|
+
result = BSON.deserialize(response['out'])
|
205
|
+
logger.trace 'Return Value', result
|
206
|
+
result
|
207
|
+
end
|
211
208
|
end
|
212
209
|
|
213
210
|
# Execute the supplied block with a connection from the pool
|
@@ -227,10 +224,11 @@ module RubySkynet
|
|
227
224
|
# Connection pool configuration options
|
228
225
|
config = pool_config.dup
|
229
226
|
|
227
|
+
logger = SemanticLogger::Logger.new("#{self.class.name} [#{server}]")
|
228
|
+
|
230
229
|
# Method to call to close idle connections
|
231
230
|
config[:close_proc] = :close
|
232
231
|
config[:logger] = logger
|
233
|
-
config[:name] = "Connection pool for #{server}"
|
234
232
|
|
235
233
|
pool = GenePool.new(pool_config) do
|
236
234
|
new(server, params)
|
@@ -242,7 +240,7 @@ module RubySkynet
|
|
242
240
|
# Cannot close all the connections since they could still be in use
|
243
241
|
pool.remove_idle(0) if pool
|
244
242
|
#pool.close if pool
|
245
|
-
logger.debug "Connection pool
|
243
|
+
logger.debug "Connection pool released"
|
246
244
|
end
|
247
245
|
|
248
246
|
pool
|
data/lib/ruby_skynet/server.rb
CHANGED
@@ -205,13 +205,13 @@ module RubySkynet
|
|
205
205
|
|
206
206
|
# Registers a Service Class as being available at this server
|
207
207
|
def register_service(klass)
|
208
|
-
logger.
|
208
|
+
logger.info "Registering Service: #{klass.name} with name: #{klass.skynet_name}"
|
209
209
|
::RubySkynet.services.register_service(klass.skynet_name, klass.skynet_version || 1, klass.skynet_region, @hostname, @port)
|
210
210
|
end
|
211
211
|
|
212
212
|
# De-register service from this server
|
213
213
|
def deregister_service(klass)
|
214
|
-
logger.
|
214
|
+
logger.info "De-registering Service: #{klass.name} with name: #{klass.skynet_name}"
|
215
215
|
::RubySkynet.services.deregister_service(klass.skynet_name, klass.skynet_version || 1, klass.skynet_region, @hostname, @port)
|
216
216
|
end
|
217
217
|
|
@@ -99,7 +99,7 @@ module RubySkynet
|
|
99
99
|
end
|
100
100
|
|
101
101
|
# Return a server that implements the specified service
|
102
|
-
def server_for(name, version='*', region=
|
102
|
+
def server_for(name, version='*', region=RubySkynet.region)
|
103
103
|
if servers = servers_for(name, version, region)
|
104
104
|
# Randomly select one of the servers offering the service
|
105
105
|
servers[rand(servers.size)]
|
@@ -111,7 +111,7 @@ module RubySkynet
|
|
111
111
|
end
|
112
112
|
|
113
113
|
# Returns [Array<String>] a list of servers implementing the requested service
|
114
|
-
def servers_for(name, version='*', region=
|
114
|
+
def servers_for(name, version='*', region=RubySkynet.region)
|
115
115
|
if version == '*'
|
116
116
|
# Find the highest version for the named service in this region
|
117
117
|
version = -1
|
@@ -173,7 +173,6 @@ module RubySkynet
|
|
173
173
|
# Add the host to the registry based on it's score
|
174
174
|
def add_server(key, hostname, port)
|
175
175
|
server = "#{hostname}:#{port}"
|
176
|
-
logger.debug "#monitor Add/Update Service: #{key} => #{server.inspect}"
|
177
176
|
|
178
177
|
server_infos = (@registry[key] ||= ThreadSafe::Array.new)
|
179
178
|
|
@@ -183,6 +182,7 @@ module RubySkynet
|
|
183
182
|
|
184
183
|
# Look for the same score with a different server
|
185
184
|
score = self.class.score_for_server(hostname, RubySkynet.local_ip_address)
|
185
|
+
logger.info "Service: #{key} now running at #{server} with score #{score}"
|
186
186
|
if server_info = server_infos.find{|si| si.score == score}
|
187
187
|
server_info.servers << server
|
188
188
|
return server_info
|
@@ -206,7 +206,7 @@ module RubySkynet
|
|
206
206
|
# Returns the server instance if it was removed
|
207
207
|
def remove_server(key, hostname, port, notify)
|
208
208
|
server = "#{hostname}:#{port}"
|
209
|
-
logger.
|
209
|
+
logger.info "Service: #{key} stopped running at #{server}"
|
210
210
|
server_info = nil
|
211
211
|
if server_infos = @registry[key]
|
212
212
|
server_infos.each do |si|
|
@@ -235,7 +235,7 @@ module RubySkynet
|
|
235
235
|
if @on_server_removed_callbacks && (callbacks = @on_server_removed_callbacks.delete(server))
|
236
236
|
callbacks.each do |block|
|
237
237
|
begin
|
238
|
-
logger.
|
238
|
+
logger.debug "Calling callback for server: #{server}"
|
239
239
|
block.call(server)
|
240
240
|
rescue Exception => exc
|
241
241
|
logger.error("Exception during a callback for server: #{server}", exc)
|
data/lib/ruby_skynet/version.rb
CHANGED
data/test/client_test.rb
CHANGED
@@ -8,10 +8,8 @@ require 'shoulda'
|
|
8
8
|
require 'ruby_skynet'
|
9
9
|
|
10
10
|
# Register an appender if one is not already registered
|
11
|
-
|
12
|
-
|
13
|
-
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('test.log')
|
14
|
-
end
|
11
|
+
SemanticLogger.default_level = :trace
|
12
|
+
SemanticLogger.add_appender('test.log') if SemanticLogger.appenders.size == 0
|
15
13
|
|
16
14
|
class ClientTestService
|
17
15
|
include RubySkynet::Service
|
@@ -38,17 +36,21 @@ class ClientTestService
|
|
38
36
|
|
39
37
|
end
|
40
38
|
|
39
|
+
# Test Client Class
|
40
|
+
class ClientTestServiceClient < RubySkynet::Client
|
41
|
+
end
|
42
|
+
|
41
43
|
# Unit Test for ResilientSocket::TCPClient
|
42
44
|
class ClientTest < Test::Unit::TestCase
|
43
45
|
context RubySkynet::Client do
|
44
46
|
|
45
47
|
context "without server" do
|
46
|
-
should "raise exception when
|
48
|
+
should "raise exception when not registered" do
|
47
49
|
exception = assert_raise RubySkynet::ServiceUnavailable do
|
48
|
-
client = RubySkynet::Client.new('SomeService')
|
50
|
+
client = RubySkynet::Client.new('SomeService','*','ClientTest')
|
49
51
|
client.call(:test, :hello => 'there')
|
50
52
|
end
|
51
|
-
assert_match /No servers available for service: SomeService with version: \* in region:
|
53
|
+
assert_match /No servers available for service: SomeService with version: \* in region: ClientTest/, exception.message
|
52
54
|
end
|
53
55
|
|
54
56
|
end
|
@@ -90,8 +92,29 @@ class ClientTest < Test::Unit::TestCase
|
|
90
92
|
end
|
91
93
|
assert_match /Timedout after #{@read_timeout} seconds trying to read/, exception.message
|
92
94
|
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "using client class" do
|
98
|
+
setup do
|
99
|
+
@client = ClientTestServiceClient.new("ClientTestService")
|
100
|
+
end
|
101
|
+
|
102
|
+
should "successfully send and receive data" do
|
103
|
+
reply = @client.test1('some' => 'parameters')
|
104
|
+
assert_equal 'test1', reply['result']
|
105
|
+
end
|
93
106
|
|
107
|
+
should "timeout on receive" do
|
108
|
+
request = { 'duration' => @read_timeout + 0.5}
|
109
|
+
|
110
|
+
exception = assert_raise ResilientSocket::ReadTimeout do
|
111
|
+
# Read 4 bytes from server
|
112
|
+
@client.sleep(request, :read_timeout => @read_timeout)
|
113
|
+
end
|
114
|
+
assert_match /Timedout after #{@read_timeout} seconds trying to read/, exception.message
|
115
|
+
end
|
94
116
|
end
|
117
|
+
|
95
118
|
end
|
96
119
|
end
|
97
120
|
end
|
@@ -7,10 +7,8 @@ require 'shoulda'
|
|
7
7
|
require 'ruby_skynet'
|
8
8
|
|
9
9
|
# Register an appender if one is not already registered
|
10
|
-
|
11
|
-
|
12
|
-
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('test.log')
|
13
|
-
end
|
10
|
+
SemanticLogger.default_level = :trace
|
11
|
+
SemanticLogger.add_appender('test.log') if SemanticLogger.appenders.size == 0
|
14
12
|
|
15
13
|
# Unit Test
|
16
14
|
class ServiceRegistryTest < Test::Unit::TestCase
|
@@ -23,12 +21,17 @@ class ServiceRegistryTest < Test::Unit::TestCase
|
|
23
21
|
@hostname = '127.0.0.1'
|
24
22
|
@port = 2100
|
25
23
|
@service_key = "/services/#{@service_name}/#{@version}/#{@region}/#{@hostname}/#{@port}"
|
24
|
+
RubySkynet.local_ip_address = @hostname
|
25
|
+
end
|
26
|
+
|
27
|
+
teardown do
|
28
|
+
RubySkynet.local_ip_address = nil
|
26
29
|
end
|
27
30
|
|
28
31
|
context "without a registered service" do
|
29
32
|
should "not be in doozer" do
|
30
33
|
RubySkynet.services.send(:doozer_pool).with_connection do |doozer|
|
31
|
-
assert_equal
|
34
|
+
assert_equal nil, doozer[@service_key]
|
32
35
|
end
|
33
36
|
end
|
34
37
|
end
|
@@ -36,12 +39,14 @@ class ServiceRegistryTest < Test::Unit::TestCase
|
|
36
39
|
context "with a registered service" do
|
37
40
|
setup do
|
38
41
|
RubySkynet.services.register_service(@service_name, @version, @region, @hostname, @port)
|
42
|
+
RubySkynet.services.register_service(@service_name, @version, @region+'BLAH', @hostname, @port)
|
39
43
|
# Allow time for doozer callback that service was registered
|
40
44
|
sleep 0.1
|
41
45
|
end
|
42
46
|
|
43
47
|
teardown do
|
44
48
|
RubySkynet.services.deregister_service(@service_name, @version, @region, @hostname, @port)
|
49
|
+
RubySkynet.services.deregister_service(@service_name, @version, @region+'BLAH', @hostname, @port)
|
45
50
|
# Allow time for doozer callback that service was deregistered
|
46
51
|
sleep 0.1
|
47
52
|
# No servers should be in the local registry
|
@@ -78,9 +83,10 @@ class ServiceRegistryTest < Test::Unit::TestCase
|
|
78
83
|
|
79
84
|
should "using * version match" do
|
80
85
|
assert servers = RubySkynet.services.servers_for(@service_name, '*', @region)
|
81
|
-
assert_equal 3, servers.size,
|
82
|
-
assert_equal "#{@hostname}:#{@port}", servers
|
83
|
-
assert_equal "#{@hostname}:#{@port+
|
86
|
+
assert_equal 3, servers.size, servers
|
87
|
+
assert_equal true, servers.include?("#{@hostname}:#{@port}"), servers
|
88
|
+
assert_equal true, servers.include?("#{@hostname}:#{@port+1}"), servers
|
89
|
+
assert_equal true, servers.include?("#{@hostname}:#{@port+3}"), servers
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
data/test/service_test.rb
CHANGED
@@ -7,10 +7,8 @@ require 'shoulda'
|
|
7
7
|
require 'ruby_skynet'
|
8
8
|
|
9
9
|
# Register an appender if one is not already registered
|
10
|
-
|
11
|
-
|
12
|
-
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('test.log')
|
13
|
-
end
|
10
|
+
SemanticLogger.default_level = :trace
|
11
|
+
SemanticLogger.add_appender('test.log') if SemanticLogger.appenders.size == 0
|
14
12
|
|
15
13
|
class TestService
|
16
14
|
include RubySkynet::Service
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_skynet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-04-
|
11
|
+
date: 2013-04-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: semantic_logger
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.
|
19
|
+
version: 2.1.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.
|
26
|
+
version: 2.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: resilient_socket
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.5.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 0.5.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: multi_json
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - '>='
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.
|
75
|
+
version: 0.6.0
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - '>='
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
82
|
+
version: 0.6.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: gene_pool
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -124,7 +124,6 @@ files:
|
|
124
124
|
- lib/ruby_skynet/service_registry.rb
|
125
125
|
- lib/ruby_skynet/version.rb
|
126
126
|
- test.sh
|
127
|
-
- test/base_test.rb
|
128
127
|
- test/client_test.rb
|
129
128
|
- test/service_registry_test.rb
|
130
129
|
- test/service_test.rb
|
data/test/base_test.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
# Allow test to be run in-place without requiring a gem install
|
2
|
-
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
-
$LOAD_PATH.unshift File.dirname(__FILE__)
|
4
|
-
|
5
|
-
require 'rubygems'
|
6
|
-
require 'test/unit'
|
7
|
-
require 'shoulda'
|
8
|
-
require 'ruby_skynet'
|
9
|
-
|
10
|
-
# Register an appender if one is not already registered
|
11
|
-
if SemanticLogger::Logger.appenders.size == 0
|
12
|
-
SemanticLogger::Logger.default_level = :debug
|
13
|
-
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('test.log')
|
14
|
-
end
|
15
|
-
|
16
|
-
# Service implementation
|
17
|
-
class BaseTestService
|
18
|
-
include RubySkynet::Service
|
19
|
-
|
20
|
-
# Methods implemented by this service
|
21
|
-
# Must take a Hash as input
|
22
|
-
# Must Return a Hash response or nil for no response
|
23
|
-
def test1(params)
|
24
|
-
{ 'result' => 'test1' }
|
25
|
-
end
|
26
|
-
|
27
|
-
def sleep(params)
|
28
|
-
Kernel.sleep params['duration'] || 1
|
29
|
-
{ 'result' => 'sleep' }
|
30
|
-
end
|
31
|
-
|
32
|
-
def fail(params)
|
33
|
-
if params['attempt'].to_i >= 2
|
34
|
-
{ 'result' => 'fail' }
|
35
|
-
else
|
36
|
-
nil
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
# Service Client
|
43
|
-
class BaseTestServiceClient
|
44
|
-
include RubySkynet::Base
|
45
|
-
|
46
|
-
# Override Name registered in skynet to match server above
|
47
|
-
self.skynet_name = 'BaseTestService'
|
48
|
-
end
|
49
|
-
|
50
|
-
# Unit Test
|
51
|
-
class BaseTest < Test::Unit::TestCase
|
52
|
-
context RubySkynet::Base do
|
53
|
-
|
54
|
-
context "with server" do
|
55
|
-
setup do
|
56
|
-
RubySkynet.region = @region
|
57
|
-
RubySkynet::Server.start
|
58
|
-
|
59
|
-
@read_timeout = 3.0
|
60
|
-
end
|
61
|
-
|
62
|
-
teardown do
|
63
|
-
RubySkynet::Server.stop
|
64
|
-
end
|
65
|
-
|
66
|
-
context "with client connection" do
|
67
|
-
setup do
|
68
|
-
@client = BaseTestServiceClient.new
|
69
|
-
end
|
70
|
-
|
71
|
-
should "successfully send and receive data" do
|
72
|
-
reply = @client.test1('some' => 'parameters')
|
73
|
-
assert_equal 'test1', reply['result']
|
74
|
-
end
|
75
|
-
|
76
|
-
should "timeout on receive" do
|
77
|
-
request = { 'duration' => @read_timeout + 0.5}
|
78
|
-
|
79
|
-
exception = assert_raise ResilientSocket::ReadTimeout do
|
80
|
-
# Read 4 bytes from server
|
81
|
-
@client.sleep(request, :read_timeout => @read_timeout)
|
82
|
-
end
|
83
|
-
assert_match /Timedout after #{@read_timeout} seconds trying to read/, exception.message
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|