pythonmultithreadinggunicorneventletflask-socketio

Update data specific to each user using flask-socketio and eventlet


I am creating a web app that should update readings pushed to the database in realtime, to do so I have to keep a method running in the background whenever a user gets to the page. The data is user-specific, so just adding a background thread won't help. The tough part though, is that I need to be able to stop the thread on a buttonpress, and start it again right after with different data. I tried creating a solution with eventlet gunicorn and flask-socketio that looks a little like this:

@socketio.on('update', namespace='/update')
    def make_thread(*args):
        thread = Thread(target=update(args)
        thread.daemon = True
        thread.start()

def update(*args):
        while True:
            //code that keeps running to query changes
            socket.emit('update_reading', reading)
            time.sleep(10)

my problem is that while the code works, and it does update the front-end I can't seem to be able to figure out how exactly to create this thread for one user specifically (if that is even necessary). And how to stop the thread from another socket method and start a new one in its place.


Solution

  • When you get a connect event, you can map your database user id to the SocketIO session id. Something like this:

    socketio.on('connect')
    def on_connect():
        user = load_user(session['user_id'])
        user.sid = request.sid  # socketio session id
        save_user(user)
    
    socketio.on('disconnect')
    def on_disconnect():
        user = load_user(session['user_id'])
        user.sid = None
        save_user(user)
    

    From then on, when you want to emit to an individual user, just add room=user.sid to your emit call. In your example:

    @socketio.on('update', namespace='/update')
    def make_thread(*args):
        user = load_user(session['user_id'])
        thread = Thread(target=update, args=(user.sid,))
        thread.daemon = True
        thread.start()
    
    def update(sid):
        while True:
            //code that keeps running to query changes
            socket.emit('update_reading', reading, room=sid)
            time.sleep(10)
    

    To stop a background thread you will have to keep track of all of them and keep track of which user each threads belongs to. In the thread loop you will need to have a boolean instead of just while True, so that you can then tell the thread to stop by switching the value of that boolean. I recommend that you make a class wrapper for your thread and add a stop() method to it where the boolean is changed and then you wait until the thread exits by calling thread.join().