0

I'm trying to make a small chat application and managed to deploy it to a EC2. After some struggling, I got the application running, but I noticed the delay was really high.

At first I thought the reason for this delay was that I'm using a free machine for testing purposes, but I'm also getting some errors in the console and thought that maybe this might have something to do with it.

I think it's valid to mention that the application works fine locally (with no errors) and when hosted I still get the messages from the server on the front end, but after a lot of delay and a lot of errors...

Console Screenshot

enter image description here

I tried changing the ping_timeout and the pint_interval parameters on the initialization of SocketIO since I thought this could have something to do with the problem, but didn't help.

This is my app.py:

from flask import Flask, render_template
from flask_socketio import SocketIO, emit, send

app = Flask(__name__)
io = SocketIO(app, ping_timeout=10, ping_interval=5)

messages = []

@app.route("/")
def home():
    return render_template("chat.html")

@io.on('sendMessage')
def send_message_handler(message):
    messages.append(message)
    emit('getMessage', message, broadcast=True)

@io.on('message')
def message_handler(message):
    send(messages)

if __name__ == "__main__":
    io.run(app)

And my Script tags from the index page:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.js"></script>
    <script>
        window.onload = function() {
            socket = io.connect('http://' + document.domain + ':' + location.port);
            console.log('http://' + document.domain + ':' + location.port);

            function addToChat(message) {
                const span = document.createElement("span");
                const chat = document.querySelector(".chat");
                
                console.log(`Adding ${message.name} and ${message.message} to the chat.`);
                span.innerHTML = `<strong>${message.name}:</strong> ${message.message}`;
                chat.append(span);
            }

            socket.on('connect', () => {
                socket.send('User connected to socket.')
            })

            document.querySelector("form").addEventListener("submit", function(event){
                event.preventDefault();

                console.log(`Sending ${event.target[0].value} and ${event.target[1].value} to the server.`);
                socket.emit('sendMessage', {name: event.target[0].value, message: event.target[1].value});
                event.target[0].value = "";
                event.target[1].value = "";
            })

            socket.on('getMessage', (message) => {
                console.log(`Receiving ${message.name} and ${message.message} from the server.`);
                addToChat(message);
            })

            socket.on('message', (messages) => {
                console.log(`Retrieving ${messages.length} messages.`);
                for (message of messages){
                    addToChat(message);
                }
            })
        }
    </script>

On the backend host, I'm using nginx and Gunicorn and start them using a service:

[Unit]
Description=Gunicorn instance for a simple hello world app
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/program
ExecStart=/home/ubuntu/program/venv/bin/gunicorn -b localhost:8000 app:app
Restart=always
[Install]
WantedBy=multi-user.target

And this is the nginx defaults:

upstream flaskprogram {
    server 127.0.0.1:8000;
}

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                proxy_pass http://flaskprogram;
        }

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

Nginx errors log: codeshare.io/vwOrny

  • None of those routes have a `methods` parameter, so I believe they will only accept GET requests by default, so that explains why the POST requests fail. – John Gordon Feb 05 '23 at 22:44
  • But you said the same code worked when deployed locally, which I can't explain. – John Gordon Feb 05 '23 at 22:51
  • I added the methods parameter to allow POST as well, but still get the GET and POST errors... I'm able to send/receive some of the messages, but I seem to lost some on the way. What could explain this behaviour? Could it be a bad configuration of my EC2 server instance? – Kojiro Hiori Feb 05 '23 at 23:20
  • Do some messages always fail and others always succeed? Or does the same message sometimes work and sometimes not? – John Gordon Feb 05 '23 at 23:25
  • It seem really random, actually... Most of the messages seem to fail (even the repeated ones) – Kojiro Hiori Feb 05 '23 at 23:47
  • I forgot to add the backend configs that I use to start the server, so I appended them to the end (service and nginx defaults) of my question. – Kojiro Hiori Feb 05 '23 at 23:59
  • Oh, I also found some of the nginx error logs: https://codeshare.io/vwOrny – Kojiro Hiori Feb 06 '23 at 00:14
  • I suggest you review the Flask-SocketIO documentation regarding how to run the application with Gunicorn, because that is likely what the issue is here. – Miguel Grinberg Feb 06 '23 at 15:54

1 Answers1

0

I found an answer for this issue. At first I couldn't understand why it didn't work even if I used gevent on gunicorn as mentioned in the documentation, but then I found out that you must specify that you're using 'gevent' since 'eventlet' seems to be the default setting on the SocketIO() method.

In my case, changing the initialization method to this fixed my issue:

io = SocketIO(app, async_mode='gevent')