
У меня есть веб-приложение (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
поддерживать активные соединения, поскольку несколько пакетов трафика каждые несколько секунд, вероятно, также решат проблему.
[править] В ответ на дополнительную информацию @quanta:
Несмотря на то, что время ожидания haproxy теперь составляет 75 секунд, вы определенно все еще получаете тайм-ауты сеанса. Возможно, есть некоторая дополнительная игра в общем времени жизни соединения JDBC, о которой я не знаю. Поскольку для этого типа сервиса требуется очень мало соединений, нет ничего плохого в увеличении времени ожидания до чего-то чрезвычайно большого, порядка часа или больше. Если пул JDBC действительно испытывает проблемы с освобождением старых соединений, это будет только маскировкой проблемы, но это также может быть простым решением.