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 в заголовке запроса.
Если есть лучший способ выполнить выше, чем то, что я пытаюсь, я все для этого.
Ответы
Ответ 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:
У меня есть это и запущено с использованием их базового прокси-примера - независимо от того, разрешает ли вам выпустить сеансы или нет, только вы узнаете.