Nginx和Flask-socketio Websockets: 活着但不传递消息?

5
我一直在努力让Nginx与基于gevent的Python Flask-socketio库(Python Flask套接字库)协同工作,但遇到了一些问题。目前,由于我们正在积极开发中,我正在尝试让Nginx作为代理工作。对于发送页面,我可以通过直接运行flask-socketio应用程序或通过gunicorn运行来实现。但是有一个问题:websocket消息似乎无法正常工作。页面已经成功托管和显示出来。然而,当我尝试使用websocket时,它们无法正常工作。虽然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调用。然而,无论我是否使用此项功能,问题都没有得到解决。如果有人有见解,我将洗耳恭听。


你应该将websocket连接代理到某个非根URL上,这不是更好吗?如果所有内容都通过websocket传输,那你又如何向客户端提供HTTP/JS文件呢? - Miguel Grinberg
在生产环境中,nginx将通过一些额外的配置设置来处理这个问题。目前,在幕后,Flask正在处理所有事情。它具有自己的静态文件路由功能。基本上,我使用了一些类似于/js/、/static/等的端点,它们会导致相应的Flask send_static_file调用到存储这些文件的位置。虽然方便测试,但显然不是扩展任何东西的方式。然而,所有这些都可以与上述nginx配置一起工作。只有websocket消息失败了。 - Namey
我已经解决了这个问题。实际上有两个问题。首先,flask-socketio希望套接字出现在“/socket.io”。其次,Ubuntu 12使用一个古老版本的NginX。虽然我升级了它,通过进行清除和安装(常规apt-get,而不是专门下载最新的稳定存储库),但在处理第一个问题时它被还原为古老的版本。今天晚些时候将把这个答案发布并关闭,以帮助其他可怜的迷失灵魂。 - Namey
1个回答

13
我成功解决了这个问题。问题不是特定于flask-socketio,而是特定于Ubuntu、NginX和gevent-socketio。存在两个显著的问题:
  1. Ubuntu 12.04拥有一个非常古老的nginx版本(1.1.19 vs 1.6.x for stable versions)。为什么?谁知道呢。我们知道的是,这个版本不支持WebSockets以任何有用的方式,因为1.3.13是你应该使用的最早版本。full ref
  2. 默认情况下,gevent-socketio希望您的sockets位于位置/socket.io。您可以升级整个HTTP连接,但我在将SSL混合使用后遇到了一些问题,难以正常工作。
  3. 我修复了第一个问题,但在调试期间我删除了我的nginx并使用apt-get安装了... Ubuntu上的默认版本。然后,我神秘地困惑于事情比以前更糟。许多.conf文件在这场战斗中英勇地失去了它们的生命。

如果尝试在此配置中调试WebSockets,我建议执行以下步骤:

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

刚刚检查了所有这些内容,有一些收获:

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

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之类的系统中,您可以使用安装程序,这样就不太可能遇到坏版本。

  • 许多配置文件可能会令人困惑,因为nginx大约在2013年正式添加了sockets,使早期的解决方法配置文件过时了。现有的配置文件通常不能涵盖nginx、gevent-socketio和SSL的所有方面,但是它们分别包括(Nginx教程Gevent-socketioNode.js with SSL)。nginx 1.6与flask-socketio(它包装了gevent-socketio)和SSL的配置文件如下:

    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;
          }
        }
     }
    
  • 检查您的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)
    
  • 对于像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>
    
  • 打开端口更像是一个服务器错误超级用户问题,因为这很大程度上取决于您的防火墙。对于Amazon EC2,请参见此处

  • 如果尝试所有这些都不起作用,请哭泣。然后返回列表顶部。因为你可能只是意外地重新安装了一个旧版本的nginx。


  • 1
    我实际上还没有尝试过。根据这个讨论,我认为不行。Socket.io运行在Websockets上。Websockets运行在TCP sockets上。我认为除非你自己重新实现或者出于某种原因添加了该功能,否则无法让Socket.io运行在Unix socket /裸TCP socket上:http://stackoverflow.com/questions/28306740/socket-io-connect-to-unix-socket - Namey

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接