Skip to content

Instantly share code, notes, and snippets.

@digitaldreamer
Created October 25, 2019 06:50
Show Gist options
  • Save digitaldreamer/efc69f879aa706a39b398b7e6eb9aea0 to your computer and use it in GitHub Desktop.
Save digitaldreamer/efc69f879aa706a39b398b7e6eb9aea0 to your computer and use it in GitHub Desktop.
Grand Central documentation

GrandCentral Distributed Messaging

Grand Central is the messaging service that will distribute information between the applications.

The central concept revolves around Topics and Channels to support pubsub Event distribution.

Glossary

  • Topic - is a unique identifier for an Event stream. Publishers POST an event to a topic. Topics will send data to every registered Channel.
  • Channel - are the distribution streams for a Consumer to receive data that is posted to a Topic. A topic can have one or more registered Channels/Consumers.
  • Event - is the data that is published on a Topic.
  • Publisher - is a creator of data. A publisher sends Events to Topic.
  • Consumer - reads/receives Event data from a Channel.

API

The server supports the following endpoints

POST /pub

This is the endpoint for producers to publish new event data that will get distributed through channels.

The data part of the payload is free-form and can be any valid JSON. The event data that will be sent to the channel will be the same that is published.

There is no need to register new publishers, simply POST data to the /pub endpoint.

{
    "topic": "button",
    "event": {
        "type": "press",
        "publisher": "monitor",
        "data": {
            "pressed": true|false
        }
    }
}

POST /channels

This registers a new channel to listen on a topic. There are two types of channels:

  • poll - meant to be queried directly by the consumer.
  • push - sends http requests to a webhook set up by the consumer.
POST /channels
{
    "topic": "button",
    "channel": "lights",
    "type": "poll"
}

or

{
    "topic": "button",
    "channel": "monitor",
    "type": "push",
    "webhook": "http://monitor:8000/webhook"
}

GET /topics/:topicID/channels/:channelID/events

This is the polling endpoint that the consumer will call to poll the event data from the channel.

The events will flush out of the channel as soon as they are read. Only new events since the last reading will be present in the payload.

{
    "events": [
        {
            "publisher": "monitor",
            "type": "button",
            "data": {
                "pressed": true
            },
            "created": "2019-10-25T06:17:43.801377Z"
        },
        {
            "publisher": "monitor",
            "type": "example",
            "data": {
                "pressed": false
            },
            "created": "2019-10-25T06:17:44.511857Z"
        }
    ]
}

GET /topics/:topicID

The topics hold onto the last published event. A topic does not need to have any registered consumers to support the topic query.

This is handy when you only care about the current state and not about the event stream.

{
    "publisher": "monitor",
    "type": "example",
    "data": {
        "distance": 5
    },
    "created": "2019-10-25T06:17:43.801377Z"
}

GET /

DO NOT USE THIS FOR PRODUCTION.

The default endpoint will return a dump of the current topics and channel data. Is meant for testing, debugging, and development only.

Example

We have two applications/consumers that want to track when a user presses a button so we want to create a Topic called button.

We have one Publisher that is going to be the button publisher.

The two Consumers that want to react whenever a user clicks are the monitor consumer and lights consumer.

Our published Event payload will just register if the button is pressed or not and looks like:

POST /pub
{
    "topic": "button",
    "event": {
        "type": "press",
        "publisher": "monitor",
        "data": {
            "pressed": true|false
        }
    }
}

Whenever a user presses the button the button publisher will POST the above payload to POST /pub.

The monitor app needs to register the Consumer so it can get notified of events published to the button topic.

It wants to register a Push Client because it's running a webserver and can setup a webhook for the central server to call into whenever data is sent to the topic. It registers a Push Consumer by sending the following POST request to /channels:

POST /channels
{
    "topic": "button",
    "channel": "monitor",
    "type": "push",
    "webhook": "http://monitor:8000/webhook"
}

The lighting app needs a separate Consumer but it's a simpler setup and cannot support a web server, so it opts to set up a Poll Channel that it will manually call every 200ms to get all the events that were published since the last time it read the channel.

POST /channels
{
    "topic": "button",
    "channel": "lights",
    "type": "poll"
}

Once the channels are registered every time the button publisher application POSTs an event to the topic the two channels will distribute the data. The Event data that is sent to the channel is the same data that is published.

The monitor app will be sent the event on POST http://monitor:8000/webhook.

POST http://monitor:8000/webhook
{
    "type": "press",
    "publisher": "monitor",
    "data": {
        "pressed": true
    },
    "created": "2019-10-25T06:17:43.801377Z"
}

In addition to being fed a stream of button data the monitor app periodically wants to simply check the current button state. It can do this by calling a GET request to the topic GET /topics/button. The topic keeps track of the last Event that was published and simply returns the event. This is useful in cases where the app only cares about the current published state and doesn't care about the stream of event data.

The lighting app will call the central server GET http://grandcentral/topics/button/channels/lights to get the array of events since it last called GET on the channel:

GET http://grandcentral/topics/button/channels/lights
{
    "events": [
        {
            "publisher": "monitor",
            "type": "press",
            "data": {
                "pressed": true
            },
            "created": "2019-10-25T06:17:43.801377Z"
        },
        {
            "publisher": "monitor",
            "type": "press",
            "data": {
                "pressed": false
            },
            "created": "2019-10-25T06:17:44.511857Z"
        }
    ]
}

In this way both consumer apps can independently monitor and respond to button presses that are generated by the publisher.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment