Nginx и Flask-socketio Websockets: живы, но не обмениваются сообщениями?

У меня были небольшие проблемы с тем, чтобы Nginx нормально работал с библиотекой Python Flask-socketio (которая основана на gevent). В настоящее время, поскольку мы активно развиваемся, я пытаюсь заставить Nginx работать просто как прокси. Для отправки страниц я могу заставить это работать, либо напрямую запустив приложение flask-socketio, либо запустив gunicorn. Одна загвоздка: обмен сообщениями через веб-сокет не работает. Страницы успешно размещены и отображаются. Однако, когда я пытаюсь использовать веб-сокеты, они не работают. Они достаточно живы, чтобы веб-сокет думал, что он подключен, но они не будут отправлять сообщение. Если я удалю прокси Nginx, они будут работать. Firefox выдает мне эту ошибку, когда я пытаюсь отправить сообщение:

Firefox не может установить соединение с сервером по адресу ws: ///socket.io/1/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;
    }
  } 
}

Я сделал файл конфигурации как смесь общего примера и конкретного веб-сокета, но попытка возиться с ним не решила проблему. Кроме того, я использую вызов werkzeug Proxy_Fix в моем Flask app.wsgi_app, когда я использую его в режиме wsgi. Я пробовал с этим и без него, однако безрезультатно. Если у кого-то есть понимание, я буду всем ушами / глазами.


person Namey    schedule 08.04.2014    source источник
comment
Разве вы не должны проксировать свое соединение с веб-сокетом на какой-то некорневой URL-адрес? Как вы предоставляете клиенту файлы HTTP / JS, если все идет через веб-сокет?   -  person Miguel    schedule 08.04.2014
comment
В производственной среде nginx справится с этим с некоторыми дополнительными настройками конфигурации. Прямо сейчас, под капотом Flask занимается всем. Он имеет собственные возможности маршрутизации для статических файлов. По сути, я использую некоторые конечные точки в строке / js /, / static / и т.д., которые приводят к соответствующим вызовам Flask send_static_file туда, где они хранятся. Удобно для тестирования, но явно не так, как можно масштабировать. Однако все они работают с указанной выше конфигурацией nginx. Ошибаются только сообщения веб-сокета.   -  person Namey    schedule 09.04.2014
comment
Я исправил это сейчас. На самом деле это были две проблемы. Во-первых, flask-socketio хочет, чтобы сокеты располагались в '/socket.io'. Во-вторых, Ubuntu 12 использует старую версию NginX. Пока я обновлял его, выполнив очистку и установку (обычный apt-get, а не специально загружая самое последнее стабильное репо), он был возвращен к старой версии, пока возился с первой проблемой. Вставлю это в ответ и закрою позже сегодня, чтобы помочь другим бедным заблудшим душам.   -  person Namey    schedule 02.05.2014


Ответы (1)


Мне удалось это исправить. Проблемы не были специфичными для flask-socketio, но они были специфичны для Ubuntu, NginX и gevent-socketio. Присутствовали две важные проблемы:

  1. Ubuntu 12.04 имеет действительно древнюю версию nginx (1.1.19 против 1.6.x для стабильных версий). Почему? Кто знает. Что мы точно знаем, так это то, что эта версия не поддерживает веб-сокеты каким-либо полезным образом, поскольку 1.3.13 - это о самом раннем, который вам следует использовать.
  2. По умолчанию gevent-socketio ожидает, что ваши сокеты будут в расположении /socket.io. Вы можете обновить все HTTP-соединение, но у меня возникли проблемы с его правильной работой (особенно после того, как я добавил SSL).
  3. Я исправил # 1, но, возясь с ним, я очистил nginx и установил apt-get ... версию nginx по умолчанию в Ubuntu. Затем я был загадочно сбит с толку, почему все работает еще хуже, чем раньше. Многие файлы .conf доблестно погибли в этой битве.

Если вы пытаетесь отлаживать веб-сокеты в этой конфигурации, я бы рекомендовал следующие шаги:

  1. Проверьте свою версию nginx с помощью nginx -v. Если он меньше 1,4, обновите его.
  2. Проверьте настройки nginx.conf. Вам необходимо убедиться, что соединение обновляется.
  3. Убедитесь, что IP-адрес и порт вашего сервера соответствуют обратному прокси-серверу nginx.conf.
  4. Убедитесь, что ваш клиент (например, socketio.js) подключается к нужному месту и порту с правильным протоколом.
  5. Проверьте заблокированные порты. Я был на EC2, поэтому вам нужно вручную открыть 80 (HTTP) и 443 (SSL / HTTPS).

Только что проверив все это, можно сделать выводы.

  1. Обновление до последней стабильной версии nginx в Ubuntu (полная ссылка) можно выполнить следующим образом:

    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, вы можете использовать установщик, и у вас меньше шансов получить плохую версию. .

  2. Многие файлы конфигурации для этого могут сбивать с толку, поскольку nginx официально добавил сокеты примерно в 2013 году, что сделало более ранние конфигурации обходных путей устаревшими. Существующие файлы конфигурации, как правило, не охватывают все основы nginx, gevent-socketio и SSL вместе, а имеют их все по отдельности (Руководство по Nginx, Gevent-socketio, Node.js с 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;
          }
        }
     }
    
  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. Открытие портов - это скорее ошибка сервера или superuser, так как это будет во многом зависеть от вашего брандмауэра. Информацию об Amazon EC2 см. здесь.

  6. Если попытка всего этого не сработает, плачьте. Затем вернитесь в начало списка. Потому что вы могли случайно переустановить старую версию nginx.

person Namey    schedule 02.05.2014
comment
На самом деле я не пробовал. Я бы не подозревал, что нет, согласно этому обсуждению. Socket.io работает на веб-сокетах. Веб-сокеты работают на сокетах TCP. Я не думаю, что есть способ заставить Socket.io работать на сокете Unix / голом сокете TCP, если только вы не выполнили свою собственную повторную реализацию или эта функциональность не была добавлена ​​по какой-либо причине: stackoverflow.com/questions/28306740/ - person Namey; 18.01.2017