Nginx, балансировка нагрузки с использованием алгоритмов липких и наименьших соединений одновременно

Мы используем Nginx в качестве балансировки нагрузки для нашего приложения websocket. Каждый сервер backend хранит информацию о сеансе, поэтому каждый запрос от клиента должен быть перенаправлен на тот же сервер. Поэтому для достижения этой цели мы используем директиву ip_hash:

upstream app {
    ip_hash;
    server 1;
}

Проблема возникает, когда мы хотим добавить еще один сервер:

upstream app {
    ip_hash;
    server 1;
    server 2;
}

Новые подключения идут на сервер 1 и сервер 2 - но это не то, что нам нужно в этой ситуации, так как загрузка на сервере 1 продолжает увеличиваться - нам все еще нужны липкие сеансы, но алгоритм least_conn тоже включен, поэтому наши два сервера получают примерно равная нагрузка.

Мы также рассмотрели использование Nginx-sticky-module, но в документе говорится, что если липкий файл cookie не будет доступен, он вернется к циклическому алгоритму Nginx по умолчанию, поэтому он также не решает проблему.

Итак, вопрос в том, можем ли мы объединить логику логики липких и наименьших соединений с помощью Nginx? Вы знаете, какие другие балансировки нагрузки решают эту проблему?

Ответы

Ответ 1

Возможно, использование модуля split_clients может помочь

upstream app {
    ip_hash;
    server 127.0.0.1:8001;
}

upstream app_new {
    ip_hash;
    server 127.0.0.1:8002;
}

split_clients "${remote_addr}AAA" $upstream_app {
    50% app_new;
    *   app;
}

Это разделит ваш трафик и создаст переменную $upstreap_app той, которую вы могли бы использовать, например:

server {
   location /some/path/ {
   proxy_pass http://$upstream_app;
}

Это временное решение для least_conn и балансировки нагрузки, которые работают с липкими сеансами, "недостатком" является то, что, если нужно добавить больше серверов, необходимо создать новый поток, например:

split_clients "${remote_addr}AAA" $upstream_app {
    30% app_another_server;
    30% app_new;
    *   app;
}

Для тестирования:

for x in {1..10}; do \
  curl "0:8080?token=$(LC_ALL=C; cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)"; done

Более подробную информацию об этом модуле можно найти в этой статье (Выполнение тестирования A/B)

Ответ 2

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

С HA Proxy у вас будет что-то вроде:

backend nodes
    # Other options above omitted for brevity
    cookie SRV_ID prefix
    server web01 127.0.0.1:9000 cookie check
    server web02 127.0.0.1:9001 cookie check
    server web03 127.0.0.1:9002 cookie check

Это просто означает, что прокси-сервер отслеживает запросы на серверы с помощью куки файлов.

Однако, если вы не хотите использовать HAProxy, я бы посоветовал вам настроить изменение вашей реализации сеанса для использования в памяти DB, например redis/memcached. Таким образом, вы можете использовать lessconn или любой другой алгоритм, не беспокоясь о сеансах.