Странное поведение с django и websocket за nginx
У меня есть приложение Docker Django, которое использует каналы django и Redis для потоковой передачи данных. Все это находится за Nginx. Все работает, кажется, хорошо. Я могу обращаться к своим сайтам под поддоменами, а также могу подключаться через websocket.
Единственная проблема заключается в том, что вебсокет передает данные, и каждый короткий промежуток времени (несколько секунд) он блокируется почти ровно на 5 секунд, а затем продолжает передачу... Как если бы каждый короткий промежуток времени запускалась фоновая задача, которая блокирует мой код. В логах я не вижу никакой ошибки. Также значения, которые я получаю на стороне клиента, иногда дублируются, а иногда и не совпадают...
Я выложил свой код. Надеюсь, это не слишком много...
У меня есть тестовый поток в django, отправляющий тестовые данные:
# websocket/apps.py
from django.apps import AppConfig
from threading import Thread
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
def task_handler():
count = 0
run = True
while run:
channel_layer = get_channel_layer() # <-- THIS BLOCKS SOMETIMES FOR 5 SECONDS
async_to_sync(channel_layer.group_send)('group_1', {'type': 'send_message', 'message': str({'c': count})})
count+=1
time.sleep(0.1)
class WebsocketConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'websocket'
def ready(self) -> None:
Thread(target=task_handler, daemon=True).start()
Это мой файл asgi.py:
# asgi.py
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import websocket.routing
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app_test.settings")
application = ProtocolTypeRouter({
"http": get_asgi_application(),
# Just HTTP for now. (We can add other protocols later.)
"websocket": AuthMiddlewareStack(
URLRouter(
websocket.routing.websocket_urlpatterns
)
)
})
файл my consumers.py:
# websocket/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class DataConsumer(AsyncWebsocketConsumer):
async def connect(self):
# Join room group
await self.channel_layer.group_add(
'group_1',
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# Leave room group
await self.channel_layer.group_discard(
'group_1',
self.channel_name
)
# Receive message from WebSocket
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
await self.channel_layer.group_send(
'group_1',
{
'type': 'chat_message',
'message': message
}
)
# Receive message from room group
async def send_message(self, event):
message = event['message']
# Send message to WebSocket
await self.send(text_data=json.dumps({
'message': message
}))
Мой файл routing.py:
# websocket/routing.py
from django.urls import path
from .consumers import DataConsumer
websocket_urlpatterns = [
path('ws/data/', DataConsumer.as_asgi()),
]
мой файл docker-compose.yml:
version: "3.8"
services:
nginx-proxy:
image: jwilder/nginx-proxy
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock
- ./system_files/nginx/templates:/app/templates
- ./system_files/nginx/nginx.conf:/etc/nginx/nginx.conf
- ./system_files/nginx/conf.d:/etc/nginx/conf.d
networks:
- backend
- frontend
db:
image: postgres
volumes:
- ./data/db:/var/lib/postgresql/data
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
networks:
- backend
app:
build: .
volumes:
- .:/app_test
restart: always
environment:
- VIRTUAL_HOST=iot.localhost
- VIRTUAL_PORT=8000
- POSTGRES_NAME=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
command: bash -c "sleep 5 && python3 manage.py runserver 0.0.0.0:8000"
depends_on:
- db
- nginx-proxy
networks:
- backend
- frontend
redis:
image: redis:alpine
ports:
- 6379:6379
depends_on:
- db
- app
restart: always
networks:
- backend
- frontend
networks:
frontend:
name: frontend-network
backend:
name: backend-network
mqtt:
name: mqtt-network
и мой файл конфигурации nginx:
Добавьте на стороне клиента мой тестовый скрипт:
<script>
var w_url = 'ws://'+window.location.host+'/ws/data/';
console.log(w_url);
const mapSocket = new WebSocket(w_url);
mapSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
console.log(data.message);
};
mapSocket.onclose = function(e) {
console.error('socket closed unexpectedly');
};
</script>
Как вы можете видеть в консоли на стороне клиента, я получаю сообщения не в порядке отправки, а иногда они повторяются и через несколько секунд после отправки.