8

I am currently working on extending my existing REST API created using Flask-RESTPlus with WebSocket support. The idea is to create a Web Thing Model compliant Web Thing (Gateway). The "Things" in my use-case are dynamically added or removed.

The current setup allows a consumer to fetch the latest values from a Thing, e.g. temperature sensor, using a HTTP GET request to /thingId/properties/temperature. The values are in reality consumed from Kafka and temporarily stored in Redis.

Now I am wondering how I can extend this setup and allow the consumer to not only poll the latest values, but subscribe to the property of a Thing using WebSockets. I have a working solution where I create "Rooms" for each property, but this requires two separate servers and duplication of endpoints.

For REST I have

@app.route('/<thingId>/properties/<propertyId>')
    # get latest datapoint
    return latestDatapoint

For Flask-SocketIO I have

@socketio.on('join')
def on_join(data):
    username = data['username']
    room = data['room'] # e.g. /thingId/properties/temperature
    join_room(room)
    send(username + ' has entered the room.', room=room)

and then I'm forwarding the data to the correct room as it comes in from Kafka. On the client side I then need to connect to the WebSocket server and join the room

socket.on('connection', function(socket){
  socket.emit('join', 'some room');
});

This implementation works, but I was strongly hoping for an alternative workflow as shown in the figure below where the client connects to the same endpoint used in the REST API, but with the WebSocket protocol instead of joining rooms etc. Web Thing Subscriptions

Do you have any idea if this already exists or is feasible to implement?

Pieter Moens
  • 331
  • 1
  • 4
  • 14

1 Answers1

7

I have a working solution where I create "Rooms" for each property, but this requires two separate servers and duplication of endpoints.

The Socket.IO server and your HTTP server do not necessarily need to be separate, In all supported configurations you can host HTTP and Socket.IO applications with a single server.

I also don't see the endpoint duplication, but maybe this is because you think of Socket.IO event handlers as endpoints, while in fact they are not. With Socket.IO there is a single endpoint in the HTTP sense, since all Socket.IO traffic travels on a single URL. Your event handlers are just that, functions that are invoked when certain events pop up on the Socket.IO endpoint.

where the client connects to the same endpoint used in the REST API, but with the WebSocket protocol instead of joining rooms etc.

So you want your client to establish a separate WebSocket connection for each thing it wants to watch? That seems a bit resource intensive and not very scalable to me. If the client needs to watch 100 things, then it will have to maintain 100 WebSocket connections. Keep in mind that most browsers cap the number of WebSocket connections they can have open at a time, both per page and globally.

Socket.IO is a higher-level protocol that is built on top of WebSocket and HTTP. If you still prefer to use WebSocket directly, then you can take any of the available open source WebSocket servers and implement your application with that instead of Socket.IO. Here are a few options for Python off the top of my mind:

You are going to lose a few things that Socket.IO offers that are very handy:

  • Automatic reconnections
  • Automatic support for non-WebSocket clients via long-polling
  • Event-based dispatching
  • Rooms

So you need to make sure these are not important features, or you are okay implementing them yourself directly on the WebSocket server.

Miguel Grinberg
  • 65,299
  • 14
  • 133
  • 152
  • 1
    Hi, Miguel. Thank you for your response :) The inspiration for my question is a book called "Building the Web of Things" by Dominique Guinard and Vlad Trifa. It includes Node.JS snippets of a quite modular application. It has plugins for several sensors (e.g. DHT) which contains sensor values. With REST endpoints you can simple poll for the latest value, but by opening a WebSocket connection on the url (endpoint), you can upgrade the protocol which registers an observer on the sensor property which will continuously send the value when udpated. – Pieter Moens Jan 07 '20 at 13:41
  • This does indeed establish a connection per observed property.. I will further look into the pros and cons of trying to set up something similar to this compared to using a higher-level protocol such as Socket.IO. I am currently looking along the lines of gevent-websocket in combination with Flask (https://gist.github.com/lrvick/1185629) – Pieter Moens Jan 07 '20 at 13:43
  • As I said in my answer, opening multiple websocket connections is wasteful, and you are risking reaching the max connection limit for the browser. A single websocket connection (Socket.IO or not) has plenty of bandwidth to handle all your watched things. – Miguel Grinberg Jan 08 '20 at 07:46