HAProxy, время ожидания клиента при подключении из пула JDBC
У меня есть веб-приложение (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.
Как я могу отлаживать дальше и получать больше информации, чем просто "cD"? Я попытался запустить 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
чтобы поддерживать соединения активными, поскольку несколько пакетов трафика каждые несколько секунд, вероятно, также облегчили бы проблему.
Я бы предложил разработчику попробовать эти варианты, если нет другого пути.
1 ответ
Единственный способ получить больше информации от haproxy, чем у вас, это использовать show sess
или же show sess <id>
периодически проверяйте состояние каждого TCP-соединения, хотя я не уверен, что вы могли бы получить больше полезной информации.
cD
состояние завершения - самая полезная часть информации, которую вы имеете. Это означает, что установленное соединение с клиентом истекло. Это контролируется в haproxy через timeout client
параметр в конфигурации, установить глобально, или в разделе frontent или listen.
Вы сказали, что не видите одновременных подключений, превышающих 7, и эта запись журнала показывает, что сбой произошел, когда было только 3 подключения, поэтому я сомневаюсь, что у вас есть проблема с лимитом подключения (даже вне контроля haproxy).
Таким образом, похоже, что происходит, что иногда пул добавляет новое соединение, которое обрабатывает некоторые запросы, а затем бездействует. Когда это соединение простаивает дольше, чем timeout client
установив в haproxy, haproxy собирается разорвать само соединение. При следующем использовании этого соединения из пула вы получите сообщение об ошибке выше.
Haproxy имеет тайм-аут по умолчанию 10 секунд (и я думаю, что у примеров конфигурации 50 секунд). Я не слишком знаком с JDBC, но, исходя из документации Tomcat, есть настройка minEvictableIdleTimeMillis
, который будет освобождать простаивающее соединение из пула, по умолчанию 60 секунд, и может быть до 65 секунд, потому что timeBetweenEvictionRunsMillis
по умолчанию 5 секунд. По сути, вам нужно убедиться, что ваш тайм-аут haproxy достаточно высок, чтобы учесть эти незанятые соединения в пуле.
Другой подход будет использовать testWhileIdle
а также valildationQuery
чтобы поддерживать соединения активными, поскольку несколько пакетов трафика каждые несколько секунд, вероятно, также облегчили бы проблему.
[edit] В ответ на дополнительную информацию @quanta:
Несмотря на то, что тайм-аут haproxy теперь составляет 75 секунд, вы определенно все еще получаете тайм-ауты сеанса. В общем времени жизни соединения JDBC может быть некоторое дополнительное воспроизведение, о котором я не знаю. Поскольку для такого типа обслуживания требуется очень мало соединений, нет ничего плохого в увеличении времени ожидания до чего-то очень высокого, порядка часа или более. Если в пуле JDBC действительно возникают проблемы с освобождением старых подключений, это только маскирует проблему, но это также может быть легко исправлено.