thalassa

A lightweight service registry using Redis inspired by Seaport

npm install thalassa
14 downloads in the last day
79 downloads in the last week
308 downloads in the last month

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.

NPM

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 thalassa

or globally

npm install -g thalassa

Running 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 --debug

or

thalassa-server --debug

Server 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 logging

Server 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.

  1. From the command-line
  2. As a module
  3. 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 --debug

This 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 logging

Client 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 5000ms). 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.

npm loves you