sneeze
Easily join SWIM networks. See
http://www.cs.cornell.edu/~asdas/research/dsn02-SWIM.pdf.
This module is used by seneca-mesh
to provide zero-configuration service discovery. A usage example is
provided by the 14 microservice
ramanujan Twitter clone (including a
Docker Swarm configuratiion).
It is also used by the wo hapi plugin for
upstream proxy discovery. There's also a nice example in that repo.
Also included is a very useful terminal monitor so that you can see
the exact status of your network:
Quick Example
The base node serves as the well-known starting point. As other
nodes (A and B) join and leave. All nodes eventually learn about them.
var base = require('sneeze')({isbase:true})
base.join()
var nodeA = require('sneeze')()
nodeA.on('add',console.log)
nodeA.on('remove',console.log)
nodeA.join({name: 'A'})
var nodeB = require('sneeze')()
nodeB.on('add',console.log)
nodeB.on('remove',console.log)
nodeB.join({name: 'B'})
Usage
This module is designed to be used by other modules that want to
expose functionality to a network of peer nodes. It does not provide
communication capabilities. It's sole purpose is to communicate meta
data on startup to other nodes. You use that meta data to establish
communication - it might the host and port on which you are exposing a
web service end point.
Sneeze uses the excellent swim-js
module as the implementation of the SWIM algorithm. Communication
between nodes of the SWIM network is via UDP, so you need to make sure
your network allows UDP transit.
Base nodes
The purpose of a SWIM network is automatic discovery without needing
any configuration or service registries. You can join the network just
by connecting with any single node. Of course, you have to know about
at least one node.
To make this easier, Sneeze provides the concept of base nodes. Base
nodes should be delployed at well-known network locations (maybe using
a fixed domain name in your local DNS). New Sneeze instances can then
join the network simply by connecting to a base node. You can deploy
multiple base nodes for redundancy. They are only used for initial
joining and are not used for service discovery. This means you can
"chaos monkey" your base nodes without causing problems, so long as at
least one is live.
IMPORTANT
The host name of each Sneeze instance is used as part of it's network
wide identification. Be careful to ensure that the host name is
consistent on all machines in your network.
If a Sneeze instance cannot join a network, it will retry a set number
of times, and then give up. Often the failure to join is because the
rest of the network cannot reply to the new Sneeze instance, due to host
name inconsistency. This is common problem with overlay networks that provide load-balancing IPs (for example: Docker Swarm).
Sneeze assumes a flat network structure. Everybody is on the same
network, and can reach everybody else.
Using windows? Sneeze uses some native modules, so make sure to
configure msbuild.
Basic Examples
The test folder
contains some simple examples. See the comments inside the scripts.
See also the more complex examples mentioned above [sneeze].
API
Sneeze(options)
Create a new Sneeze instance, passing in an options object. You can
create multiple Sneeze instances in the same process.
This constructor is exposed by the module:
var Sneeze = require('sneeze')
var sneeze_instance = Sneeze()
See below for the options.
sneeze.join(meta)
Join a SWIM network. The meta
object is meta data about this
instances that will be shared with all other members of the network.
sneeze.members()
Returns an array of member description objects. These are the
currently known and healthy members.
sneeze.leave()
Leave the network. You can also leave just by exiting the process -
SWIM is designed to handle that. This method is just to give you extra
control.
seneca.info
This public member variable is null
until the instance sucessfully
joins a network. It is descriptive object containing meta data about
this instance.
Options
The top level options that you can provide to Sneeze are:
isbase: false
If true
, become a base node.
host: 127.0.0.1
Host name of the instance.
port: 39999
Port of the instance. If not a base node, then this chosen randomly if undefined. Normally, you leave this undefined and let Sneeze work out what port to use. NOTE: this is the SWIM UDP port, not the port you use for communication.
bases: ['127.0.0.1:39999']
The list of base nodes, in the format host:port. The Sneeze instance
will try to join the network by contacting these nodes.
silent: true
If false
, print logs of debugging information about SWIM network
events. Useful if your network configuration is causing headaches.
tag: null
You can have multiple Sneeze networks running at the same time. Each
network can have its own tag. Members with different tags will ignore
each other.
A tag of null
means observe all other members, regardless of
tag. This is what you need for base nodes, a
repl,
or monitoring - see below.
identifier: null
You can provide a unique identifier for your instance. This is
generated automatically if you do not provide one.
v: null
If you leave and rejoin a network, within the same process, using the
same identifier, you should provide different version number v
so
that any changed configuration is disseminated to the network.
monitor: {}
Print a live monitoring table to the terminal. This lists all known
instances, and shows their status. More below.
Monitoring
To turn on monitoring, run an instance (assuming default bases, etc) using:
// file: monitor.js
var Sneeze = require('sneeze')
var sneeze = Sneeze({
monitor: {
active: true
}
})
sneeze.join()
This instance will print a table of all known instances to the
terminal window and update the table dynamically as instances come and
go. It is very useful for sanity checking your network.
The table columns are:
- host: SWIM host and UDP port
- a: number of add events for this node
- r: number of remove events for this node
- s: node status: A: alive, S: suspect, F: failed, U: unknown
- time: milliseconds since the monitor started
- tag: Sneeze network tag
- meta: custom node meta data (see below)
- id: the node identifier
The custom node meta data is picked out from the instance meta data
using JsonPath expressions:
var sneeze = Sneeze({
monitor: {
active: true,
meta: ['foo.bar']
}
})
This will print the value of foo: {bar: 123}
in the instance meta data.
Keys
Ctrl-C
Shutdown the monitor process.
p
Prune failed nodes from the table.
Questions?
@rjrodger