Nginx和Flask-socketio Websockets:Alive但不是Messaging?

时间:2014-04-08 03:37:47

标签: sockets nginx flask socket.io gevent-socketio

我在使用Python Flask-socketio库(基于gevent)很好地使用Nginx时遇到了一些麻烦。目前,由于我们正在积极开发,我试图让Nginx作为代理工作。对于发送页面,我可以通过直接运行flask-socketio app或者通过gunicorn来实现这一点。一个问题:websocket消息传递似乎不起作用。页面已成功托管和显示。但是,当我尝试使用websockets时,它们不起作用。它们还活着,websocket认为它已连接,但它们不会发送消息。如果我删除Nginx代理,它们确实有效。当我尝试发送消息时,Firefox给出了这个错误:

  

Firefox无法在ws:///socket.io/1/websocket/建立与服务器的连接。

网址是服务器所在的位置,唯一ID只是一堆随机数字。它似乎做得足以使连接保持活动(例如,客户端认为它已连接),但无法通过websocket发送消息。我不得不认为这个问题与代理的某些部分有关,但是在调试问题时遇到了麻烦(部分原因是这是我第一次使用Flask-socketIO和nginx)。我用于nginx的配置文件是:

user       <user name>;  ## This is set to the user name for the remote SSH session
worker_processes  5;

events {
  worker_connections  1024;  ## Default: 1024
}

http {
  default_type application/octet-stream;
  log_format   main '$remote_addr - $remote_user [$time_local]  $status '
    '"$request" $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
  sendfile     on;
  server_names_hash_bucket_size 128; # this seems to be required for some vhosts

  server {
    listen 80;
    server_name _;
    location / {
        proxy_pass http://localhost:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
  } 
}

我将配置文件作为一个通用示例的混合和特定于websocket的混合,但是试图摆弄它并没有解决问题。另外,当我在wsgi模式下使用它时,我在我的Flask app.wsgi_app上使用werkzeug Proxy_Fix调用。然而,无论如何,我都试过它,但无济于事。如果有人有一些洞察力,我会全力以赴。

1 个答案:

答案 0 :(得分:11)

我设法解决了这个问题。这些问题并非特定于烧瓶,但它们特定于Ubuntu,NginX和gevent-socketio。存在两个重要问题:

  1. Ubuntu 12.04有一个真正古老的nginx版本(稳定版本为1.1.19 vs 1.6.x)。为什么?谁知道。我们所知道的是,这个版本不支持任何有用的websockets,因为1.3.13是你应该使用的about the earliest
  2. 默认情况下,gevent-socketio要求您的套接字位于/socket.io位置。您可以升级整个HTTP连接,但我在使其正常工作时遇到了一些麻烦(特别是在我将SSL投入混合之后)。
  3. 我修复了#1,但是在摆弄它时我通过nginx和apt-get安装来清除...在Ubuntu上的默认版本的nginx。然后,我神秘地迷惑为什么事情比以前更糟糕。许多.conf文件在这场战斗中勇敢地丧生。
  4. 如果尝试在此配置中调试websockets,我建议执行以下步骤:

    1. 通过'nginx -v'检查你的nginx版本。如果它小于1.4,请升级它。
    2. 检查您的nginx.conf设置。您需要确保连接升级。
    3. 检查您的服务器IP和端口是否与您的nginx.conf反向代理匹配。
    4. 检查您的客户端(例如,socketio.js)是否使用正确的协议连接到正确的位置和端口。
    5. 检查您的阻止端口。我在EC2上,所以你必须手动打开80(HTTP)和443(SSL / HTTPS)。
    6. 刚刚检查了所有这些东西,有一些需要。

      1. 在Ubuntu(full ref)上升级到最新的稳定nginx版本可以通过以下方式完成:

        sudo apt-get install python-software-properties
        sudo apt-get install software-properties-common
        sudo add-apt-repository ppa:nginx/stable
        sudo apt-get update
        sudo apt-get install nginx
        

        在像Windows这样的系统中,您可以使用installer,并且不太可能获得不良版本。

      2. 许多配置文件可能令人困惑,因为nginx在2013年左右正式添加套接字,使早期的解决方案配置过时。现有的配置文件不一定涵盖nginx,gevent-socketio和SSL的所有基础,而是将它们全部分开(Nginx TutorialGevent-socketioNode.js with SSL)。带有flask-socketio(包装gevent-socketio)和SSL的nginx 1.6的配置文件是:

        user <user account, probably optional>;
        worker_processes  2;
        error_log  /var/log/nginx/error.log;
        pid        /var/run/nginx.pid;
        
        events {
            worker_connections  1024;
        }
        
        http {
            include mime.types;
            default_type       application/octet-stream;
            access_log         /var/log/nginx/access.log;
            sendfile           on;
        #   tcp_nopush         on;
            keepalive_timeout  3;
        #   tcp_nodelay        on;
        #   gzip               on;
            client_max_body_size 20m;
            index              index.html;
        
            map $http_upgrade $connection_upgrade {
                    default upgrade;
                    ''      close;
            }
        
            server {
              # Listen on 80 and 443
              listen 80 default;
              listen 443 ssl;  (only needed if you want SSL/HTTPS)
              server_name <your server name here, optional unless you use SSL>;
        
              # SSL Certificate (only needed if you want SSL/HTTPS)
              ssl_certificate <file location for your unified .crt file>;
              ssl_certificate_key <file location for your .key file>;
        
              # Optional: Redirect all non-SSL traffic to SSL. (if you want ONLY SSL/HTTPS)
              # if ($ssl_protocol = "") {
              #   rewrite ^ https://$host$request_uri? permanent;
              # }
        
              # Split off basic traffic to backends
              location / {
                proxy_pass http://localhost:8081; # 127.0.0.1 is preferred, actually.
                proxy_redirect off;
              }
        
              location /socket.io {
                proxy_pass          http://127.0.0.1:8081/socket.io; # 127.0.0.1 is preferred, actually.
                proxy_redirect off;
                proxy_buffering off; # Optional
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
              }
            }
         }
        
      3. 检查您的Flask-socketio是否使用正确的端口很容易。这足以使用上述内容:

        from flask import Flask, render_template, session, request, abort
        import flask.ext.socketio
        FLASK_CORE_APP = Flask(__name__)
        FLASK_CORE_APP.config['SECRET_KEY'] = '12345' # Luggage combination
        SOCKET_IO_CORE = flask.ext.socketio.SocketIO(FLASK_CORE_APP)
        
        @FLASK_CORE_APP.route('/')
        def index():
            return render_template('index.html')
        
        @SOCKET_IO_CORE.on('message')
        def receive_message(message):
            return "Echo: %s"%(message,)
        
        SOCKET_IO_CORE.run(FLASK_CORE_APP, host=127.0.0.1, port=8081)
        
      4. 对于像socketio.js这样的客户端,连接应该很容易。例如:

        <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/0.9.16/socket.io.min.js"></script>
        <script type="text/javascript">
            var url = window.location.protocol + document.domain + ':' + location.port,
                socket = io.connect(url);
            socket.on('message', alert);
            io.emit("message", "Test")
        </script>
        
      5. 打开端口实际上更多是server-faultsuperuser问题,因为它在很大程度上取决于您的防火墙。对于Amazon EC2,请参阅here

      6. 如果尝试所有这些都不起作用,请哭泣。然后返回列表顶部。因为您可能只是偶然重新安装了旧版本的nginx。