У меня есть веб-приложение (Tomcat / Hibernate / DBCP 1.4), которое выполняет запросы к MySQL, и это отлично работает при определенной нагрузке, скажем, 50 запросов в секунду. Когда я маршрутизирую ту же умеренную нагрузку через HAProxy (все еще используя одну базу данных), я получаю сбой, возможно, один на каждые 500 запросов. Мое приложение сообщает:
Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 196,898 milliseconds ago. The last packet sent successfully to the server was 0 milliseconds ago.
at sun.reflect.GeneratedConstructorAccessor210.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1117)
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3567)
...
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3017)
...
Между тем в журнале HAProxy отображается множество записей, например:
27] mysql mysql/db03 0/0/34605 2364382 cD 3/3/3/3/0 0/0
Oct 15 15:43:12 localhost haproxy[3141]: 127.0.0.1:35500 [15/Oct/2012:15:42:50.0
"CD" явно указывает на состояние тайм-аута клиента. Итак, в то время как мое веб-приложение говорит, что HAProxy отказывается принимать новые соединения, HAProxy говорит, что мое веб-приложение не принимает данные обратно.
Я не включаю свою конфигурацию HAProxy, потому что я пробовал много разных значений параметров, практически с тем же результатом. В частности, я установил для maxconn как высокие, так и низкие значения, как в глобальном, так и в серверном разделах, и что всегда происходит в статистике, так это то, что максимальное количество сеансов возрастает не более чем примерно до 7. Размер моего пула JDBC также велик.
Можно ли совместно использовать пул JDBC и пул HAProxy? Люди раньше сталкивались с подобными проблемами?
У меня есть идея, как решить эту проблему, а именно отправлять «проверочный запрос» перед каждым запросом. Но здесь есть определенные накладные расходы, и я все же хотел бы знать, почему мое веб-приложение работает успешно, когда оно идет прямо в MySQL, но теряет соединения при прохождении через HAProxy.
Как я могу продолжить отладку и получить больше информации, чем просто "компакт-диск"? Я попытался запустить HAProxy в режиме отладки, но, похоже, больше ничего не обнаружил.
ОБНОВЛЕНИЕ - Пт 4 января, 11:49:28 ICT 2013 (ответ JimB)
Единственный способ получить от haproxy больше информации, чем есть у вас, - это использовать
show sess
илиshow sess <id>
команда периодически наблюдать за состоянием каждого TCP-соединения
Вот некоторая информация о сеансах:
0x31f4310: proto=tcpv4 src=192.168.3.40:60401 fe=FE_mysql be=BE_mysql srv=mysql3 ts=08 age=1m2s calls=2 rq[f=909202h,l=0,an=00h,rx=13s,wx=,ax=] rp[f=109202h,l=0,an=00h,rx=13s,wx=,ax=] s0=[7,18h,fd=0,ex=] s1=[7,18h,fd=1,ex=] exp=13s
0x31fca50: proto=tcpv4 src=192.168.3.40:60423 fe=FE_mysql be=BE_mysql srv=mysql1 ts=08 age=2s calls=2 rq[f=909202h,l=0,an=00h,rx=1m13s,wx=,ax=] rp[f=109202h,l=0,an=00h,rx=1m13s,wx=,ax=] s0=[7,18h,fd=9,ex=] s1=[7,18h,fd=12,ex=] exp=1m13s
Haproxy имеет тайм-аут по умолчанию 10 секунд (а в конфигурациях примера, я думаю, 50 секунд). Я не слишком знаком с JDBC, но, исходя из документации Tomcat, есть настройка
minEvictableIdleTimeMillis
, что приведет к удалению неактивного соединения из пула и по умолчанию составляет 60 секунд, а может быть до 65 секунд, потому чтоtimeBetweenEvictionRunsMillis
по умолчанию составляет 5 секунд. По сути, вам нужно убедиться, что время ожидания вашего haproxy достаточно велико, чтобы учесть эти простаивающие соединения в пуле.
Я увеличил timeout client
до 75 секунд, и теперь указанная выше ошибка возникает меньше, чем раньше:
2013-01-04 11:59:59 Отладка: сбой канала связи
Последний пакет, успешно полученный от сервера, был 145 255 миллисекунд назад. Последний пакет, успешно отправленный серверу, был 10 миллисекунд назад.
Также хочу отметить, что: помимо вышеперечисленного, есть еще такие ошибки:
Сбой канала связи Последний пакет, успешно отправленный на сервер, был 0 миллисекунд назад. Драйвер не получил пакетов от сервера.
На стороне сервера иногда я вижу sD
флаг при отключении:
haproxy[15770]: 192.168.3.40:56944 [04/Jan/2013:11:06:55.895] FE_mysql BE_mysql/mysql1 0/0/77153 1954480 sD 1/1/1/1/0 0/0
В timeout server
также установлено на 75 секунд.
Другой подход - использовать
testWhileIdle
иvalildationQuery
чтобы соединения оставались активными, поскольку несколько пакетов трафика каждые несколько секунд, вероятно, также решат проблему.
Я бы посоветовал разработчику попробовать эти варианты, если другого выхода нет.
Единственный способ получить от haproxy больше информации, чем есть у вас, - это использовать show sess
или show sess <id>
command периодически, чтобы следить за состоянием каждого TCP-соединения, хотя я не уверен, что вы получите больше полезной информации.
В cD
состояние завершения - это самая полезная информация, которая у вас есть. Это означает, что время ожидания установленного соединения с клиентом истекло. Это контролируется в haproxy через timeout client
параметр в конфигурации, установленный глобально, во фронтенте или разделе прослушивания.
Вы сказали, что не видите, что количество одновременных подключений превышает 7, и эта запись в журнале показывает, что сбой произошел, когда было только 3 подключения, поэтому я сомневаюсь, что у вас есть проблема с ограничением количества подключений (даже вне контроля haproxy).
Итак, похоже, что происходит: время от времени пул добавляет новое соединение, которое обрабатывает некоторые запросы, а затем бездействует. Когда это соединение простаивает дольше, чем timeout client
При установке в haproxy, haproxy сам разорвет соединение. В следующий раз, когда это соединение будет использоваться из пула, вы получите указанную выше ошибку.
Haproxy имеет тайм-аут по умолчанию 10 секунд (а в конфигурациях примера, я думаю, 50 секунд). Я не слишком знаком с JDBC, но, исходя из документации Tomcat, есть настройка minEvictableIdleTimeMillis
, что приведет к удалению неактивного соединения из пула и по умолчанию составляет 60 секунд, а может быть до 65 секунд, потому что timeBetweenEvictionRunsMillis
по умолчанию составляет 5 секунд. По сути, вам нужно убедиться, что время ожидания вашего haproxy достаточно велико, чтобы учесть эти простаивающие соединения в пуле.
Другой подход - использовать testWhileIdle
и valildationQuery
чтобы соединения оставались активными, поскольку несколько пакетов трафика каждые несколько секунд, вероятно, также решат проблему.
[править] В ответ на дополнительную информацию от @ Quanta:
Несмотря на то, что таймаут haproxy теперь составляет 75 секунд, вы определенно все еще получаете таймаут сеанса. В общем времени жизни соединения JDBC может быть некоторая дополнительная игра, о которой я не знаю. Поскольку для этого типа услуг требуется очень мало подключений, нет ничего плохого в увеличении таймаутов до чего-то чрезвычайно высокого, порядка часа или более. Если пул JDBC действительно испытывает проблемы с освобождением старых соединений, это только маскирует проблему, но это также может быть легким решением.