Подтвердить что ты не робот

Node.js http-proxy отбрасывает запросы websocket

Хорошо, я потратил более недели, пытаясь понять это безрезультатно, поэтому, если у кого есть ключ, вы герой. Это не будет простым ответом на вопрос, если я не стану дураком.

Я использую node -http-proxy для прокси-липких сеансов для 16 node.js-рабочих, работающих на разных портах.

Я использую Socket.IO Web Sockets для обработки множества различных типов запросов, а также для использования традиционных запросов.

Когда я переключил свой сервер на проксирование через node -http-proxy, в нем иногда возникала новая проблема, мой сеанс Socket.IO не может установить соединение.

Я буквально не могу стабильно воспроизвести его для жизни меня, единственным способом превратить его в том, чтобы бросить много трафика с нескольких клиентов на сервер.

Если я перезагружаю пользовательский браузер, он иногда может повторно подключаться, а иногда и нет.

Sticky Sessions

У меня есть прокси-липкие сеансы, так как мое приложение проверяется подлинниками на рабочем месте и поэтому направляет запрос на основе его cookie Connect.SID(я использую connect/express).

Хорошо, код

Это мой файл proxy.js, который работает в node и маршрутизируется к каждому из рабочих:

var http = require('http');
var httpProxy = require('http-proxy');

// What ports the proxy is routing to.
var data = {
  proxyPort: 8888,
  currentPort: 8850,
  portStart: 8850,
  portEnd: 8865,
};

// Just gives the next port number.
nextPort = function() {
  var next = data.currentPort++;
  next = (next > data.portEnd) ? data.portStart : next;
  data.currentPort = next;
  return data.currentPort;
};

// A hash of Connect.SIDs for sticky sessions.
data.routes = {}

var svr = httpProxy.createServer(function (req, res, proxy) {

  var port = false;

  // parseCookies is just a little function
  // that... parses cookies.
  var cookies = parseCookies(req);  

  // If there is an SID passed from the browser.
  if (cookies['connect.sid'] !== undefined) {

    var ip = req.connection.remoteAddress;

    if (data.routes[cookies['connect.sid']] !== undefined) {

      // If there is already a route assigned to this SID,
      // make that route port the assigned port.
      port = data.routes[cookies['connect.sid']].port;
    } else {

      // If there isn't a route for this SID,
      // create the route object and log its
      // assigned port.
      port = data.currentPort;
      data.routes[cookies['connect.sid']] = {
        port: port,
      }

      nextPort();
    }

  } else {

    // Otherwise assign a random port, it will/
    // pick up a connect SID on the next go.
    // This doesn't really happen.
    port = nextPort();
  }

  // Now that we have the chosen port, 
  // proxy the request.
  proxy.proxyRequest(req, res, {
    host: '127.0.0.1',
    port: port
  });
}).listen(data.proxyPort);

// Now we handle WebSocket requests.
// Basically, I feed off of the above route
// logic and try to route my WebSocket to the
// same server regular requests are going to.
svr.on('upgrade', function (req, socket, head) {

  var cookies = parseCookies(req);  
  var port = false;

  // Make sure there is a Connect.SID,
  if (cookies['connect.sid'] != undefined) {

    // Make sure there is a route...
    if (data.routes[cookies['connect.sid']] !== undefined) {

      // Assign the appropriate port.
      port = data.routes[cookies['connect.sid']].port;
    } else {

      // this has never, ever happened, i've been logging it.
    }
  } else {

    // this has never, ever happened, i've been logging it.
  };

  if (port === false) {

    // this has never happened...
  };

  // So now route the WebSocket to the same port
  // as the regular requests are getting.
  svr.proxy.proxyWebSocketRequest(req, socket, head, {
    host: 'localhost',
    port: port
  });

});

Клиентская сторона/Явления

Socket подключается так:

var socket = io.connect('http://whatever:8888');

Примерно через 10 секунд при входе в систему я получаю эту ошибку на этом слушателе, что мало помогает.

socket.on('error', function (data) {
  // this is what gets triggered. ->
  // Firefox can't establish a connection to the server at ws://whatever:8888/socket.io/1/websocket/Nnx08nYaZkLY2N479KX0.
});

Запрос Socket.IO GET, который отправляет браузер, никогда не возвращается - он просто зависает в ожидании, даже после того, как ошибка возвращается, поэтому он выглядит как ошибка тайм-аута. Сервер никогда не отвечает.

Сторона сервера - рабочий

Вот как рабочий получает запрос на сокет. Довольно просто. Все работники имеют одинаковый код, поэтому вы думаете, что один из них получит запрос и подтвердит его...

app.sio.socketio.sockets.on('connection', function (socket) {
  // works... some of the time! all of my workers run this
  // exact same process.
});

Резюме

Это много данных, и я сомневаюсь, что кто-то хочет противостоять ему, но я полностью в тупике, не знаю, где проверить следующее, запишите следующий, что угодно, чтобы решить его. Я пробовал все, что знаю, чтобы понять, в чем проблема, безрезультатно.

UPDATE

Хорошо, я вполне уверен, что проблема заключается в этом заявлении на главной странице node -http-proxy github:

node -http-proxy поддерживает <= 0.8.x, если вы ищете a >= 0.10 совместимая версия пожалуйста, проверьте caronte

Я запускаю node.js v0.10.13, и явления точно так же, как некоторые комментируют в вопросах github по этому вопросу: он просто случайно отключает подключения к веб-узлам.

Я попытался реализовать caronte, "новую" вилку, но это совсем не документировано, и я изо всех сил старался объединить свои документы в работоспособное решение, но я не могу заставить его пересылать веб-узлы, мой Socket.IO переходит к опросу.

Есть ли другие идеи о том, как это реализовать и работать? node -http-proxy вчера загрузок 8200! Конечно, кто-то использует сборку node с этого года и прокси-серверы...

То, что я ищу именно

Я хочу выполнить прокси-сервер (предпочтительнее Node), который проксирует несколько рабочих node.js и направляет запросы через липкие сеансы на основе cookie браузера. Этот прокси должен будет стабильно поддерживать традиционные запросы, а также веб-сокеты.

Или...

Я не возражаю против выполнения вышеизложенного с помощью кластерных рабочих node, если это работает. Мое единственное реальное требование - поддерживать липкие сеансы на основе cookie в заголовке запроса.

Если есть лучший способ выполнить выше, чем то, что я пытаюсь, я все для этого.

4b9b3361

Ответ 1

В общем, я не думаю, что node не самый используемый вариант в качестве прокси-сервера, я для одного использования nginx как интерфейсный сервер для node, и это действительно отличная комбинация. Ниже приведены инструкции для установки и использования модуля липких сессий nginx.

Это легкий интерфейсный сервер с json-конфигурацией, прочный и очень хорошо протестированный.

nginx также намного быстрее, если вы хотите обслуживать статические страницы css. Он идеально подходит для настройки заголовков кеширования, перенаправления трафика на несколько серверов в зависимости от домена, липких сессий, сжатия css и javascript и т.д.

Вы также можете рассмотреть решение с открытым исходным кодом с чистой балансировкой нагрузки, например HAProxy. В любом случае я не верю, что node - лучший инструмент для этого, лучше использовать его для реализации только вашего бэкэнда и поставить перед ним что-то вроде nginx для обработки обычных задач сервера frontend.

Ответ 2

Я согласен с гексацианидом. Для меня было бы разумнее ставить в очередь работников через службу, например redis или какую-то систему Message Query. Рабочие будут поставлены в очередь через Redis Pub/Sub функциональные возможности через веб-узлы (которые проксированы). Работники обратились бы к ошибке, завершению или потоку данных в реальном времени с событием "данные". Возможно, просмотрите библиотеку kue. Вы также можете перевернуть свою собственную библиотеку. RabbitMQ - еще одна система для аналогичной цели.

Я использую socket.io, если вы уже используете эту технологию, но вам нужно использовать инструменты по назначению. Redis или система MQ будут иметь наибольший смысл и отлично сочетаются с websockets (socket.io) для создания приложений реального времени и проницательных приложений.

Сессия Affinity (липкие сессии) поддерживается с помощью Elastic LoadBalancer для aws, это поддерживает webSockets. Поставщик PaaS (Modulus) делает это точно. Theres также satalite, который предоставляет липкие сессии для node -http-proxy, однако я понятия не имею, поддерживает ли он webSockets.

Ответ 3

Я искал нечто очень похожее на это сам, с намерением генерировать (и уничтожать) Node.js узлы кластера на лету.

Отказ от ответственности: я бы по-прежнему не рекомендовал делать это с помощью Node; nginx более стабильна для архитектуры дизайна, которую вы ищете, или, что еще важнее, HAProxy (очень зрелая и легко поддерживает прокси-листинг). Как указывает @tsturzl, существует satellite, но с учетом небольшого объема загрузок я старался осторожно (по крайней мере, в производственной среде).

Тем не менее, поскольку у вас есть все, что уже настроено с помощью Node, перестройка и повторная архивация могут быть более эффективными, чем это стоит. Поэтому для установки caronte ветки с NPM:

  • Удалите предыдущую установку http-node-proxy Master с помощью npm uninstall node-proxy и/или sudo npm -d uninstall node-proxy

  • Загрузите caronte ветвь .zip и извлеките ее.

  • Запустить npm -g install /path/to/node-http-proxy-caronte
  • В моем случае установочная ссылка была сломана, поэтому мне пришлось запустить sudo npm link http-proxy

У меня есть это и запущено с использованием их базового прокси-примера - независимо от того, разрешает ли вам выпустить сеансы или нет, только вы узнаете.