关于套接字:Nginx和Flask-socketio Web套接字:尚可,但不是消息传递?

Nginx and Flask-socketio Websockets: Alive but not Messaging?

让Nginx与Python Flask-socketio库(基于gevent)很好地协作时,我一直遇到一些麻烦。当前,由于我们正在积极开发中,因此我试图让Nginx充当代理。对于发送页面,我可以通过直接运行flask-socketio应用程序或通过运行gunicorn使其工作。一个障碍:websocket消息传递似乎无效。页面已成功托管并显示。但是,当我尝试使用websocket时,它们不起作用。他们还活着,以至于Websocket认为它已连接,但是他们不会发送消息。如果我删除了Nginx代理,它们就会起作用。当我尝试发送消息时,Firefox给了我这个错误:

Firefox can't establish a connection to the server at ws:///socket.io/1/websocket/.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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调用。我尝试过这种情况,但没有,毫无结果。如果有人有见识,我将全神贯注。


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

  • Ubuntu 12.04拥有真正古老的Nginx版本(稳定版本为1.1.19与1.6.x)。为什么?谁知道。我们所知道的是该版本不以任何有用的方式支持websockets,因为1.3.13大约是您应该最早使用的版本。
  • 默认情况下,gevent-socketio希望您的套接字位于/socket.io位置。您可以升级整个HTTP连接,但是在使其正常工作时遇到了一些麻烦(特别是在我将SSL放入混合中之后)。
  • 我修复了#1,但由于弄不清它,我被nginx清除并安装apt-get ... Ubuntu上nginx的默认版本。然后,我对事情为什么比以前更糟感到困惑。许多.conf文件在这场战斗中英勇牺牲。
  • 如果尝试在此配置中调试websocket,则建议执行以下步骤:

  • 通过" nginx -v"检查您的nginx版本。如果它小于1.4,请升级它。
  • 检查您的nginx.conf设置。您需要确保连接升级。
  • 检查您的服务器IP和端口是否与您的nginx.conf反向代理匹配。
  • 检查您的客户端(例如socketio.js)是否已使用正确的协议连接到正确的位置和端口。
  • 检查被阻止的端口。我使用的是EC2,因此您必须手动打开80(HTTP)和443(SSL / HTTPS)。
  • 刚刚检查了所有这些内容之后,便有了外卖服务。

  • 可以通过以下方法将Ubuntu升级到最新的稳定Nginx版本(完整参考):

    1
    2
    3
    4
    5
    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年正式添加了套接字,因此许多配置文件可能会令人困惑,这使得较早的解决方法配置已过时。现有的配置文件并不倾向于同时覆盖nginx,gevent-socketio和SSL的所有基础,而是将它们全部分开(Nginx教程,Gevent-socketio,带有SSL的Node.js)。使用flask-socketio(包装gevent-socketio)和SSL的nginx 1.6的配置文件是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    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使用正确的端口很容易。这足以解决上述问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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这样的客户端,连接应该很容易。例如:

    1
    2
    3
    4
    5
    6
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/0.9.16/socket.io.min.js">
    <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")
  • 打开端口实际上实际上是服务器故障或超级用户问题,因为打开端口在很大程度上取决于防火墙。对于Amazon EC2,请参阅此处。

  • 如果尝试所有这些方法均无效,请哭泣。然后返回到列表的顶部。因为您可能只是不小心重新安装了较旧的nginx版本。