Каналы Django с nginx не получают сообщений от клиентов

У нас есть приложение, которое использует пакет каналов и отлично работает... на локальном хосте. Как только мы начнем подготовку и поместим поле nginx перед Django (с SSL), мы сможем подключиться к сокету, но клиент не получит никаких сообщений.

Конфигурация Nginx:

worker_processes auto;

error_log /dev/stdout info;

user nobody nogroup;
pid /tmp/nginx.pid;

events {
    worker_connections 1024;
    accept_mutex off;
}

http {
    include mime.types;
    default_type application/octet-stream;
    access_log /dev/stdout;
    sendfile on;
    keepalive_timeout 65;
    gzip on;
    gzip_disable "MSIE [1-6].(?!.*SV1)";
    gzip_vary on;

    upstream ws_server {
        server unix:/tmp/daphne.sock fail_timeout=0;
    }

    server {
        #   redirect all http requests to https
        listen 80;
        listen [::]:80 ipv6only=on;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;

        client_max_body_size 4G;
        server_name changemyip.com;
        keepalive_timeout 5;
        add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:50m;
        ssl_session_tickets on;

        ssl_dhparam /etc/nginx/ssl/dhparam.pem;

        location /ws/ {
            try_files $uri @proxy_to_ws;
        }

        location @proxy_to_ws {
            proxy_pass   http://ws_server;

            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-Proto $scheme;

            #   Websocket specific
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $http_host;
            proxy_connect_timeout 86400;
            proxy_read_timeout 86400;
            proxy_send_timeout 86400;
        }

        ...
        ssl_protocols TLSv1.1 TLSv1.2;
        ...
        ssl_prefer_server_ciphers on;

        ssl_stapling on;
        ssl_stapling_verify on;
    }
}

Django работает с gunicorn, а для веб-сокетов я поднял сервер daphne. В журналах daphne я вижу, что мой клиент подключается, но сообщения от daphne клиенту не поступают.

Дафна создает сокет unix, который nginx использует для связи: daphne main.asgi:channel_layer -u /tmp/daphne.sock


person Romeo Mihalcea    schedule 23.09.2016    source источник


Ответы (2)


У меня была точно такая же проблема. Мне не удалось подключиться через unix-сокет, но я нашел очень простой способ использовать системный порт для управления запросами. Я использовал следующие руководства (и использовал свой опыт работы с Gunicorn), и мне удалось немного изменить их файл конфигурации Nginx, я бы порекомендовал вам ознакомиться с руководствами:

Мой файл Nginx

# Enable upgrading of connection (and websocket proxying) depending on the
# presence of the upgrade field in the client request header
map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}

# Create an upstream alias to where we've set daphne to bind to
upstream django_app_server {
  server 127.0.0.1:8000;
}

server {
  listen 80;
  server_name YOURDOMAIN.COM;

  client_max_body_size 4G;
  access_log /webapps/General/logs/nginx-access.log;
  error_log /webapps/General/logs/nginx-error.log;

  location /static/ {
      alias /webapps/General/DjangoProject/static/;
  }

  location /media/ {
      alias /webapps/General/DjangoProject/media/;
  }

  location / {
    if (!-f $request_filename) {
        proxy_pass http://django_app_server;
        break;
    }
    # Require http version 1.1 to allow for upgrade requests
    proxy_http_version 1.1;
    # We want proxy_buffering off for proxying to websockets.
    proxy_buffering off;
    # http://en.wikipedia.org/wiki/X-Forwarded-For
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # enable this if you use HTTPS:
    # proxy_set_header X-Forwarded-Proto https;
    # pass the Host: header from the client for the sake of redirects
    proxy_set_header Host $http_host;
    # We've set the Host header, so we don't need Nginx to muddle
    # about with redirects
    proxy_redirect off;

    # Depending on the request value, set the Upgrade and
    # connection headers
    proxy_set_header Upgrade $http_upgrade;

    proxy_set_header Connection $connection_upgrade;
  }

  # Error pages
  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /webapps/General/DjangoProject/templates/;
  }
}

Веб-сокеты в моих проектах работают довольно хорошо (группы и каналы), и все запросы обслуживаются Daphne, но если вам ДЕЙСТВИТЕЛЬНО нужно использовать сокет, эта конфигурация может вам помочь.

Вопросы для рассмотрения

  • Помните, что этот файл Nginx позволяет Daphne подключаться в целом, но на рабочем сервере вам необходимо запускать «Daphne Instance Server» и «Daphne Workers» отдельно, чтобы иметь возможность передавать сообщения по вашим каналам.

  • Проверьте, будете ли вы использовать Redis-Server или другой диспетчер очередей при обслуживании каналов и групп. Я говорю это, потому что заметил, что при использовании конфигурации «InMemory» несколько сообщений терялись.

  • Также проверьте, работает ли в вашей производственной среде Redis-Server в качестве демона. Я заметил, что в некоторых системах Redis-Server даже не работал, но приложение Django не вызывало исключения при отказе в подключении.

  • Вам нужно что-то, чтобы поддерживать работу Daphne и ее рабочих, потому что, несмотря на то, что они зацикливаются, они не «устойчивы к исключениям», поэтому они умрут, когда возникнет исключение. Очевидно, я рекомендую Supervisor или использовать систему Linux для служб.

  • Я не знаю, могут ли рабочие daphne обслуживать статические и медиафайлы при DEBUG==False, но, видимо, лучше обслуживать их отдельно, используя конфигурацию Nginx.

  • Я до сих пор не знаю последствий использования порта для безопасности/производительности по сравнению с использованием сокета, поэтому это стоит проверить (читайте ниже, я нашел возможную ошибку с Дафной или моей конфигурацией).

Я знаю, что это может быть неактуально для вас сейчас (я имею в виду, что прошел почти 1 месяц), но, возможно, кто-то найдет применение этому ответу.

Неизвестная и довольно странная проблема безопасности

TL;DR: не размещайте два приложения Django-Daphne на одном сервере с этой конфигурацией, иначе у вас будут плохие времена.

Используя эту конфигурацию, я смог развернуть приложения Phoenix вместе с приложениями Django без каких-либо проблем, НО у меня возникли проблемы при развертывании 2 или более приложений Django с использованием этого типа конфигурации. По какой-то причине Daphne знает, какие порты ей нужно постоянно читать, чтобы получать запросы, но она просто читает их все и отдает кому угодно. Например, если у меня есть DJANGO_APP_1 и DJANGO_APP_2, работающие на одном сервере (с разными конфигурациями Nginx и, очевидно, разными системными портами), иногда Daphne Workers из DJANGO_APP_2 будут КРАТЬ запросы, предназначенные для DJANGO_APP_1, и наоборот. Я не смог точно определить источник проблемы, но я считаю, что это связано с тем, что рабочие Daphne были в некотором роде агностиками проекта, с которым они связаны. (Просто теория, у меня нет времени проверять их код).

person Daniel Ortiz    schedule 25.10.2016
comment
Извините за множественные удаления и правки... Мне просто нужно было проверить то, что я писал - person Daniel Ortiz; 25.10.2016

я работал с daphne gunicorn и nginx, и мне было трудно найти правильную конфигурацию для nginx, после того, как я какое-то время возился с ней, эта конфигурация работала для меня.

​worker_processes  2;

events {
worker_connections  1024;
}

http {

  include       mime.types;
  default_type  application/octet-stream;
  sendfile        on;
  keepalive_timeout  5;

  upstream webserver {
  server 127.0.0.1:8000;
  }

  upstream wsserver {
  server 127.0.0.1:9000;
  }

  server {
    listen 8046;

    client_max_body_size 20M;
    server_name localhost;
    tcp_nodelay     on;

    location / {
        proxy_pass http://webserver;
        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;
   }

   location /ws/ {
        proxy_pass http://wsserver;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
 }

 }
person doubleo46    schedule 03.08.2017