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