uwsgi + flask-socketio in multi-processes

86 Views Asked by At

I am trying to build a websocket server step by step. But I meet crucial problem when I try to use eventlet or gevent.

There are my codes for the server:

[uwsgi.ini]

[uwsgi]
chdir = /home/user/websocket
module=websocket:app
callable=app

processes=4
socket=/home/user/websocket/uwsgi.sock
uid = user
gid = user

chmod-socket=664

http-socket = :15000

log-reopen=true
die-on-term=true
master=true
vacuum=true

plugin=python3

virtualenv = /home/user/websocket/web

gevent = 100

[websocket.py]

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

app = Flask(__name__)
socketio = SocketIO(app, logger=True, engineio_logger=True, cors_allowed_origins='*')


@socketio.on('connect')
def connected():
    print('-'*30, '[connect]', '-'*30)

@socketio.on('message')
def handle_message(data):
    print('-'*30, '[message]', '-'*30)
    print('received message: ' + data)
    send(data)  # Echoes back the received message

@socketio.on_error()  # This decorator handles all errors including connect_error
def handle_error(e):
    if isinstance(e, Exception):
        print('An error occurred:', str(e))
        # Here, you can log the error or perform any necessary action.
        # For example, you can check if it's a connect_error and take appropriate action.

@app.route("/")
def hello():
    return "Connected"

if __name__ == '__main__':
    socketio.run(app)

I ran:

uwsgi --ini uwsgi.ini

But I got the error:

Traceback (most recent call last):
  File "/home/user/websocket/web/lib/python3.10/site-packages/flask/app.py", line 1478, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/user/websocket/web/lib/python3.10/site-packages/flask_socketio/__init__.py", line 43, in __call__
    return super(_SocketIOMiddleware, self).__call__(environ,
  File "/home/user/websocket/web/lib/python3.10/site-packages/engineio/middleware.py", line 63, in __call__
    return self.engineio_app.handle_request(environ, start_response)
  File "/home/user/websocket/web/lib/python3.10/site-packages/socketio/server.py", line 428, in handle_request
    return self.eio.handle_request(environ, start_response)
  File "/home/user/websocket/web/lib/python3.10/site-packages/engineio/server.py", line 271, in handle_request
    packets = socket.handle_get_request(
  File "/home/user/websocket/web/lib/python3.10/site-packages/engineio/socket.py", line 90, in handle_get_request
    return getattr(self, '_upgrade_' + transport)(environ,
  File "/home/user/websocket/web/lib/python3.10/site-packages/engineio/socket.py", line 146, in _upgrade_websocket
    ws(environ, start_response)
  File "/home/user/websocket/web/lib/python3.10/site-packages/engineio/async_drivers/eventlet.py", line 40, in __call__
    raise RuntimeError('You need to use the eventlet server. '
RuntimeError: You need to use the eventlet server. See the Deployment section of the documentation for more information.

So I tried to put "http-websockets = true" in the ini file but got the same error.

This is the client I tried

[index.html]

<!DOCTYPE html>
<html>
<head>
    <title>Flask SocketIO Client</title>
    <script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
</head>
<body>
    <input type="text" id="messageInput" placeholder="Type a message...">
    <button onclick="sendMessage()">Send</button>
    <div id="messages"></div>

    <script>
        var socket = io('http://localhost:15000'); // Change to your server's address and port

        socket.on('connect', function() {
            console.log('Connected to the server.');
        });

        socket.on('message', function(data) {
            console.log('Received message:', data);
            document.getElementById('messages').innerText += data + '\n';
        });


        function sendMessage() {
            var message = document.getElementById('messageInput').value;
            console.log('sending...:', message);
            socket.emit('message', message);
            document.getElementById('messageInput').value = '';
        }
    </script>
</body>
</html>

[client.py]

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(port=5001)  # Run on a different port than your SocketIO server

In the client page, it shows this error:

POST http://localhost:15000/socket.io/?EIO=4&transport=polling&t=Ondmm8T&sid=BooMMCC89x9oei5_AAAQ 400 (BAD REQUEST)
websocket.js:88 WebSocket connection to 'ws://localhost:15000/socket.io/?EIO=4&transport=websocket&sid=1_GSTZsrzqFdezzbAAAP' failed: 

Please help me to solve this problem.

1

There are 1 best solutions below

1
Miguel Grinberg On

It sounds like you have several supported libraries installed, so Socket.IO needs to know which of them you want to use. The default one is eventlet, but you are using uWSGI, so you have to tell it to use the uWSGI logic explicity by adding async_mode='gevent_uwsgi' to your SocketIO() constructor.

I also recommend that you look at the documentation to learn how to configure for uWSGI. Multiple worker processes are not supported in the way that you are doing it, you should run a single worker, which can handle hundreds/thousands of concurrent clients through gevent.