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.
- 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 aTopic
. A topic can have one or more registeredChannels/Consumers
. - Event - is the data that is published on a
Topic
. - Publisher - is a creator of data. A publisher sends
Events
toTopic
. - Consumer - reads/receives
Event
data from aChannel
.
The server supports the following endpoints
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
}
}
}
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"
}
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"
}
]
}
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"
}
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.
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.