thalassa v0.4.1
Thalassa
Thalassa is a lightweight service registry build primarily on node.js, Redis and Axon, inspired by Seaport. Thalassa is actually a system of components primarily geared to enable continuous deployment scenarios through dynamic configuration of HAProxy load balancers and seamless, no connection drop A/B deploys.
This is the central module that includes the server registry and client. The registry uses a ping and expiration type approach. Clients register with the server and pass an optional secondsToExpire property telling the server when it is acceptable to expire the registration if the registration is not updated in that time. There is an internal "reaper" function that runs periodically to reap expired registrations. More on that later.
History
Thalassa is a second generation system, superseding what was otherwise knows as "Spindrift" inside of Pearson. Spindrift leaned heavily on @substack's Seaport module. Incidentally, Seaport was the original inspiration for the aquatic theme of Spindrift and Thalassa.
In Greek mythology, Thalassa was the primeval spirit of the sea. In the fables of Aesop, Thalassa appears as a woman formed of sea water rising up from her native element. Thalassa was depicted in Roman-era mosaics as a woman half submerged in the sea, with crab-claw horns, clothed in bands of seaweed, and holding a ship's oar.1
Installation
npm install thalassaor globally
npm install -g thalassaRunning the Server
The Thalassa server and client may be run from the command line or embedded as a module within your application.
Running from Command Line
Assuming Redis is installed and running, start the thalassa server with default options:
./node_modules/.bin/thalassa-server --debugor
thalassa-server --debugServer Command Line Options
thalassa-server --help
Options:
--host host to bind to [default: "127.0.0.1"]
--port port to bind to for axon socket [default: 5001]
--apihost host to bind to [default: "127.0.0.1"]
--apiport port to bind to for http api [default: 9000]
--redisHost Redis host [default: "127.0.0.1"]
--redisPort Redis port [default: 6379]
--redisDatabase Redis database to select [default: 0]
--reaperFreq Reaper frequency (ms) [default: 2000]
--debug enabled debug loggingServer as an Embedded Module
The same options above (except debug) may be passed by properties set in the opts object. For example using new Thalassa.Server(opts):
var Thalassa = require('thalassa');
var server = new Thalassa.Server({
port: 4444,
apiport: 4445,
host: 'localhost'
});In addition opts.log may be optionally set to your own function to handle logging. opts.log expects this signature: function log (level, message, object){}. level will be one of debug, info, and error. message is a string and object is an optional object with key value pairs. Of opts.log is not passed, the module will be quiet.
Running the Client
The client can be run any of three ways.
- From the command-line
- As a module
- Over HTTP
Running Client from Command Line
Why would you do this? Let's say you have an existing legacy Java application that you'd rather not change. You can create a sister service that invokes the command line client to register the service on it's behalf.
For example, if Thalassa is installed globally (other wise `./node_modules/.bin/thalassa-client):
thalassa-client --register myapp@1.0.0:8080 --debugThis registers the application named my app at version 1.0.0 that's on the current host on port 8080. The client will continue to ping the Thalassa server with updates.
Client Command Line Options
thalassa-client --help
Options:
--host thalassa host [default: "127.0.0.1"]
--port thalassa axon socket port [default: 5001]
--apiport thalassa http api port [default: 9000]
--register name@x.x.x:port,name@x.x.x:port [required]
--secsToExpire default time in seconds for a thalassa registration to be valid [default: 60]
--updateFreq time frequency in ms to ping the thalassa server [default: 20000]
--updateTimeout time in ms to wait for a registrion request to respond [default: 2500]
--debug enabled debug loggingClient as an Embedded Module
Using the client from within a node.js application to register your service is simple. Pass options via the opts object like new Thalassa.Client(opts):
var Thalassa = require('thalassa');
var client = new Thalassa.Client({
port: 4444,
apiport: 4445,
host: 'localhost'
});
client.register('myapp', '1.0.0', 8080);
// start reporting registrations to the server
client.start();
// stop reporting registrations to the server
client.stop();opts.log may be passed just like the server.
updateSuccessful and updateFailed Events
The client will periodically check in with the Thalassa server according to opts.updateFreq (default 20000ms). Each registration will product a updateSuccessful or updateFailed event to be emitted.
client.on('updateSuccessful', function () {}); client.on('updateFailed', function (error) {});
Subscriptions and online and offline Events
If running as a module, you also have access to subscribe to online and offline events of certain applications. For example:
client.subscribe('myapp', '1.0.0');
client.on('online', function (registration) {});
client.on('offline', function (registration) {});Alternatively for all versions of myapp:
client.subscribe('myapp');Or every service registration:
client.subscribe();Querying Registrations
Also as a module, you can use the client API to query for registrations.
client.getRegistrations('myapp', '1.0.0', function (err, registrations) {
// registrations is an Array of Registrations
}See the HTTP API section for the Registration structure.
Metadata
You can also pass metadata with any registration as a fourth parameter. This can be any javascript object with properties. For example:
var meta = {
az: 'use1a',
size: 'm1.large',
foo: {
bar: 'baz'
}
};
client.register('myapp', '1.0.0', 8080, meta)HTTP Client
The Thalassa server exposes a simple HTTP API so it's not necessary to use the node.js client and any application that's capable of calling HTTP can participate as an application in the system. See the HTTP API.
HTTP API
GET /registrations/{name}/{version}
GET /registrations/{name}
GET /registrations
Return Registrations[] of all registrations for the optionally provided name and version. /registrations returns everything.
Registration is defined in it's own module thalassa-registrations. A typical registration looks like this:
{
"name": "myapp",
"version": "1.0.0",
"host": "192.168.8.106",
"port": 8080,
"lastKnown": 1378682020883,
"meta": {
"hostname": "mb-mbp.local",
"pid": 66593,
"registered": 1378682010864
},
"id": "/myapp/1.0.0/192.168.8.106/8080"
}All times are in Unix time since epoch form.
The Thalassa client will automatically add meta.pid and the server will automatically add registered and hostname if not provided. If a hostname is not provided by the client, the IP will be used instead. Additionally, the Thalassa client will automatically set hostname to require('os').hostname().
POST /registrations/{name}/{version}/{host}/{port}
Create or update a registration.
The BODY of the POST should be application/json and will be added to meta.
Additionally meta.secondsToExpire should be set to explicitly set the expiration time of the registration. In essence you are telling the Thalassa server, if you don't hear back from me in so many seconds, expire my registration and fire an offline event. This properly allows the client to tune how often they poll balanced with how long they are willing to accept stale registration data. If you set secondsToExpire to 300 then you may poll every ten minutes, but if your service goes down or is underplayed, consumers won't know it for at most 300 seconds.
DELETE /registrations/{name}/{version}/{host}/{port}
Explicitly delete a registration, causing an offline event.
STATUS status
Returns basic status with thalassa version, memory usage, uptime and registrationsPerSecond stats.
Known Limitations and Roadmap
Thalassa currently doesn't implement any type of authentication or authorization and at this point expects to be running on a trusted private network. This will be addressed in the future. Ultimately auth should be extensible and customizable. Suggestions and pull requests welcome!
License
Licensed under Apache 2.0. See LICENSE file.
