Socket IO отклоняет запросы от прокси-сервера Nginx
У меня есть приложение docker, в котором запущено несколько контейнеров. Один из этих контейнеров представляет собой Python-приложение, которое может обрабатывать как сокетные io-запросы, так и обычные HTTP-запросы. Django's ASGI обрабатывает HTTP/ASGI запросы, а python-socketio
обрабатывает сокетные запросы.
Поскольку есть еще 4 подобных приложения, которые все должны быть серверами через Nginx, я должен указать URL пространства имен для приложений socket io и ASGI для всех приложений.
Я использую следующую конфигурацию для приложения:
location /app/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://app:8000/; # docker container name is "app"
}
location /app/socket.io/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://app:8000/socket.io/; # docker container name is "app"
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
asgi.py
import os
import socketio
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
django_application = get_asgi_application()
from app.async_socketio.server import sio # noqa 402
# this allows Socket IO to handle requests by default to a
# default path of /socket.io and forwards any other requests
# to the Django application.
application = socketio.ASGIApp(sio, django_application)
# just so all my socket event handles can load
from app.async_socketio.namespace import * # noqa 403
Точка входа: main.py
, который использует Uvicorn
import uvicorn
if __name__ == "__main__":
uvicorn.run(
"config.asgi:application",
host="0.0.0.0",
reload=True,
log_level="debug",
)
Когда я делаю запрос на подключение с помощью Postman к URL, http://localhost:8089/app/socket.io/?token=token&organisation_id=org_id
, я получаю следующие журналы:
Я вижу, что запрос достигает приложения, но соединение закрывается до того, как может быть выполнена какая-либо логика приложения.
Но если я подключаюсь напрямую к порту приложения, а не через Nginx, то все работает без проблем.
У меня была эта ошибка, и она действительно раздражала меня некоторое время, не на 100% уверен, что это точно такая же первопричина, но вот что я сделал, если это может быть полезно. Я не могу вспомнить, почему это работает, но это как-то связано с тем, как docker маршрутизирует вещи. Для proxy_pass используйте следующий формат URL: http://host.docker.internal:PORT Где PORT - это номер порта (номер порта, используемый в docker) запущенного приложения
В URL-адресе соединения не указан URL-адрес Socket.IO. Я не уверен, как это работает в Postman, но из JavaScript можно сделать примерно следующее:
const socket = io("http://localhost:8089", {
path: "/app/socket.io"
});
Путь, который вы передаете в URL-адресе соединения, интерпретируется как пространство имен. Если посмотреть на ваши журналы, кажется, что сервер Socket.IO получает запрос на подключение, но пытается подключить вас к пространству имен с именем /app/socket.io
.