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

Posted

技术标签:

【中文标题】Nginx 和 Flask-socketio Websockets:活着但不是消息传递?【英文标题】:Nginx and Flask-socketio Websockets: Alive but not Messaging? 【发布时间】:2014-05-20 13:26:54 【问题描述】:

我在让 nginx 与 Python Flask-socketio 库(基于 gevent)很好地配合使用时遇到了一些麻烦。目前,由于我们正在积极开发,我正试图让 Nginx 仅作为代理工作。对于发送页面,我可以通过直接运行 flask-socketio 应用程序或通过 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 调用。然而,我已经尝试过,不管有没有,都无济于事。如果有人有一些见解,我会全神贯注。

【问题讨论】:

你不应该在一些非根 URL 上代理你的 websocket 连接吗?如果一切都通过 websocket,你如何将 HTTP/JS 文件提供给客户端? 在生产中,nginx 将通过一些额外的配置设置来处理它。现在,在引擎盖下,Flask 正在处理所有事情。它有自己的静态文件路由功能。基本上,我正在使用 /js/、/static/ 等的一些端点,这些端点会导致相应的 Flask send_static_file 调用到这些端点的存储位置。便于测试,但显然不是你如何扩展任何东西。但是,这些都适用于上述 nginx 配置。只有 websocket 消息失败。 我现在已经解决了这个问题。这实际上是两个问题。首先,flask-socketio 希望套接字出现在“/socket.io”。其次,Ubuntu 12 使用了一个古老版本的 NginX。当我升级它时,通过执行清除和安装(常规 apt-get,而不是专门下载最新的稳定 repo),它在摆弄第一个问题时恢复到旧版本。将把它放在答案中并在今天晚些时候关闭它,以帮助其他可怜的迷失的灵魂。 【参考方案1】:

我设法解决了这个问题。这些问题并非特定于 flask-socketio,而是特定于 Ubuntu、NginX 和 gevent-socketio。存在两个重要问题:

    Ubuntu 12.04 有一个真正古老的 nginx 版本(1.1.19 vs 1.6.x 用于稳定版本)。为什么?谁知道。我们所知道的是,此版本不以任何有用的方式支持 websocket,因为 1.3.13 是您应该使用的 about the earliest。 默认情况下,gevent-socketio 期望您的套接字位于 /socket.io 位置。您可以升级整个 HTTP 连接,但我很难让它正常工作(尤其是在我将 SSL 加入混合之后)。 我修复了 #1,但在摆弄它时,我被 nginx 清除并安装了 apt-get...Ubuntu 上 nginx 的默认版本。然后,我莫名其妙地感到困惑,为什么事情比以前更糟。许多 .conf 文件在这场战斗中英勇牺牲。

如果尝试在此配置中调试 websockets,我会推荐以下步骤:

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

刚刚检查了所有这些东西,有一些收获。

    在 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,这样就不太可能得到错误的版本。

    为此的许多配置文件可能会令人困惑,因为 nginx 大约在 2013 年正式添加了套接字,使得早期的解决方法配置过时了。现有的配置文件并不倾向于涵盖 nginx、gevent-socketio 和 SSL 的所有基础,而是将它们全部分开(Nginx Tutorial、Gevent-socketio、Node.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;
          
        
     
    

    检查您的 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>
    

    打开端口实际上更像是server-fault 或superuser 问题,因为它很大程度上取决于您的防火墙。对于 Amazon EC2,请参阅 here。

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

【讨论】:

其实我没试过。根据这个讨论,我怀疑不是。 Socket.io 在 Websockets 上运行。 Websocket 在 TCP 套接字上运行。我认为没有任何方法可以让 Socket.io 在 Unix 套接字/裸 TCP 套接字上运行,除非您自己重新实现或由于某种原因添加了该功能:***.com/questions/28306740/…

以上是关于Nginx 和 Flask-socketio Websockets:活着但不是消息传递?的主要内容,如果未能解决你的问题,请参考以下文章

Flask-SocketIO 未使用 Gevent/Gevent-websocket

Flask 和 Flask-SocketIO 集成和导入错误

使用 Flask-socketio 和 socketIO 客户端

flask-SocketIO 和 eventlet 有问题

[翻译] flask-SocketIO

Flask 和 Flask-SocketIO [重复]