ruby_skynet 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|