Сокеты распределения нагрузки на горизонтально масштабируемом сервере WebSocket?
Каждые несколько месяцев, размышляя над личным проектом, включающим сокеты, у меня возникает вопрос: "Как бы вы правильно загрузили балансировочные сокеты на динамически масштабируемом сервере WebSocket с горизонтальным масштабированием?"
Я понимаю теорию горизонтального масштабирования WebSocket s и использования моделей pub/sub для передачи данных на нужный сервер, который поддерживает сокетное соединение для конкретного пользователя. Я думаю, что я понимаю способы эффективной идентификации сервера с наименьшим количеством текущих сокетных подключений, которые я хотел бы также перенаправить на новое сокетное соединение. Чего я не понимаю, так это как эффективно маршрутизировать новые сокетные соединения на выбранный вами сервер с низким количеством сокетов.
Я не думаю, что этот ответ будет привязан к конкретной реализации сервера, а скорее может быть применен к большинству серверов. Я мог легко увидеть, как я реализую это с помощью vert.x, node.js или даже perfect.
2 ответа
Во-первых, вам нужно определить границы проблемы, о которой вы спрашиваете. Если вы действительно говорите о динамическом горизонтальном масштабировании, когда вы увеличиваете или уменьшаете количество серверов в зависимости от общей нагрузки, то это еще более сложная проблема, чем просто выяснение, куда направить последнее входящее новое сокетное соединение.
Чтобы решить эту проблему, у вас должен быть способ "перемещения" сокета с одного хоста на другой, чтобы вы могли очистить соединения от хоста, который вы хотите уменьшить (я предполагаю, что истинное динамическое масштабирование идет вверх и вниз). Обычный способ, которым я видел, что это сделано, - это задействовать сотрудничающего клиента, когда вы говорите клиенту о повторном подключении, а при повторном подключении он балансирует нагрузку на другой сервер, чтобы вы могли отключить тот, который вы хотели сократить. Если у вашего клиента уже есть логика автоматического переподключения (как у socket.io), вы можете просто заставить сервер закрыть соединение, и клиент автоматически переподключится.
Что касается балансировки нагрузки входящих клиентских соединений, вы должны решить, какую метрику нагрузки вы хотите использовать. В конечном счете, вам нужно получить оценку для каждого серверного процесса, которая скажет вам, насколько "занят" вы думаете, что вы можете устанавливать новые соединения на менее загруженном сервере. Элементарная оценка будет просто количеством текущих соединений. Если у вас большое количество подключений к одному серверному процессу (десятки тысяч), и в вашем приложении нет особой причины, по которой некоторые могут быть намного более загруженными, чем другие, тогда закон больших чисел, вероятно, усредняет нагрузку, чтобы вы могли сойти с рук. сколько подключений имеет каждый сервер. Если использование соединений не такое уж справедливое или даже нечетное, вам, возможно, придется также учитывать какое-то временное скользящее среднее значение загрузки ЦП вместе с общим количеством соединений.
Если вы собираетесь балансировать нагрузку между несколькими физическими серверами, то вам потребуется балансировщик нагрузки или прокси-служба, к которой все подключаются изначально, и этот прокси-сервер может просматривать показатели для всех работающих в данный момент серверов в пуле и назначать соединение для один с самым низким текущим счетом. Это можно сделать либо с помощью схемы прокси, либо (более масштабируемой) с помощью перенаправления, чтобы прокси-сервер не работал после первоначального назначения.
Затем у вас также может быть процесс, который регулярно проверяет ваш показатель нагрузки (однако вы решили его рассчитать) на всех серверах в кластере и решает, когда развернуть новый сервер, или когда его убрать, или когда ситуация слишком велика. баланса на данном сервере, и этому серверу нужно сказать, чтобы отключить несколько соединений, заставляя их балансировать.
Чего я не понимаю, так это как эффективно маршрутизировать новые сокетные соединения на выбранный вами сервер с низким количеством сокетов.
Как описано выше, вы используете либо схему прокси, либо схему перенаправления. При более высокой стоимости во время соединения я предпочитаю схему перенаправления, потому что она более масштабируема при работе и создает меньше точек отказа для существующего соединения. Все клиенты подключаются к вашему серверу шлюза входящих соединений, который отвечает за знание текущей оценки нагрузки для каждого из серверов в ферме и на основании этого назначает входящее соединение узлу с наименьшей оценкой, и это новое соединение затем перенаправляется восстановить соединение с одним из конкретных серверов в вашей ферме.
Я также видел балансировку нагрузки, выполненную исключительно с помощью специальной реализации DNS. Клиент запрашивает IP-адрес для farm.somedomain.com
и этот пользовательский DNS-сервер дает им IP-адрес хоста, которому он их назначает. Каждый клиент, который ищет IP-адрес для farm.somedomain.com
может получить другой IP-адрес. Вы вращаете хосты вверх или вниз, добавляя или удаляя их с настраиваемого DNS-сервера, и именно этот настраиваемый DNS-сервер должен содержать логику для знания логики балансировки нагрузки и текущих показателей нагрузки всех работающих хостов.
Направляйте запросы websocket на балансировщик нагрузки, который принимает решение о том, куда отправлять соединения.
Например, HAProxy имеет leastconn
метод для длинных соединений, который выбирает наименее недавно использовавшийся сервер с наименьшим количеством соединений.
Веса внутреннего сервера HAProxy также могут быть изменены внешними входами, @jfriend00 подробно описал технические характеристики взвешивания в своем ответе.
Я нашел этот проект, который может быть полезен:https://github.com/apundir/wsbalancer
Отрывок из описания:
Балансировщик веб-сокетов - это обратный прокси-сервер с отслеживанием состояния для веб-сокетов. Он распределяет входящие веб-сокеты по нескольким доступным бэкэндам. Помимо балансировки нагрузки, балансировщик также заботится о прозрачном переключении с одного бэкэнда на другой в случае аварийного сбоя в середине сеанса. Во время этого аварийного переключения соединение удаленного клиента сохраняется как есть, поэтому удаленный клиент даже не видит этого аварийного переключения. Предпринимаются все попытки, чтобы ни одно сообщение не было отброшено во время переключения при отказе.
Что касается вашего вопроса: это новое соединение будет маршрутизироваться балансировщиком нагрузки, если он настроен для этого.
Как упоминалось в @Matt, например, с HAProxy, использующим параметр минимального подключения.