Friday, 21 June 2019

How to fix websocket messages being delayed by Post request in Flask-uwsgi-Nginx setup?

I am working on a Flask app with a single webpage rendered by the render_template function of Flask. This webpage contains a form to submit data by a post request + WebSocket connection for the status of the task. When the form is submitted, post request for a long-running task is triggered. During the execution of this long-running task, there are few WebSocket messages that are being sent from flask backend to this webpage as the status of the task. However, even though these messages are being sent from backend in proper order, they are only received in the webpage when the long-running task is completed(in other words when the response of form post request is received by webpage). As per my understanding, WebSockets are asynchronous. So why is this happening?

What I am doing:

Post request handler --> websocket msg 1 --> websocket msg2 --> celery --> reponse of post request

I tried adding processes, threads,etc in app.ini.

app.ini for uwsgi

[uwsgi]
module = wsgi:app
master = true
http-socket = 127.0.0.1:7001
gevent = 10
http-websockets = true

nginx config

server {
    listen 7000;
    server_name _;  

    location / {
        include proxy_params;
    proxy_pass http://127.0.0.1:7001;
    uwsgi_buffering off;
    }

    location /socket.io {
    include proxy_params;
        proxy_http_version 1.1;
    uwsgi_buffering off;
        proxy_buffering off;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_pass http://127.0.0.1:7001/socket.io;

    }
}

webpage websocket code

<script type="text/javascript" charset="utf-8">
        var socket = io.connect('http://' + document.domain + ':' + location.port);

         socket.on('connect', function() {
            socket.emit('get_status')
            socket.emit('get_message')
            console.log('Websocket connected!');
            });

        socket.on('response_status',function(data){
        document.getElementById("status").innerHTML = "Status : "+data.status;
        console.log("status received");
    });


        socket.on('response_last_message',function(data){
        document.getElementById("message").innerHTML = "Message : "+data.last_message;
        console.log("Message received");
    });


</script>

Python code:

Main class

socketio = SocketIO()

def create_app():
    app = Flask(__name__)
    api = Api(app)
    socketio.init_app(app)


    with open('config.json', 'r') as conf:
        configObject = json.load(conf)

    commonConfig = commons.Common(configObject)
    socketWork = commons.SocketWork(socketio, commonConfig)


    api.add_resource(routes.IndexPage,'/', resource_class_kwargs={ 'commonConfig': commonConfig, 'socketWork': socketWork})
    api.add_resource(routes.UploadFile, '/uploadFile', resource_class_kwargs={ 'commonConfig': commonConfig, 'socketWork': socketWork})


    return app


Socket class

class SocketWork():

    def __init__(self, socketio = None, commonObj=None):
        self.commonObj = commonObj
        self.socketio = socketio
        self.socketio.on_event('get_status',self.send_last_status)
        self.socketio.on_event('get_message',self.send_last_message)

    def send_last_status(self):
        print("Send status now")
        status = self.commonObj.status
        self.socketio.emit("response_status", {"status": status})

    def send_last_message(self):
        print("Send last message")
        msg = self.commonObj.msg
        self.socketio.emit("response_last_message", {"last_message": msg})

    def send_message(self,msg):
        self.commonObj.msg = msg
        self.send_last_message()

    def send_status(self,statusVal):
        self.commonObj.status = statusVal
        self.send_last_status()

Long running post method:

class UploadFile(Resource):

    def __init__(self, **kwargs):
        self.commonConfig = kwargs['commonConfig']
        self.socketWork    = kwargs['socketWork']

    @use_args(args.argsUploadData)
    def post(self, args):
        msg = "This is socket message"
        socketWork.send_message(msg)
        socketWork.send_status(commonConfig.config['Status']['READY'])
        sleep(15)       ###long running task
        return constants.generic_msg.PROCESSING_QUERY.name

Expected WebSockets in the webpage to receive messages from flask backend instantaneously but receiving messages after post request is complete. No error message is generated.



from How to fix websocket messages being delayed by Post request in Flask-uwsgi-Nginx setup?

No comments:

Post a Comment