Getting Started With Skyring Distributed Timers

T

he very idea of distributed timers is complex. Conceptually is full of race conditions and edge cases. Skyring for Node.js boils the problems space down to a simple to use library and API for building scalable service that need to perform time sensitive, actions. That is a mouthful - Think An email gateway, a web-hook service, auto-dialers for telephony systems. Or in the most practical sense, anytime you might need functionality like setTimeout but needs to survive restarts / crashes; Or are using a language that doesn't support non-blocking timers. Skyring fills that gap, and it is easy to use. We can get something going in less that 20 lines of code.

To start, we just install the skyring package from npm

$ npm install skyring --save

The only external dependency is a running nats instance. The defaults will do. You can certainly run nats as a cluster if you need high availability, but it isn't necessary for this

docker run -d --name=nats -p 4222:4222 nats:latest

Now we can just require skyring and start it.

// index.js
'use strict'

const Skyring = require('skyring')
const HTTP_PORT = +(process.env.PORT || 3000)
const node = new Skyring({
  node: {
    app: 'demo'
  }
}).load().listen( HTTP_PORT )

function onSignal () {
  // trigger triggers a rebalance and shuts down
  node.close((err) => {
    if (err) process.exitCode = 1
  })
}

process.on('SIGINT', onSignal)
process.on('SIGTERM', onSignal)

Pretty simple! We care about two ports here - the port for the http server which we have defaulted to 3000, and the port for the skyring cluster, which defaults to 3456. The default configuration is a 2 node cluster on ports3456 and 3455.

# Start Node 1
$ DEBUG=skyring:* PORT=3001 node index.js
# Start Node 2
$ DEBUG=skyring:* PORT=3002 node index.js --channel:port=3455
skyring


That is it! You just made one of those! You have a running skyring cluster, and we can start making timers.

$ curl -i -XPOST http://localhost:3000/timer -d '{
  "timeout": 5000,
  "data": "hello world!",
  "callback": {
    "transport": "http",
    "method": "post",
    "uri": "http://0.0.0.0:5555/"
  }
}'

If everything goes as planned, you should get back a 201 and a location header to the timer instance if you need to cancel it, or update it.

HTTP/1.1 201 Created
location: /timer/040a8101-9543-40a8-9634-e66de573abbc
Date: Tue, 02 May 2017 03:04:21 GMT
Connection: keep-alive
Content-Length: 0

For this to all come together, you'll want something to handle the timer when it is triggers. We can do that, again, with a simple node echo server that listens on port 5555

// echo.js
'use strict';

const http = require('http')

http.createServer( (req, res) => {
  res.writeHead(200)
  res.end();
  req.pipe(process.stdout)
}).listen(5555);

You can play around with hammering the skyring cluster with requests, stopping/starting one of the nodes, and any pending timers will find their way to a running node. The unit tests do just that! The skyring api is very fast, which means it can be used efficiently from synchronous languages like python or ruby with out the complexity of threading, or introducing bloated async frameworks. Skyring is http <> http which most all languages support out of the box. And with out too much additional work, the default skyring server could be a pretty good web hooks microservice

Skyring: Distributed, reliable timers made easy.
The code for this example can be found here

skyring timers node