【问题标题】:Unable to connect via websockets (daphne, django, nginx, docker)无法通过 websockets 连接(daphne、django、nginx、docker)
【发布时间】:2021-08-06 04:27:24
【问题描述】:

带有 django、docker-compose、nginx、daphne 的 Websocket。

常规 http 连接工作正常,但 websocket 连接没有输出。 websocket 似乎没有处于活动状态。

通过 javascript 连接会产生错误:

new WebSocket('ws://127.0.0.1:8000/ws/pollData');
// WebSocket connection to 'ws://127.0.0.1:8000/ws/pollData' failed: 

或连接到 ws://localhost/ws/pollData 时:

new WebSocket('ws://localhost/ws/pollData');

// Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.

docker-compose.yaml

version: '3'
services:
  volume_configurer:
    image: busybox
    volumes:
      - shared:/shared:z
      - static:/static:z
    command: ["/bin/sh", "-c", "
      mkdir -p /static;
      chmod -R 777 /static;
      mkdir -p /shared/sync;
      chmod -R 777 /shared/sync;
      echo STARTED > /shared/sync/volumesetter && chmod a+r /shared/sync/volumesetter"]
  db:
    container_name: postgresdb
    image: postgres:latest
    restart: always
    env_file:
     - project.env
    ports:
      - 5432:5432
    volumes:
      - postgres-data1:/var/lib/postgresql/data1:z
  web:
    build:
      context: ./
      dockerfile: ./mdb/Dockerfile
    container_name: django
    command: >
      daphne mdb.asgi:application -b 0.0.0.0 -p 8000
    env_file:
      - project.env
    expose:
      - 8000
    depends_on:
      - db
    volumes:
      - ./mdb:/home/app/web/:z
      - static:/home/app/web/static/:z
      - shared:/uploads/:z
  nginx:
    container_name: nginx
    image: nginx
    restart: always
    ports:
      - 80:80
    volumes:
      - ./nginx:/etc/nginx/conf.d:z
      - static:/home/app/web/static/:z
    depends_on:
      - web
      - db

volumes:
  postgres-data1:
  static: 
  shared:

nginx.conf:

upstream mdb {
  server django:8000;
}
server {
  listen 80 default_server;
  listen [::]:80 default_server ipv6only=on;
  server_name localhost;
  client_max_body_size 2000M;
  location /static/ {
    alias /home/app/web/static/;
  }
  
  location /ws/ {
    proxy_pass http://mdb;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
  }
  location / {
    try_files $uri @proxy_to_app;
  }
  location @proxy_to_app {
    proxy_pass http://mdb;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_redirect off;
    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-Host $server_name;
  }
}

consumer.py

from channels.generic.websocket import WebsocketConsumer
import json
class DashConsumer(WebsocketConsumer):
  def connect(self):
    print('==================2')
    self.accept()

  def disconnect(self, close_code):
    print('==================1')
    pass

  def receive(self, text_data):
    print('==================0')
    text_data_json = json.loads(text_data)
    message = text_data_json['message']
    self.send(text_data = json.dumps({
        'message': message
      })
    )

从 python 视图来看,连接到ws://localhost:8000/ws/pollData 时,连接到 websocket 不会产生任何输出。但是,它也不会崩溃。而当连接到其他东西时,比如ws://localhost/ws/pollData,它会因ConnectionRefusedError: [Errno 111] Connection refused 而崩溃。但是,使用 ws.connect() 时 django 没有输出,即 consumer.py 中的 print('==================<->') 没有触发。

views.py

ws = websocket.WebSocket()
# does not print
print(ws.connect('ws://localhost:8000/ws/pollData'))
# also does not print
print(ws.send('{"message": "test from django"}'))
# prints "still here"
print('still here')
# this crashes
print(ws.connect('ws://localhost:8000/ws/pollData'))
# does not print
print('still here')

asgi.py

import os
from django.conf.urls import url
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mdb.settings")
django_asgi_app = get_asgi_application()
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from chat.consumer import DashConsumer
application = ProtocolTypeRouter({
  "http": django_asgi_app,
  "websocket": AuthMiddlewareStack(
    URLRouter([
      url(r"^ws/pollData$", DashConsumer.as_asgi()),
    ])
  ),
})

settings.py

WSGI_APPLICATION = 'mdb.wsgi.application'
ASGI_APPLICATION = 'mdb.routing.application'
CHANNEL_LAYERS = {
  "default": {
    "BACKEND": "channels.layers.InMemoryChannelLayer"
  },
}

非常感谢您的帮助!

编辑:

当使用ws://localhost/ws/pollDataws://0.0.0.0/ws/pollData 的地址时,javascript 似乎正在连接,因为在任何一种情况下都会发生连接事件并且网络面板显示打开的 websocket 连接。但是,在python中,这两个地址都会导致ConnectionRefusedError: [Errno 111] Connection refused,即:

ws = websocket.WebSocket()
# appears to connect but does not send anything to javascript
ws.connect('ws://localhost:8000/ws/pollData')
ws.send('{"message": "test from django"}')
# crashes
ws.connect('ws://localhost/ws/pollData')
# also crashes
ws.connect('ws://0.0.0.0/ws/pollData')

【问题讨论】:

    标签: python django docker nginx websocket


    【解决方案1】:

    DashConsumer 更改为AsyncJsonWebsocketConsumer 并注意到websocket 实际上是在不同地址(即ws://localhost/ws/pollDataws://0.0.0.0/ws/pollData)的javascript 中打开的。

    class DashConsumer(AsyncJsonWebsocketConsumer):
      print('==================0')
      async def connect(self):
        print('==================1')
        self.groupname = 'dashboard'
        await self.channel_layer.group_add(
          self.groupname,
          self.channel_name,
        )
        await self.accept()
    
      async def disconnect(self, close_code):
        print('==================2')
        await self.channel_layer.group_discard(
          self.groupname,
          self.channel_name
        )
    
      async def receive(self, text_data):
        print('==================3')
        # ~ #datapoint = json.loads(text_data)
        # ~ #val = datapoint['value']
        val = text_data
    
        await self.channel_layer.group_send(
          self.groupname,
          {
            'type': 'deprocessing', #function name to run
            'value': val #value to send function
          }
        )
        print ('>>>>', text_data)
    
      async def deprocessing(self, event):
        print('==================4')
        valOther = event['value']
        valOther = f'IP VALUE: {valOther}'
        # send for frontend
        await self.send(text_data = json.dumps({'value2': valOther}))
    

    views.py

    ws = websocket.WebSocket()
    ws.connect('ws://localhost:8000/ws/pollData')
    ws.send('{"message": "test from django"}')
    

    【讨论】:

      猜你喜欢
      • 2022-06-29
      • 2021-03-07
      • 2020-06-09
      • 1970-01-01
      • 1970-01-01
      • 2017-07-14
      • 2016-11-19
      • 2021-12-12
      • 1970-01-01
      相关资源
      最近更新 更多