-
Notifications
You must be signed in to change notification settings - Fork 114
RFC: Multiplexed WebSocket Streams
In Zetta today, each stream is exposed as a WebSocket link in the API.
{
"title": "state",
"rel": ["monitor", "http://rels.zettajs.io/object-stream"],
"href": "ws://zetta-cloud-2.herokuapp.com/servers/Detroit/events?topic=arm%2Fa5cb1a72-c3e7-47b6-818d-6d81b16e9ed4%2Fstate"
}
Clients subscribe to streams by opening a new WebSocket connection using the URL provided in the API response.
While this approach has worked so far, it is not without its issues.
- Each stream subscription requires a new WebSocket connection. If a client wants to subscribe to many WebSockets, this can become resource intensive and even prohibitive on mobile devices.
- If a topic can be inferred based on previously acquired information, clients must crawl the API to find the stream representation before subscribing. This can introduce latency in the client experience.
A change will impact:
- HTTP/WebSockets Client API
- SPDY Client API
- Object stream URL parsing for clients
A simple WebSocket sub-protocol will need to be introduced.
Zetta can expose a new link on the root resource of the Client API.
{
"rel": ["[http://rels.zettajs.io/events](http://rels.zettajs.io/events)"],
"href": "ws://example.com/events"
}
After a client’s initial connection, no streams will be subscribed to implicitly. Subscription requests must be sent over the WebSocket. Then event messages will be received.
The pattern for topic names can be derived from information about a server, a device, and a stream:
{server-name}/{device-type}/{device-id}/{stream-name}
A client SHOULD send only one Subscription Request per topic. Upon a successful request, the server MUST send a Subscription Acknowledgement Message. If an error is encountered processing the request, the server MUST send an Error Message if it's able. After a Subscription Acknowledgement Message, a client will start receiving Event Messages.
A subscription request MUST contain the following properties:
-
action
- Must be set to the string "subscribe". (String) -
topic
- The topic of the subscription. See Topic Format below for more information. (String)
Optionally, a subscription request MAY contain the following properties:
-
limit
- Number of events to be received before the client is automatically unsubscribed from the topic. Server will send an unsubscribe-ack when the limit is reached. When not present, a value of Infinity will be used. (Number)
For a Subscription Request, the topic provided is hierarchical, with each node separated by a slash (/
), such as:
Detroit/thermostat/a5cb1a72-c3e7-47b6-818d-6d81b16e9ed4/temperature
Topics may include wildcards. A wildcard is represented by an asterisk (*
) and may be used in hierarchical topics, such as:
Detroit/thermostat/*/temperature
A single asterisk (*
) denotes a wildcard for a single level of the hierarchy. A double asterisk (**
) denotes a wildcard for multiple levels of the hierarchy, such as:
Detroit/**/temperature
Topic nodes may be represented in the form of a regular expression. Regular expressions should be wrapped in curly braces ({}
), such as:
{^Det.+$}/thermostat/a5cb1a72-c3e7-47b6-818d-6d81b16e9ed4/temperature.
Topics may also include a query. Queries ensure an event is only published if it meets a certain filter. They also allow for field selection. Queries are expressed in the Calypso Query Language. They are appended to a topic after a question mark, such as:
Detroit/thermostat/*/temperature?select * where data > 85
Detroit/thermostat/*/temperature?select data.degreesC where data.degreesF > 85
{
"action": "subscribe",
"topic": "Detroit/arm/a5cb1a72-c3e7-47b6-818d-6d81b16e9ed4/state",
"limit": 10
}
A server MUST send a Subscription Acknowledgement Message to a client upon a successful subscription.
A Subscription Acknowledgement Message MUST contain the following properties:
-
type
- Must be set to the string "subscribe-ack". (String) -
timestamp
- A UTC timestamp of when the message was sent. (Number) -
topic
- The topic of the subscription. (String) -
subscriptionId
- Subscription identifier to be returned with related Event Messages. (Number)
{
"type": "subscribe-ack",
"timestamp": 1442944840135,
"topic": "Detroit/arm/a5cb1a72-c3e7-47b6-818d-6d81b16e9ed4/state",
"subscriptionId": 2
}
Once a subscription request has been made, an Event Message stream will start to flow.
An Event Message MUST contain the following properties:
-
type
- Must be set to the string "event". (String) -
topic
- The topic to which the message belongs. (String) -
subscriptionId
- Identifier of the associated subscription. (Number) -
timestamp
- A UTC timestamp of when the message was sent. (Number) -
data
- The data payload for the message. (String|Number|Array|Object)
TODO: Add language about queries coming back as an object (key-value pairs).
{
"type": "event",
"topic": "Detroit/arm/a5cb1a72-c3e7-47b6-818d-6d81b16e9ed4/state",
"subscriptionId": 2,
"timestamp": 1442944840135,
"data": "moving-claw"
}
Once an Unsubscribe Request is received, the server MUST perform a best effort to stop the message flow immediately. The client MAY receive additional messages for a topic while the server is processing the request. Upon successfully processing an Unsubscribe Request, a server MUST send an Unsubscribe Acknowledgement Message. If an error is encountered processing the request, the server should send an Error Message.
An Unsubscribe Request MUST contain the following properties:
-
action
- Must be set to the string "unsubscribe". (String) -
subscriptionId
- The identifier of the subscription. (Number)
{
"action": "unsubscribe",
"subscriptionId": 2
}
A server MUST send an Unsubscribe Acknowledgement Message to a client upon successfully processing an Unsubscribe Request.
An Unsubscribe Acknowledgement Message MUST contain the following properties:
-
type
- Must be set to the string "unsubscribe-ack". (String) -
timestamp
- A UTC timestamp of when the message was sent. (Number) -
subscriptionId
- The identifier of the subscription. (Number)
{
"type": "unsubscribe-ack",
"timestamp": 1442944840135,
"subscriptionId": 2
}
A server MUST send an Error Message upon encountering any issues processing requests from clients.
An Error Message MUST contain the following properties:
-
type
- Must be set to the string "error". (String) -
code
- Error code defining the error. See Error Codes below. (Number) -
timestamp
- A UTC timestamp of when the message was sent. (Number) -
topic
- The topic of the subscription. (String)
An Error Message MAY contain the following property:
-
message
- A plaintext description of the error. (String) -
subscriptionId
- Identifier of the associated subscription. Only applicable if an error occurs after a subscription is established. (Number)
-
400
- Bad Request - Invalid JSON in request -
405
- Method Not Supported - Invalid "action" value for incoming message. -
500
- Server Error - We messed up.
{
"type": "error",
"code": 500,
"timestamp": 1442944840135,
"topic": "Detroit/arm/a5cb1a72-c3e7-47b6-818d-6d81b16e9ed4/state",
"message": "Server exploded.",
"subscriptionId": 2
}
- Only Zetta object streams will qualify for this protocol.
- We should come up with a WebSocket sub-protocol type.
- We should investigate if WAMP/STOMP/ReactiveSocket support would better serve our needs.
- Existing functionality should remain. WebSocket URLs should be in context.
- Zetta needs language around clients parsing topics out of URLs to use with the generic subscription link.
Need help? Visit the Zetta Discuss List !
Need help? Visit the Zetta Discuss List ! |
---|
About Zetta
Videos and webcasts
- NEW! Building with Zetta
Tutorials
- NEW! Zetta tutorial series
- Quick start
- Configure a simple device
- Build a mock LED device
- Use the browser client
- Deploy a Zetta server to Heroku
Understanding Zetta
Writing Zetta drivers
- Finding Zetta device drivers
- Create a device driver from starter code
- More coming soon...
Using streams
Reference