Flask-socketio: Getting 404 error when using gunicorn/eventlet in prod

Created on 31 May 2019  路  6Comments  路  Source: miguelgrinberg/Flask-SocketIO

Hi, I've spent several hours looking online and reading through the issues posted but have not found a solution.

This totally works on dev without gunicorn and eventlet

websocket request return the following error:
~~
{
"error": "404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.",
"traceback": [
" File \"/usr/local/lib/python3.7/site-packages/flask/app.py\", line 1813, in full_dispatch_request\n rv = self.dispatch_request()\n",
" File \"/usr/local/lib/python3.7/site-packages/flask/app.py\", line 1791, in dispatch_request\n self.raise_routing_exception(req)\n",
" File \"/usr/local/lib/python3.7/site-packages/flask/app.py\", line 1774, in raise_routing_exception\n raise request.routing_exception\n",
" File \"/usr/local/lib/python3.7/site-packages/flask/ctx.py\", line 336, in match_request\n self.url_adapter.match(return_rule=True)\n",
" File \"/usr/local/lib/python3.7/site-packages/werkzeug/routing.py\", line 1786, in match\n raise NotFound()\n"
]
}
~
~
I'm running gunicorn like gunicorn --worker-class eventlet --bind :5000 wsgi:app --log-level=debug --log-file=-

my wsgi.py file looks like
~~~
from app import init_app
from flask_socketio import SocketIO

app = init_app()

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

~~~

the init_app func is just a wrapper for the usual app = Flask(__name__) no magic there other than setting some variables and environment configs

my nginx config for this is

~~
location /socket.io {
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass http://api:5000/socket.io;
}
~
~

Python packages

~~~

$ pip freeze
aniso8601==6.0.0
blinker==1.4
Click==7.0
dnspython==1.16.0
eventlet==0.24.1
Flask==1.0.2
Flask-Caching==1.5.0
Flask-Mail==0.9.1
Flask-RESTful==0.3.7
Flask-SocketIO==3.2.1
Flask-SQLAlchemy==2.3.2
greenlet==0.4.15
gunicorn==19.9.0
itsdangerous==1.1.0
Jinja2==2.10.1
MarkupSafe==1.1.1
monotonic==1.5
mysqlclient==1.3.14
PyJWT==1.7.1
python-engineio==3.7.0
python-socketio==4.0.3
pytz==2019.1
six==1.12.0
SQLAlchemy==1.3.4
Werkzeug==0.15.4

~~~

The error output is obviously being generated by flask.
Otherwise my endpoints work fine.

Any ideas?

question

Most helpful comment

It's not eventlet, the SocketIO object is what instantiates the Socket.IO server. That line needs to execute both for development and production deployments. When you had it inside the if statement it only executed for development.

All 6 comments

By the way, std outputs this for every ws request:
~~~

[2019-05-31 02:20:52 +0000] [9] [DEBUG] GET /socket.io/
[2019-05-31 02:20:52 +0000] [9] [DEBUG] Closing connection.
~~~

this is the console output when I run in debug mode

~~~
bind: [':5000']

backlog: 2048


workers: 1

worker_class: eventlet


threads: 1

worker_connections: 1000

max_requests: 0

max_requests_jitter: 0

timeout: 30

graceful_timeout: 30

keepalive: 2

limit_request_line: 4094

limit_request_fields: 100

limit_request_field_size: 8190

reload: False

reload_engine: auto

reload_extra_files: []

spew: False

check_config: False

preload_app: False

sendfile: None

reuse_port: False

chdir: /usr/src/app

daemon: False

raw_env: []

pidfile: None

worker_tmp_dir: None



0

group: 0

umask: 0

initgroups: False

tmp_upload_dir: None

secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}

forwarded_allow_ips: ['127.0.0.1']

accesslog: None

disable_redirect_access_to_syslog: False

access_log_format: %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"

errorlog: -

loglevel: debug

capture_output: False

logger_class: gunicorn.glogging.Logger

logconfig: None

logconfig_dict: {}

syslog_addr: udp://localhost:514

syslog: False

syslog_prefix: None

syslog_facility: user

enable_stdio_inheritance: False

statsd_host: None

statsd_prefix:

proc_name: None

default_proc_name: wsgi:app

pythonpath: None

paste: None

on_starting:

on_reload:

when_ready:

pre_fork:

post_fork:

post_worker_init:

worker_int:

worker_abort:

pre_exec:

pre_request:

post_request:

child_exit:

worker_exit:

nworkers_changed:

on_exit:

proxy_protocol: False

proxy_allow_ips: ['127.0.0.1']

keyfile: None

certfile: None

ssl_version: 2

cert_reqs: 0

ca_certs: None

suppress_ragged_eofs: True

do_handshake_on_connect: False

ciphers: TLSv1

raw_paste_global_conf: []

[2019-05-31 02:28:23 +0000] [7] [INFO] Starting gunicorn 19.9.0

[2019-05-31 02:28:23 +0000] [7] [DEBUG] Arbiter booted

[2019-05-31 02:28:23 +0000] [7] [INFO] Listening at: http://0.0.0.0:5000 (7)

[2019-05-31 02:28:23 +0000] [7] [INFO] Using worker: eventlet

[2019-05-31 02:28:23 +0000] [10] [INFO] Booting worker with pid: 10

[2019-05-31 02:28:23 +0000] [7] [DEBUG] 1 workers
~~~

just updated all related packages to latest version and issue still persists.

~
aniso8601==6.0.0
blinker==1.4
Click==7.0
dnspython==1.16.0
eventlet==0.25.0
Flask==1.0.3
Flask-Caching==1.7.2
Flask-Mail==0.9.1
Flask-RESTful==0.3.7
Flask-SocketIO==4.0.0
Flask-SQLAlchemy==2.3.2
greenlet==0.4.15
gunicorn==19.9.0
itsdangerous==1.1.0
Jinja2==2.10.1
MarkupSafe==1.1.1
monotonic==1.5
mysqlclient==1.4.2.post1
PyJWT==1.7.1
python-engineio==3.7.0
python-socketio==4.0.3
pytz==2019.1
six==1.12.0
SQLAlchemy==1.3.4
Werkzeug==0.15.4
~

Your SocketIO object needs to be moved to the global scope:

from app import init_app
from flask_socketio import SocketIO

app = init_app()
socketio = SocketIO(app)

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

Thanks for your reply, that fixed it!. It did not occurred to me to try this since it works in dev. I gather that Eventlet's concurrency has something to do with that. If you can shed any light on why this is, I'd appreciate it but otherwise thanks for your time.

It's not eventlet, the SocketIO object is what instantiates the Socket.IO server. That line needs to execute both for development and production deployments. When you had it inside the if statement it only executed for development.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nh916 picture nh916  路  3Comments

blstdmi picture blstdmi  路  3Comments

huangganggui picture huangganggui  路  3Comments

novice79 picture novice79  路  3Comments

nayebare picture nayebare  路  4Comments