我们有一个使用Channels软件包的应用程序,在localhost上运行正常。一旦达到暂存状态,并在Django前面(使用SSL)放置了一个nginx框,我们就可以连接到套接字,但是客户端没有收到任何消息。

Nginx conf:

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来运行,对于websocket,我启动了daphne服务器。我可以在daphne日志中看到我的客户端正在连接,但是仍然没有收到从daphne到客户端的消息。

达芙妮(Daphne)正在创建一个unix套接字,nginx会使用它来进行通信:daphne main.asgi:channel_layer -u /tmp/daphne.sock

最佳答案

我有同样的问题。我无法通过unix套接字进行连接,但是我发现了一种非常简单的方法来使用系统端口来实现请求管理。我使用了以下教程(并利用了我对Gunicorn的经验),并设法对Nginx配置文件进行了一些修改,建议您将这些教程 checkout :

  • Django Channels Group Pt1
  • Django Channels Group Pt2

  • 我的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/;
      }
    }
    

    我项目中的websocket运行得非常好(组和 channel ),并且所有请求都由Daphne处理,但是如果您确实需要使用套接字,那么此配置实际上可以为您提供帮助。

    要考虑的要点
  • 请记住,此Nginx文件通常允许Daphne连接,但是在生产服务器中,您需要分别运行“Daphne实例服务器”和“Daphne Workers”,以便能够通过您的 channel 传输消息。
  • 检查在为 channel 和组提供服务时是否将使用Redis-Server或其他队列管理器。我之所以这样说,是因为我注意到使用“InMemory”配置时,丢失了多条消息。
  • 另请检查您的生产环境是否将Redis-Server作为守护程序运行。我注意到在一些系统中,Redis-Server甚至无法正常工作,但是当拒绝连接时,Django应用程序并未引发异常。
  • 您需要一些东西来使Daphne及其工作人员保持正常运行,因为即使它们循环了,它们也不是“抗异常性的”,因此它们会在引发异常时死亡。显然,我建议使用Supervisor或将Linux系统用于服务。
  • 我不知道在DEBUG==False时daphne的worker是否可以提供静态文件和媒体文件,但是使用Nginx配置显然可以更好地为它们单独提供服务。
  • 我仍然不知道使用端口与使用套接字相比对安全性/性能的影响,因此值得检查(请阅读以下内容,我发现Daphne或我的配置可能存在错误)。

  • 我知道这对您现在可能已经不重要了(我的意思是已经快一个月了),但是也许有人会对此答案有所帮助。

    未知且相当奇怪的安全性问题

    TL; DR :不要使用此配置在同一服务器上绘制两个Django-Daphne应用程序,否则您会遇到麻烦。

    通过使用此配置,我能够将凤凰城应用程序与Django应用程序一起部署而没有任何类型的问题,但是当使用这种类型的配置部署2个或更多Django应用程序时,我遇到了问题。出于某种原因,达芙妮知道必须不断读取哪些端 Eloquent 能接收请求,但它只会读取所有这些端口,并根据需要将其提供给任何人。例如,如果我在同一服务器上运行DJANGO_APP_1DJANGO_APP_2(具有不同的Nginx配置和明显不同的系统端口),则有时DJANGO_APP_2的Daphne Workers会窃取用于DJANGO_APP_1的请求,反之亦然。我无法查明问题的根源,但我认为这与达芙妮的 worker 在某种程度上与他们所涉及的项目无关。 (只是一个理论,我没有时间检查他们的代码)。

    09-27 02:41