Haproxy + netty: способ предотвратить исключения при сбросе соединения?

Мы используем haproxy перед серверной частью netty-3.6. Мы обслуживаем огромное количество подключений, некоторые из которых могут быть длительными.

Теперь проблема в том, что когда haproxy закрывает соединение для перебалансировки, он делает это, отправляя tcp-RST. Когда класс sun.nio.ch, используемый netty, видит это, он выдает исключение IOException: «Сброс соединения одноранговым узлом».

След:

sun.nio.ch.FileDispatcherImpl.read0(Native Method):1 in ""
sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39):1 in ""
sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:225):1 in ""
sun.nio.ch.IOUtil.read(IOUtil.java:193):1 in ""
sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:375):1 in ""
org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:64):1 in ""
org.jboss.netty.channel.socket.nio.AbstractNioWorker.process(AbstractNioWorker.java:109):1 in ""
org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:312):1 in ""
org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:90):1 in ""
org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:178):1 in ""
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145):1 in ""
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615):1 in ""
java.lang.Thread.run(Thread.java:724):1 in ""

Это вызывает следующие проблемы для каждой конфигурации:

вариант http-pretend-keepalive

Это то, что работает лучше всего (поскольку haproxy, похоже, закрывает большинство соединений с помощью FIN, а не RST), но все же производит около 3 исключений на сервер в секунду. Кроме того, он эффективно нейтрализует балансировку нагрузки, потому что некоторые входящие соединения очень давнишние с очень высокой пропускной способностью: с pretend-keepalive они никогда не перебалансируются на другой сервер с помощью haproxy.

опция http-keep-alive

Поскольку наш бэкэнд ожидает, что соединения keep-alive действительно поддерживаются (и, следовательно, не закрывают их сами по себе), этот параметр сводится к тому, что каждое соединение в конечном итоге создает одно исключение, которое, в свою очередь, приводит к сбою наших серверов. Мы пробовали добавить предпоследний сервер, но это мало помогло.

вариант http-server-close

Теоретически это должно работать как для правильной балансировки нагрузки, так и без исключений. Однако, похоже, что после того, как наши backend-серверы ответят, начинается гонка относительно того, какая сторона первой отправляет свой RST: haproxy или наш зарегистрированный ChannelFutureListener.CLOSE. На практике мы все еще получаем слишком много исключений и наши серверы падают.

Интересно, что чем больше работников мы снабжаем наши каналы, тем больше исключений. Думаю, это ускоряет чтение больше, чем письмо.

В любом случае, я некоторое время читал о различных параметрах каналов и сокетов в netty, а также о haproxy и не нашел ничего, что походило бы на решение (или работало, когда я его пробовал).


person Benjaminssp    schedule 04.02.2014    source источник


Ответы (5)


Примечание. Насколько я понимаю, вам не нужно беспокоиться об исключении сброса подключения, если только у вас нет пула подключений на вашем конце с Keep-Alive Connections.

Я столкнулся с аналогичной проблемой с множеством сбросов подключения (RST) (раньше это было 5-20 раз в окне в 10 секунд, в зависимости от нагрузки) при использовании HAProxy для наших служб.
Вот как я это исправил.

У нас была система, в которой соединения всегда поддерживаются (keep-alive всегда истинно на уровне HTTP-соединения. То есть, как только соединение установлено, мы повторно используем это соединение из пул HTTP-соединений для последующих вызовов вместо создания новых.)

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

  1. Когда клиент тайм-аута HAProxy или истекло время ожидания сервера при простаивающем соединении.
    Эта конфигурация была для нас установлена ​​как 60 секунд. Поскольку у нас есть пул подключений, при уменьшении нагрузки на сервер некоторые из этих подключений не используются в течение минуты.
    Затем эти соединения были закрыты HAProxy с помощью сигнала RST.

  2. Когда параметр HAProxy предпочитает-последний-сервер не был установлен.
    Согласно Документам:

Реальное использование - это соединения для поддержания активности, отправляемые на серверы. Когда используется эта опция, haproxy будет пытаться повторно использовать то же соединение, которое подключено к серверу, вместо перенастройки на другой сервер, что приведет к закрытию соединения.

Поскольку это не было установлено, каждый раз, когда соединение повторно использовалось из пула, HAProxy использовал для закрытия этого соединения с помощью сигнала RST и создания нового соединения на другом сервере (Поскольку наш балансировщик нагрузки был настроен на циклический перебор < / em>). Из-за этого весь пул подключений становился бесполезным.

Итак, конфигурация, которая работала нормально:

  1. : Таким образом, существующие подключения к серверу будут повторно использованы.
    Примечание. Это НЕ приведет к тому, что балансировщик нагрузки будет использовать предыдущий сервер вместо нового для нового соединения. Принятие решения о новых подключениях всегда основывается на алгоритме балансировки нагрузки. Этот параметр предназначен только для существующего соединения, которое уже было активным между клиентом и сервером.
    Когда я тестировал этот параметр, новое соединение все еще шло на сервер2, даже если соединение до этого было отправлено на server1.
  2. баланс lessconn: с циклическим перебором < / strong> и Keep-Alive, возможен перекос соединений с одним сервером. (Скажем, есть только 2 сервера, и когда один сервер выходит из строя из-за развертывания, тогда все новые соединения начнут переходить на другой сервер. Таким образом, даже когда появится server2, циклический перебор все равно будет распределять новые запросы один на server1 и один на в качестве альтернативы server2. Несмотря на то, что server1 имеет много подключений на своем конце. Таким образом, нагрузка на сервер никогда не бывает точно сбалансированной.).
  3. Установка клиента тайм-аута HAProxy или тайм-аут сервера до 10 минут. Это увеличило количество времени, в течение которого наши соединения могли бездействовать.
  4. Реализован IdleConnectionMonitor: для тайм-аута установлено значение Через 10 минут шансы RST от HAProxy были уменьшены, но не устранены.
    Чтобы удалить его полностью, мы добавили IdleConnectionMonitor, который отвечал за закрытие соединений, которые простаивали более 9 минут.


С этими конфигурациями мы могли

  • Устранение сброса подключения
  • Получите работу пула подключений
  • Обеспечение равномерного распределения нагрузки между серверами независимо от времени их запуска.

Надеюсь это поможет!!

person Kishore Bandi    schedule 12.10.2016

Обработчик Tomcat Nio просто делает:

} catch (java.net.SocketException e) {
    // SocketExceptions are normal
    Http11NioProtocol.log.debug
        (sm.getString
         ("http11protocol.proto.socketexception.debug"), e);

} catch (java.io.IOException e) {
    // IOExceptions are normal
    Http11NioProtocol.log.debug

        (sm.getString
         ("http11protocol.proto.ioexception.debug"), e);

}

Таким образом, кажется, что первоначальный выброс внутренних классов sun (sun.nio.ch.FileDispatcherImpl) действительно неизбежен, если вы не переопределите их самостоятельно.

person Benjaminssp    schedule 07.02.2014

«Сброс соединения одноранговым узлом» обычно вызывается записью в соединение, которое уже было закрыто другим концом. Это заставляет одноранговый узел отправить RST. Но почти наверняка он уже отправил FIN. Я бы пересмотрел ваши предположения здесь. Очень немногие приложения намеренно отправляют RST. Скорее всего, вы столкнулись с ошибкой протокола приложения. Если это неизбежно, ECONNRESET тоже.

person user207421    schedule 05.02.2014
comment
Мой источник для haproxy, специально отправляющего RST, находится здесь: formilux.org/archives/haproxy/ 1111 / 5108.html Haproxy использует RST, чтобы закрыть соединение с внутренним сервером именно из-за этого, иначе это не сработает вообще. Убедившись, что tcpdump для меня не показывает никаких FIN, отправляемых на бэкэнд, за исключением http-pretend-keepalive. ps: Использование последней версии haproxy, 1.5.21 - person Benjaminssp; 06.02.2014

Попробуйте с

  • вариант http-туннель
  • нет возможности повторной отправки

не уверен в повторной отправке, но http-tunnel устранил проблему с нашей стороны.

person Rajashekhar S Choukimath    schedule 21.11.2014
comment
haproxy docs не рекомендует этого: [http-tunnel] не следует используется, поскольку создает множество проблем с ведением журнала и обработкой HTTP. - person Tom; 18.07.2016

Начиная с haproxy 1.5 он теперь отправляет FIN (FIN,ACK) на бэкэнд-сервер, тогда как harpoxy 1.4 раньше отправлял RST. Это, вероятно, поможет в этом сценарии.

Если он найдет это задокументированным, я добавлю ссылку ...

person KCD    schedule 08.04.2016
comment
Трассировка пакетов, которую я только что сделал, показывает, что haproxy отправляет RST по крайней мере haproxy 1.6.4. - person Tom; 18.07.2016
comment
Я тестировал последнюю версию HAProxy, и я все еще использую RST в качестве сигнала, используемого HAProxy. - person Kishore Bandi; 12.10.2016