Class: Servicy::Server
- Inherits:
-
Object
- Object
- Servicy::Server
- Defined in:
- lib/server/server.rb
Instance Attribute Summary collapse
-
#deactivated_services ⇒ Object
readonly
Returns the value of attribute deactivated_services.
-
#load_balancer ⇒ Object
Returns the value of attribute load_balancer.
-
#services ⇒ Object
readonly
Returns the value of attribute services.
-
#transport ⇒ Object
Returns the value of attribute transport.
Class Method Summary collapse
-
.load(filename) ⇒ Servicy::Server
Loads a server configuration off disk.
Instance Method Summary collapse
-
#find(args = {}) ⇒ Object
Find one or more services based on some search criteria.
-
#find_one(args = {}) ⇒ Object
Find one or more services based on some search criteria.
-
#go! ⇒ Object
Starts the Transport listening (whatever that means for the implementation), gets messages, handles them, and sends replies.
-
#initialize(transport = nil, load_balancer = nil, logger_stream = nil, config_file = nil) ⇒ Server
constructor
Create a new server that is used to register and find Services.
-
#latencies ⇒ Hash{String => Float}
Get a hash of uptimes for each service, where the key is the service name and hostname, joined with a ‘#’, and the value is the uptime for that service on that host.
-
#load!(filename) ⇒ Object
Load a configuration from disk, and override the current one with it.
-
#register(args) ⇒ Object
Register a new service :name is the name of the service you are registering, in inverted-domain format (ie, “com.foo.api”).
-
#reregister(service) ⇒ Object
Removes a service from the deactivated list if it’s there.
-
#save!(filename) ⇒ Object
Save the service configuration to disk.
-
#stop! ⇒ Object
Shut the server down.
-
#unregister(service) ⇒ Object
Un-register a previously registered service.
-
#uptimes ⇒ Hash{String => Float}
Get a hash of uptimes for each service, where the key is the service name and hostname, joined with a ‘#’, and the value is the uptime for that service on that host.
Constructor Details
#initialize(transport = nil, load_balancer = nil, logger_stream = nil, config_file = nil) ⇒ Server
Create a new server that is used to register and find Services. TODO: I should really make this take an options hash, but that will require changing a lot of shit…
11 12 13 14 15 16 17 18 |
# File 'lib/server/server.rb', line 11 def initialize(transport=nil, load_balancer=nil, logger_stream=nil, config_file=nil) @transport = transport || Servicy.config.server.transport @services = [] # All services @deactivated_services = [] # Dead services @load_balancer = load_balancer || Servicy.config.server.load_balancer @config_file = config_file || Servicy.config.server.config_file start_heartbeat_process end |
Instance Attribute Details
#deactivated_services ⇒ Object (readonly)
Returns the value of attribute deactivated_services.
3 4 5 |
# File 'lib/server/server.rb', line 3 def deactivated_services @deactivated_services end |
#load_balancer ⇒ Object
Returns the value of attribute load_balancer.
4 5 6 |
# File 'lib/server/server.rb', line 4 def load_balancer @load_balancer end |
#services ⇒ Object (readonly)
Returns the value of attribute services.
3 4 5 |
# File 'lib/server/server.rb', line 3 def services @services end |
#transport ⇒ Object
Returns the value of attribute transport.
4 5 6 |
# File 'lib/server/server.rb', line 4 def transport @transport end |
Class Method Details
.load(filename) ⇒ Servicy::Server
Loads a server configuration off disk
70 71 72 73 74 |
# File 'lib/server/server.rb', line 70 def self.load(filename) s = Servicy::Server.new s.load!(filename) s end |
Instance Method Details
#find(args = {}) ⇒ Object
Find one or more services based on some search criteria. When finding things, you can search by any single or combination of name, version, and api definition. When searching for name:
find(name: 'com.foobar.baz')
… or for wild-cards if you don’t care who provides a thing
find(name: '.baz')
For searching by version, you can search by absolute version number, minimum, maximum, or a range (by passing min and max together):
# Absolute version
find(version: '1.2.4-p123')
# Min and max as range
find(min_version: '1.2.0', max_version: '2.0.0')
All version numbers must follow the format:
(major).(minor).(revision)(-patch)?
And are compared to one another by converting the version to numbers and comparing those numbers. The version happens by:
(major * 1000) + (minor * 100) + (revision * 10) + (1/patch)
Searching by api can be used to search for a service that provides a certain interface, even if you don’t know what that interface is called. For example, if you don’t know what your authorization service is called, but you know that it takes 2 strings (username and password) and returns either false or a User object, you can search by:
find(api: '.login/2[String,String] -> User|nil')
NOTE The query syntax listed here isn’t working currently. See Servicy::ServiceSearcher for information on how to actually search by api in the current version.
201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/server/server.rb', line 201 def find(args={}) Servicy.logger.info "Finding #{args.inspect}..." searcher = Servicy::ServiceSearcher.new(services - deactivated_services) searcher.filter_by_name args[:name] searcher.filter_by_versions args[:min_version], args[:max_version], args[:version] searcher.filter_by_api args[:api] Servicy.logger.info "Found #{searcher.services.length} providers for #{args.inspect}" searcher.services end |
#find_one(args = {}) ⇒ Object
Find one or more services based on some search criteria. When finding things, you can search by any single or combination of name, version, and api definition. When searching for name:
find(name: 'com.foobar.baz')
… or for wild-cards if you don’t care who provides a thing
find(name: '.baz')
For searching by version, you can search by absolute version number, minimum, maximum, or a range (by passing min and max together):
# Absolute version
find(version: '1.2.4-p123')
# Min and max as range
find(min_version: '1.2.0', max_version: '2.0.0')
All version numbers must follow the format:
(major).(minor).(revision)(-patch)?
And are compared to one another by converting the version to numbers and comparing those numbers. The version happens by:
(major * 1000) + (minor * 100) + (revision * 10) + (1/patch)
Searching by api can be used to search for a service that provides a certain interface, even if you don’t know what that interface is called. For example, if you don’t know what your authorization service is called, but you know that it takes 2 strings (username and password) and returns either false or a User object, you can search by:
find(api: '.login/2[String,String] -> User|nil')
NOTE The query syntax listed here isn’t working currently. See Servicy::ServiceSearcher for information on how to actually search by api in the current version. Return only the first load-balanced instance. If you want load-balancing, use this.
216 217 218 |
# File 'lib/server/server.rb', line 216 def find_one(args={}) load_balancer.next(find(args)) end |
#go! ⇒ Object
Starts the Transport listening (whatever that means for the implementation), gets messages, handles them, and sends replies.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/server/server.rb', line 22 def go! transport.start do || Servicy.logger.info "Received message, #{.inspect}" last_service_count = @services.length result = case . when 'registration' self.register .service Transport::Message.success when 'query' if .only_one temp = [self.find_one(.query)].compact else temp = self.find(.query) end Transport::Message.services temp when 'stats' # Gather stats about the running system, and return it # TODO: Other stats? Transport::Message.statistics(num_services: last_service_count, uptimes: uptimes, latencies: latencies ) else Transport::Message.error("Bad message type, #{.inspect}") end if @services.length > last_service_count Servicy.logger.debug("Saving to #{@config_file}") self.save!(@config_file) Servicy.logger.debug("Saved") last_service_count = @services.length end result end transport.stop end |
#latencies ⇒ Hash{String => Float}
Get a hash of uptimes for each service, where the key is the service name and hostname, joined with a ‘#’, and the value is the uptime for that service on that host. Hash is in the same format, but the values are avg latencies.
230 231 232 |
# File 'lib/server/server.rb', line 230 def latencies services.inject({}) { |h, s| h[s.to_s] = s.avg_latency; h } end |
#load!(filename) ⇒ Object
Load a configuration from disk, and override the current one with it.
148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/server/server.rb', line 148 def load!(filename) json = '' File.open(filename, 'r') do |f| json = f.read end @config_file = filename @services = JSON.parse(json).map do |s| s = s.inject({}) { |h,(k,v)| h[k.to_sym] = v; h } Servicy::Service.new s end end |
#register(args) ⇒ Object
Register a new service
:name is the name of the service you are registering, in
inverted-domain format (ie, "com.foo.api").
:host must be either an IP address or hostname/domain name that is
reachable by the Server instance (and any API consumers, of course.)
:port must be an integer between 1 and 65535, as with :heartbeat_port,
and is the port used to connect to the service provider.
:heartbeat_port is the port to connect to to make sure that things are
still running, and will default to the same as :port.
:version is the version of the API/service that you are registering,
and must be in the "major.minor.revision-patch" format, where the patch
section is optional. For example, "1.0.3-p124". It defaults to,
"1.0.0".
:protocol can be anything you like, but is the protocol used to connect
to the api. It defaults to "HTTP/S", meaning either HTTP or HTTP/TLS
:api can be an array of hashes that describe the API provided. See the
tests and README.md for examples.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/server/server.rb', line 103 def register(args) service = Service.new(args) Servicy.logger.info "Registering service, #{service}..." if service.up? if @services.include? service Servicy.logger.info "Service, #{service}, already exists. Not duplicating" else @services << service Servicy.logger.info "Service, #{service}, up; registered." end return service else Servicy.logger.info "Service, #{service}, down; not registered." return false end end |
#reregister(service) ⇒ Object
Removes a service from the deactivated list if it’s there. No side-effects otherwise.
132 133 134 135 136 |
# File 'lib/server/server.rb', line 132 def reregister(service) if @deactivated_services.delete(service) Servicy.logger.info "Re-registering service, #{service}" end end |
#save!(filename) ⇒ Object
Save the service configuration to disk
140 141 142 143 144 |
# File 'lib/server/server.rb', line 140 def save!(filename) File.open(filename, 'w') do |f| f.write services.map(&:as_json).to_json end end |
#stop! ⇒ Object
Shut the server down
62 63 64 65 |
# File 'lib/server/server.rb', line 62 def stop! transport.stop @heartbeat_thread.kill end |
#unregister(service) ⇒ Object
Un-register a previously registered service. This removes it from the active list of services that are available to searches, etc.
123 124 125 126 127 |
# File 'lib/server/server.rb', line 123 def unregister(service) return if @deactivated_services.include?(service) Servicy.logger.info "De-registering service, #{service}" @deactivated_services << service end |
#uptimes ⇒ Hash{String => Float}
Get a hash of uptimes for each service, where the key is the service name and hostname, joined with a ‘#’, and the value is the uptime for that service on that host.
224 225 226 |
# File 'lib/server/server.rb', line 224 def uptimes services.inject({}) { |h, s| h[s.to_s] = s.uptime; h } end |