ruby_skynet 1.3.0.alpha3 → 2.0.0.rc1
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/lib/ruby_skynet.rb +5 -11
- data/lib/ruby_skynet/client.rb +15 -8
- data/lib/ruby_skynet/common.rb +54 -14
- data/lib/ruby_skynet/connection.rb +56 -16
- data/lib/ruby_skynet/doozer/service_registry.rb +14 -3
- data/lib/ruby_skynet/railties/ruby_skynet.rake +7 -10
- data/lib/ruby_skynet/ruby_skynet.rb +2 -9
- data/lib/ruby_skynet/server.rb +62 -75
- data/lib/ruby_skynet/service.rb +1 -4
- data/lib/ruby_skynet/version.rb +1 -1
- data/lib/ruby_skynet/zookeeper/service_registry.rb +14 -3
- metadata +32 -28
- data/Gemfile +0 -20
- data/Gemfile.lock +0 -45
- data/Rakefile +0 -44
- data/lib/ruby_skynet/base.rb +0 -47
- data/lib/ruby_skynet/static_service_registry.rb +0 -75
- data/ruby_skynet.gemspec +0 -25
- data/test/client_test.rb +0 -120
- data/test/service_registry_test.rb +0 -122
- data/test/service_test.rb +0 -71
- data/test/zookeeper_registry_test.rb +0 -209
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf65f91389e27d97e04be4be8a1ef49700bb245b
|
4
|
+
data.tar.gz: 6b26fea72828375f49fff798c931744951747698
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20a409544353b4634fde2fb83595e0998ef808383591ad08692bf5422d6bbf07995e1bf781b5a698ceeb7d1e1ec18d840eb8ef98b9f6939d27273a8c9324574c
|
7
|
+
data.tar.gz: 8b049acd60bc68157ee6c632d7f44273fa366199c6bacbd6a0ef703c02d56190f1a1cec7471957452c453d07fd99a426efdba7842e6ca51383dc2abcaff6150a
|
data/lib/ruby_skynet.rb
CHANGED
@@ -2,10 +2,8 @@ require 'semantic_logger'
|
|
2
2
|
require 'ruby_skynet/exceptions'
|
3
3
|
require 'ruby_skynet/version'
|
4
4
|
require 'ruby_skynet/ruby_skynet'
|
5
|
-
require 'ruby_skynet/zookeeper'
|
6
5
|
|
7
6
|
module RubySkynet
|
8
|
-
autoload :Base, 'ruby_skynet/base'
|
9
7
|
autoload :Common, 'ruby_skynet/common'
|
10
8
|
autoload :Connection, 'ruby_skynet/connection'
|
11
9
|
autoload :Client, 'ruby_skynet/client'
|
@@ -31,18 +29,14 @@ module RubySkynet
|
|
31
29
|
begin
|
32
30
|
require 'ruby_doozer'
|
33
31
|
require 'ruby_skynet/doozer/service_registry'
|
34
|
-
|
35
|
-
# Shortcuts to loaded Registry classes
|
36
|
-
ServiceRegistry = RubySkynet::Doozer::ServiceRegistry
|
37
|
-
CachedRegistry = Doozer::CachedRegistry
|
38
|
-
Registry = Doozer::Registry
|
39
32
|
rescue LoadError
|
40
|
-
|
41
|
-
|
42
|
-
# Use Static Service Registry
|
43
|
-
ServiceRegistry = RubySkynet::StaticServiceRegistry
|
33
|
+
raise LoadError, "Load either the 'zookeeper' or 'ruby_doozer' gem prior to loading RubySkynet. 'zookeeper' is preferred"
|
44
34
|
end
|
45
35
|
|
36
|
+
# Shortcuts to loaded Registry classes
|
37
|
+
ServiceRegistry = RubySkynet::Doozer::ServiceRegistry
|
38
|
+
CachedRegistry = Doozer::CachedRegistry
|
39
|
+
Registry = Doozer::Registry
|
46
40
|
end
|
47
41
|
end
|
48
42
|
|
data/lib/ruby_skynet/client.rb
CHANGED
@@ -8,8 +8,9 @@ require 'bson'
|
|
8
8
|
#
|
9
9
|
module RubySkynet
|
10
10
|
class Client
|
11
|
-
include
|
11
|
+
include Common
|
12
12
|
|
13
|
+
# Allows this instance of the client to use different versions or regions.
|
13
14
|
attr_reader :skynet_name, :skynet_version, :skynet_region
|
14
15
|
|
15
16
|
# Version of the Skynet service to use
|
@@ -83,14 +84,20 @@ module RubySkynet
|
|
83
84
|
# Skynet requires BSON RPC Calls to have the following format:
|
84
85
|
# https://github.com/skynetservices/skynet/blob/master/protocol.md
|
85
86
|
request_id = BSON::ObjectId.new.to_s
|
86
|
-
|
87
|
-
# Obtain list of servers implementing this service in order of priority
|
88
|
-
servers = ::RubySkynet.service_registry.servers_for(skynet_name, skynet_version, skynet_region)
|
89
|
-
|
90
87
|
logger.tagged request_id do
|
91
88
|
logger.benchmark_info "Called Skynet Service: #{skynet_name}.#{method_name}" do
|
92
|
-
|
93
|
-
|
89
|
+
retries = 0
|
90
|
+
# If it cannot connect to a server, try a different server
|
91
|
+
begin
|
92
|
+
Connection.with_connection(::RubySkynet.service_registry.server_for(skynet_name, skynet_version, skynet_region), connection_params) do |connection|
|
93
|
+
connection.rpc_call(request_id, skynet_name, method_name, parameters)
|
94
|
+
end
|
95
|
+
rescue ResilientSocket::ConnectionFailure => exc
|
96
|
+
if (retries < 3) && exc.cause.is_a?(Errno::ECONNREFUSED)
|
97
|
+
retries += 1
|
98
|
+
retry
|
99
|
+
end
|
100
|
+
# TODO rescue ServiceUnavailable retry x times until the service becomes available
|
94
101
|
end
|
95
102
|
end
|
96
103
|
end
|
@@ -104,7 +111,7 @@ module RubySkynet
|
|
104
111
|
#
|
105
112
|
# Define the method if the call was successful and no other thread has
|
106
113
|
# already created the method
|
107
|
-
if result[:exception].nil? && !self.class.method_defined?(method)
|
114
|
+
if !result.nil? && result[:exception].nil? && !self.class.method_defined?(method)
|
108
115
|
self.class.send(:define_method, method) {|*args| call(method, *args)}
|
109
116
|
end
|
110
117
|
result
|
data/lib/ruby_skynet/common.rb
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
require '
|
1
|
+
require 'stringio'
|
2
2
|
require 'socket'
|
3
|
+
require 'semantic_logger'
|
4
|
+
require 'bson'
|
3
5
|
|
4
6
|
module RubySkynet
|
5
7
|
module Common
|
6
8
|
|
9
|
+
BINARY_ENCODING = Encoding.find("binary")
|
10
|
+
|
7
11
|
# Returns a BSON document read from the socket.
|
8
12
|
# Returns nil if the operation times out or if a network
|
9
13
|
# connection failure occurs
|
10
14
|
def self.read_bson_document(socket)
|
11
|
-
bytebuf = BSON::ByteBuffer.new
|
12
15
|
# Read 4 byte size of following BSON document
|
13
|
-
bytes = socket.read(4)
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
if bytes = socket.read(4)
|
17
|
+
bytes.force_encoding(BINARY_ENCODING)
|
18
|
+
# Read BSON document
|
19
|
+
sz = bytes.unpack("V")[0]
|
20
|
+
raise "Invalid Data received from server:#{bytes.inspect}" unless sz
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
bytebuf.append!(bytes)
|
23
|
-
bytebuf.append!(socket.read(sz - 4))
|
24
|
-
raise "Socket is not returning #{sz} requested bytes. #{bytebuf.length} bytes returned" unless sz == bytebuf.length
|
25
|
-
return BSON.deserialize(bytebuf)
|
22
|
+
bytes << socket.read(sz - 4)
|
23
|
+
raise "Socket is not returning #{sz} requested bytes. #{bytes.length} bytes returned" unless sz == bytes.length
|
24
|
+
Hash.from_bson(StringIO.new(bytes))
|
25
|
+
end
|
26
26
|
end
|
27
27
|
|
28
28
|
# Returns the local ip address being used by this machine to talk to the
|
@@ -31,5 +31,45 @@ module RubySkynet
|
|
31
31
|
@@local_ip_address ||= ::UDPSocket.open {|s| s.connect(remote_ip, 1); s.addr.last }
|
32
32
|
end
|
33
33
|
|
34
|
+
def self.included(base)
|
35
|
+
base.extend ClassMethods
|
36
|
+
base.class_eval do
|
37
|
+
include SemanticLogger::Loggable
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module ClassMethods
|
42
|
+
# Name of this service to Register with Skynet
|
43
|
+
# Default: class name
|
44
|
+
def skynet_name
|
45
|
+
@skynet_name ||= name.gsub('::', '.')
|
46
|
+
end
|
47
|
+
|
48
|
+
def skynet_name=(skynet_name)
|
49
|
+
@skynet_name = skynet_name
|
50
|
+
end
|
51
|
+
|
52
|
+
# Version of this service to register with Skynet
|
53
|
+
# Default: nil
|
54
|
+
def skynet_version
|
55
|
+
@skynet_version ||= nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def skynet_version=(skynet_version)
|
59
|
+
@skynet_version = skynet_version
|
60
|
+
end
|
61
|
+
|
62
|
+
# Region within which this service is defined
|
63
|
+
# Default: RubySkynet.region
|
64
|
+
def skynet_region
|
65
|
+
@skynet_region || ::RubySkynet.region
|
66
|
+
end
|
67
|
+
|
68
|
+
def skynet_region=(skynet_region)
|
69
|
+
@skynet_region = skynet_region
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
34
74
|
end
|
35
75
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'bson'
|
2
|
+
require 'gene_pool'
|
2
3
|
require 'thread_safe'
|
3
4
|
require 'resilient_socket'
|
4
5
|
require 'sync_attr'
|
@@ -16,6 +17,20 @@ module RubySkynet
|
|
16
17
|
# Returns the underlying socket being used by a Connection instance
|
17
18
|
attr_reader :socket
|
18
19
|
|
20
|
+
# Default Pool configuration
|
21
|
+
sync_cattr_accessor :pool_config do
|
22
|
+
{
|
23
|
+
:pool_size => 30, # Maximum number of connections to any one server
|
24
|
+
:warn_timeout => 2, # Log a warning if no connections are available after the :warn_timeout seconds
|
25
|
+
:timeout => 10, # Raise a Timeout exception if no connections are available after the :timeout seconds
|
26
|
+
:idle_timeout => 600, # Renew a connection if it has been idle for this period of time
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
# For each server there is a connection pool keyed on the
|
31
|
+
# server address: 'host:port'
|
32
|
+
@@connection_pools = ThreadSafe::Hash.new
|
33
|
+
|
19
34
|
# Returns a new RubySkynet connection to the server
|
20
35
|
#
|
21
36
|
# Parameters:
|
@@ -35,8 +50,8 @@ module RubySkynet
|
|
35
50
|
# :connect_retry_interval [Float]
|
36
51
|
# Number of seconds between connection retry attempts after the first failed attempt
|
37
52
|
# Default: 0.5
|
38
|
-
def initialize(
|
39
|
-
|
53
|
+
def initialize(server, params = {})
|
54
|
+
self.logger = SemanticLogger["#{self.class.name} [#{server}]"]
|
40
55
|
|
41
56
|
# User configurable options
|
42
57
|
params[:read_timeout] ||= 60
|
@@ -76,12 +91,12 @@ module RubySkynet
|
|
76
91
|
client_handshake = { 'clientid' => client_id }
|
77
92
|
logger.debug "Sending Client Handshake"
|
78
93
|
logger.trace 'Client Handshake', client_handshake
|
79
|
-
socket.write(
|
94
|
+
socket.write(client_handshake.to_bson)
|
80
95
|
end
|
81
96
|
|
82
97
|
# To prevent strange issues if user incorrectly supplies server names
|
83
|
-
params.delete(:
|
84
|
-
params[:
|
98
|
+
params.delete(:servers)
|
99
|
+
params[:server] = server
|
85
100
|
|
86
101
|
@socket = ResilientSocket::TCPClient.new(params)
|
87
102
|
end
|
@@ -116,12 +131,12 @@ module RubySkynet
|
|
116
131
|
|
117
132
|
logger.debug "Sending Header"
|
118
133
|
logger.trace 'Header', header
|
119
|
-
socket.write(
|
134
|
+
socket.write(header.to_bson)
|
120
135
|
|
121
136
|
# The parameters are placed in the request object in BSON serialized form
|
122
137
|
request = {
|
123
138
|
'clientid' => socket.user_data[:client_id],
|
124
|
-
'in' => BSON::Binary.new(
|
139
|
+
'in' => BSON::Binary.new(parameters.to_bson),
|
125
140
|
'method' => method_name.to_s,
|
126
141
|
'requestinfo' => {
|
127
142
|
'requestid' => request_id,
|
@@ -137,7 +152,7 @@ module RubySkynet
|
|
137
152
|
logger.debug "Sending Request"
|
138
153
|
logger.trace 'Request', request
|
139
154
|
logger.trace 'Parameters:', parameters
|
140
|
-
socket.write(
|
155
|
+
socket.write(request.to_bson)
|
141
156
|
|
142
157
|
# Since Send does not affect state on the server we can also retry reads
|
143
158
|
if idempotent
|
@@ -186,7 +201,7 @@ module RubySkynet
|
|
186
201
|
|
187
202
|
# Return Value
|
188
203
|
# The return value is inside the response object, it's a byte array of it's own and needs to be deserialized
|
189
|
-
result =
|
204
|
+
result = Hash.from_bson(StringIO.new(response['out'].data))
|
190
205
|
logger.trace 'Return Value', result
|
191
206
|
result
|
192
207
|
end
|
@@ -194,19 +209,44 @@ module RubySkynet
|
|
194
209
|
|
195
210
|
# Execute the supplied block with a connection from the pool
|
196
211
|
def self.with_connection(server, params={}, &block)
|
197
|
-
|
198
|
-
begin
|
199
|
-
conn = new(server, params)
|
200
|
-
block.call(conn)
|
201
|
-
ensure
|
202
|
-
conn.close if conn
|
203
|
-
end
|
212
|
+
(@@connection_pools[server] ||= new_connection_pool(server, params)).with_connection(&block)
|
204
213
|
end
|
205
214
|
|
206
215
|
def close
|
207
216
|
@socket.close if @socket
|
208
217
|
end
|
209
218
|
|
219
|
+
########################
|
220
|
+
protected
|
221
|
+
|
222
|
+
# Returns a new connection pool for the specified server
|
223
|
+
def self.new_connection_pool(server, params={})
|
224
|
+
# Connection pool configuration options
|
225
|
+
config = pool_config.dup
|
226
|
+
|
227
|
+
logger = SemanticLogger::Logger.new("#{self.class.name} [#{server}]")
|
228
|
+
|
229
|
+
# Method to call to close idle connections
|
230
|
+
config[:close_proc] = :close
|
231
|
+
config[:logger] = logger
|
232
|
+
|
233
|
+
pool = GenePool.new(pool_config) do
|
234
|
+
new(server, params)
|
235
|
+
end
|
236
|
+
|
237
|
+
# Cleanup corresponding connection pool when a server terminates
|
238
|
+
RubySkynet.service_registry.on_server_removed(server) do
|
239
|
+
pool = @@connection_pools.delete(server)
|
240
|
+
# Cannot close all the connections since they could still be in use
|
241
|
+
pool.remove_idle(0) if pool
|
242
|
+
#pool.close if pool
|
243
|
+
logger.debug "Connection pool released"
|
244
|
+
end
|
245
|
+
|
246
|
+
pool
|
247
|
+
end
|
248
|
+
|
210
249
|
end
|
250
|
+
|
211
251
|
end
|
212
252
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'semantic_logger'
|
2
2
|
require 'thread_safe'
|
3
|
+
require 'gene_pool'
|
3
4
|
require 'resolv'
|
4
5
|
|
5
6
|
#
|
@@ -61,6 +62,18 @@ module RubySkynet
|
|
61
62
|
@registry.delete("#{name}/#{version}/#{region}/#{hostname}/#{port}")
|
62
63
|
end
|
63
64
|
|
65
|
+
# Return a server that implements the specified service
|
66
|
+
def server_for(name, version='*', region=RubySkynet.region)
|
67
|
+
if servers = servers_for(name, version, region)
|
68
|
+
# Randomly select one of the servers offering the service
|
69
|
+
servers[rand(servers.size)]
|
70
|
+
else
|
71
|
+
msg = "No servers available for service: #{name} with version: #{version} in region: #{region}"
|
72
|
+
logger.warn msg
|
73
|
+
raise ServiceUnavailable.new(msg)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
64
77
|
# Returns [Array<String>] a list of servers implementing the requested service
|
65
78
|
def servers_for(name, version='*', region=RubySkynet.region)
|
66
79
|
if version == '*'
|
@@ -73,11 +86,9 @@ module RubySkynet
|
|
73
86
|
end
|
74
87
|
end
|
75
88
|
end
|
76
|
-
|
89
|
+
if server_infos = @cache["#{name}/#{version}/#{region}"]
|
77
90
|
server_infos.first.servers
|
78
91
|
end
|
79
|
-
raise ServiceUnavailable.new("No servers available for service: #{name} with version: #{version} in region: #{region}") unless servers
|
80
|
-
servers
|
81
92
|
end
|
82
93
|
|
83
94
|
# Invokes registered callbacks when a specific server is shutdown or terminates
|
@@ -6,28 +6,25 @@ namespace :ruby_skynet do
|
|
6
6
|
# so skip it here under Rails
|
7
7
|
unless defined?(Rails)
|
8
8
|
# Environment to use in config file
|
9
|
+
# Defaults to Rails.env
|
9
10
|
environment = ENV['SKYNET_ENV']
|
10
11
|
|
11
12
|
# Environment to use in config file
|
13
|
+
# Defaults to config/ruby_skynet.yml
|
12
14
|
cfg_file = ENV['SKYNET_CONFIG']
|
13
15
|
|
14
16
|
# Load the configuration file
|
15
17
|
RubySkynet.configure!(cfg_file, environment)
|
16
18
|
end
|
17
19
|
|
18
|
-
|
19
|
-
RubySkynet.services
|
20
|
-
|
21
|
-
RubySkynet::Server.load_services
|
22
|
-
|
20
|
+
server = nil
|
23
21
|
begin
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
server = RubySkynet::Server.new
|
23
|
+
server.register_services_in_path
|
24
|
+
server.wait_until_server_stops
|
27
25
|
ensure
|
28
|
-
|
26
|
+
server.close if server
|
29
27
|
end
|
30
|
-
|
31
28
|
end
|
32
29
|
|
33
30
|
end
|
@@ -2,6 +2,7 @@ require 'sync_attr'
|
|
2
2
|
|
3
3
|
module RubySkynet
|
4
4
|
include SyncAttr
|
5
|
+
@@config = nil
|
5
6
|
|
6
7
|
# Returns the default region for all Ruby Skynet Clients and Services
|
7
8
|
def self.region
|
@@ -51,7 +52,7 @@ module RubySkynet
|
|
51
52
|
# By default it connects to a local ZooKeeper instance
|
52
53
|
# Use .configure! to supply a configuration file with any other settings
|
53
54
|
sync_cattr_reader :service_registry do
|
54
|
-
ServiceRegistry.new
|
55
|
+
ServiceRegistry.new
|
55
56
|
end
|
56
57
|
|
57
58
|
# Returns the current Registry Config information
|
@@ -69,11 +70,6 @@ module RubySkynet
|
|
69
70
|
@@service_registry = service_registry
|
70
71
|
end
|
71
72
|
|
72
|
-
# DEPRECATED - Use RubySkynet.service_registry
|
73
|
-
def self.services
|
74
|
-
@@service_registry
|
75
|
-
end
|
76
|
-
|
77
73
|
# Load the Configuration information from a YAML file
|
78
74
|
# filename:
|
79
75
|
# Name of file to read.
|
@@ -104,7 +100,4 @@ module RubySkynet
|
|
104
100
|
config.each_pair {|k,v| warn "Ignoring unknown RubySkynet config option #{k} => #{v}"}
|
105
101
|
end
|
106
102
|
|
107
|
-
# Initialize internal class variable
|
108
|
-
@@config = nil
|
109
|
-
|
110
103
|
end
|
data/lib/ruby_skynet/server.rb
CHANGED
@@ -9,73 +9,8 @@ module RubySkynet
|
|
9
9
|
class Server
|
10
10
|
include SemanticLogger::Loggable
|
11
11
|
|
12
|
-
@@server = nil
|
13
|
-
@@services = ThreadSafe::Hash.new
|
14
|
-
|
15
|
-
# Start a single instance of the server
|
16
|
-
def self.start(start_port = nil, ip_address = nil)
|
17
|
-
@@server ||= new(start_port, ip_address)
|
18
|
-
|
19
|
-
# Stop the skynet server on shutdown
|
20
|
-
# To ensure services are de-registered in the service registry
|
21
|
-
at_exit do
|
22
|
-
::RubySkynet::Server.stop
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Stop the single instance of the server
|
27
|
-
def self.stop
|
28
|
-
@@server.finalize if @@server
|
29
|
-
@@server = nil
|
30
|
-
end
|
31
|
-
|
32
|
-
# Is the single instance of the server running
|
33
|
-
def self.running?
|
34
|
-
(@@server != nil) && @@server.running?
|
35
|
-
end
|
36
|
-
|
37
|
-
# Wait forever until the running server stops
|
38
|
-
def self.wait_until_server_stops
|
39
|
-
(@@server != nil) && @@server.wait_until_server_stops
|
40
|
-
end
|
41
|
-
|
42
|
-
# Services currently loaded and available at this server when running
|
43
|
-
def self.services
|
44
|
-
@@services
|
45
|
-
end
|
46
|
-
|
47
|
-
# Registers a Service Class as being available at this host and port
|
48
|
-
def self.register_service(klass)
|
49
|
-
raise InvalidServiceException.new("#{klass.inspect} is not a RubySkynet::Service") unless klass.respond_to?(:skynet_name) && klass.respond_to?(:skynet_version) && klass.respond_to?(:skynet_region)
|
50
|
-
|
51
|
-
previous_klass = @@services[klass.skynet_name]
|
52
|
-
if previous_klass && (previous_klass.name != klass.name)
|
53
|
-
logger.warn("Service with name: #{klass.skynet_name} is already registered to a different implementation:#{previous_klass.name}")
|
54
|
-
end
|
55
|
-
@@services[klass.skynet_name] = klass
|
56
|
-
@@server.register_service(klass) if @@server
|
57
|
-
end
|
58
|
-
|
59
|
-
# De-register service
|
60
|
-
def self.deregister_service(klass)
|
61
|
-
raise InvalidServiceException.new("#{klass.inspect} is not a RubySkynet::Service") unless klass.respond_to?(:skynet_name) && klass.respond_to?(:skynet_version) && klass.respond_to?(:skynet_region)
|
62
|
-
|
63
|
-
@@server.deregister_service(klass) if @@server
|
64
|
-
@@services.delete(klass.skynet_name)
|
65
|
-
end
|
66
|
-
|
67
|
-
# Load and register all services found in the supplied path and it's sub-directories
|
68
|
-
def self.load_services
|
69
|
-
RubySkynet::Server.logger.benchmark_info "Loaded Skynet Services" do
|
70
|
-
# Load services
|
71
|
-
Dir.glob("#{RubySkynet.services_path}/**/*.rb").each do |path|
|
72
|
-
load path
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
12
|
# The actual port the server is running at
|
78
|
-
attr_reader :hostname, :port
|
13
|
+
attr_reader :hostname, :port, :services
|
79
14
|
|
80
15
|
# Start the server so that it can start taking RPC calls
|
81
16
|
# Returns false if the server is already running
|
@@ -101,16 +36,16 @@ module RubySkynet
|
|
101
36
|
# Start Server listener thread
|
102
37
|
@listener_thread = Thread.new { run }
|
103
38
|
|
104
|
-
#
|
105
|
-
|
39
|
+
# Array[RubySkynet::Service] List of services registered with this server instance
|
40
|
+
@services = ThreadSafe::Hash.new
|
106
41
|
end
|
107
42
|
|
108
|
-
def
|
43
|
+
def close
|
109
44
|
@server.close if @server
|
110
45
|
logger.info "Skynet Server Stopped"
|
111
46
|
|
112
47
|
# Deregister services hosted by this server
|
113
|
-
|
48
|
+
@services.each_value do |klass|
|
114
49
|
deregister_service(klass) rescue nil
|
115
50
|
end
|
116
51
|
logger.info "Skynet Services De-registered"
|
@@ -128,19 +63,59 @@ module RubySkynet
|
|
128
63
|
|
129
64
|
# Registers a Service Class as being available at this server
|
130
65
|
def register_service(klass)
|
66
|
+
raise InvalidServiceException.new("#{klass.inspect} is not a RubySkynet::Service") unless klass.respond_to?(:skynet_name) && klass.respond_to?(:skynet_version) && klass.respond_to?(:skynet_region)
|
67
|
+
|
68
|
+
previous_klass = @services[klass.skynet_name]
|
69
|
+
if previous_klass && (previous_klass.name != klass.name)
|
70
|
+
logger.warn("Service with name: #{klass.skynet_name} is already registered to a different implementation:#{previous_klass.name}")
|
71
|
+
end
|
72
|
+
@services[klass.skynet_name] = klass
|
73
|
+
|
131
74
|
logger.info "Registering Service: #{klass.name} with name: #{klass.skynet_name}"
|
132
75
|
::RubySkynet.service_registry.register_service(klass.skynet_name, klass.skynet_version || 1, klass.skynet_region, @hostname, @port)
|
133
76
|
end
|
134
77
|
|
135
78
|
# De-register service from this server
|
136
79
|
def deregister_service(klass)
|
80
|
+
raise InvalidServiceException.new("#{klass.inspect} is not a RubySkynet::Service") unless klass.respond_to?(:skynet_name) && klass.respond_to?(:skynet_version) && klass.respond_to?(:skynet_region)
|
81
|
+
|
137
82
|
logger.info "De-registering Service: #{klass.name} with name: #{klass.skynet_name}"
|
138
83
|
::RubySkynet.service_registry.deregister_service(klass.skynet_name, klass.skynet_version || 1, klass.skynet_region, @hostname, @port)
|
84
|
+
@services.delete(klass.skynet_name)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Loads and registers all services found in the supplied path and it's sub-directories
|
88
|
+
# Returns [RubySkynet::Service] the list of Services registered
|
89
|
+
def register_services_in_path(path=RubySkynet.services_path)
|
90
|
+
logger.benchmark_info "Loaded Skynet Services" do
|
91
|
+
# Load services
|
92
|
+
klasses = []
|
93
|
+
Dir.glob("#{path}/**/*.rb").each do |filename|
|
94
|
+
partial = filename.sub(path,'').sub('.rb', '')
|
95
|
+
load filename
|
96
|
+
camelized = partial.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
97
|
+
begin
|
98
|
+
klass = constantize(camelized)
|
99
|
+
# Register the service
|
100
|
+
register_service(klass)
|
101
|
+
klasses << klass
|
102
|
+
rescue Exception => exc
|
103
|
+
p exc
|
104
|
+
raise "Expected to find class #{camelized} in file #{filename}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
klasses
|
108
|
+
end
|
139
109
|
end
|
140
110
|
|
141
111
|
############################################################################
|
142
112
|
protected
|
143
113
|
|
114
|
+
# Re-Register services hosted by this server in the registry
|
115
|
+
def re_register_services_in_registry
|
116
|
+
@services.each_value {|klass| register_service(klass)}
|
117
|
+
end
|
118
|
+
|
144
119
|
def run
|
145
120
|
logger.info("Starting listener on #{hostname}:#{port}")
|
146
121
|
loop do
|
@@ -168,7 +143,7 @@ module RubySkynet
|
|
168
143
|
'registered' => true,
|
169
144
|
'clientid' => BSON::ObjectId.new.to_s
|
170
145
|
}
|
171
|
-
client.write(
|
146
|
+
client.write(handshake.to_bson)
|
172
147
|
Common.read_bson_document(client)
|
173
148
|
|
174
149
|
while(header = Common.read_bson_document(client)) do
|
@@ -185,7 +160,7 @@ module RubySkynet
|
|
185
160
|
request = Common.read_bson_document(client)
|
186
161
|
logger.trace 'Request', request
|
187
162
|
break unless request
|
188
|
-
params =
|
163
|
+
params = Hash.from_bson(StringIO.new(request['in'].data))
|
189
164
|
logger.trace 'Parameters', params
|
190
165
|
|
191
166
|
reply = begin
|
@@ -198,11 +173,11 @@ module RubySkynet
|
|
198
173
|
if reply
|
199
174
|
logger.debug "Sending Header"
|
200
175
|
# For this test we just send back the received header
|
201
|
-
client.write(
|
176
|
+
client.write(header.to_bson)
|
202
177
|
|
203
178
|
logger.debug "Sending Reply"
|
204
179
|
logger.trace 'Reply', reply
|
205
|
-
client.write(
|
180
|
+
client.write({'out' => BSON::Binary.new(reply.to_bson)}.to_bson)
|
206
181
|
else
|
207
182
|
logger.debug "Closing client since no reply is being sent back"
|
208
183
|
break
|
@@ -221,7 +196,7 @@ module RubySkynet
|
|
221
196
|
def on_message(skynet_name, method, params)
|
222
197
|
logger.benchmark_info("Skynet Call: #{skynet_name}##{method}") do
|
223
198
|
logger.trace "Method Call: #{method} with parameters:", params
|
224
|
-
klass =
|
199
|
+
klass = services[skynet_name]
|
225
200
|
raise "Invalid Skynet RPC call, service: #{skynet_name} is not available at this server" unless klass
|
226
201
|
# TODO Use pool of services
|
227
202
|
service = klass.new
|
@@ -230,5 +205,17 @@ module RubySkynet
|
|
230
205
|
end
|
231
206
|
end
|
232
207
|
|
208
|
+
# Returns the supplied camel_cased string as it's class
|
209
|
+
def constantize(camel_cased_word)
|
210
|
+
names = camel_cased_word.split('::')
|
211
|
+
names.shift if names.empty? || names.first.empty?
|
212
|
+
|
213
|
+
constant = Object
|
214
|
+
names.each do |name|
|
215
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
216
|
+
end
|
217
|
+
constant
|
218
|
+
end
|
219
|
+
|
233
220
|
end
|
234
221
|
end
|