Apache + Tomcat мають проблеми з спілкуванням. Неясні повідомлення про помилки. Збірка веб-сайтів, розміщених під Tomcat


22

Налаштування:
Fedora 8
Apache 2.2.8
Tomcat 5.5.8
Apache пересилає запити за допомогою AJP.

Проблема:
Через певний проміжок часу (зовсім не постійний, може бути від години до двох, або один або більше днів) Tomcat знизиться. Або він припиняє відповідати, або він ставить загальний "Сервіс тимчасово недоступний".

Діагностика:
Є два сервери з однаковим налаштуванням. Один розміщує веб-сайт із більшим трафіком (кілька запитів в секунду), інший - низький трафік (кілька запитів кожні кілька хвилин). Обидва веб-сайти є абсолютно різними кодовими базами, але вони мають подібні проблеми.

На першому сервері, коли виникає проблема, усі потоки повільно починають братися до досягнення межі (MaxThreads 200). У цей момент сервер більше не реагує (і з’являється сторінка з недоступною службою через тривалий проміжок часу).

На другому сервері, коли виникає проблема, запити займають багато часу, і коли вони виконуються, ви бачите лише сторінку недоступних послуг.

За винятком згадки про проблему MaxThreads, журнали Tomcat не вказують на конкретні проблеми, які могли б викликати це.

Однак у журналах Apache ми бачимо випадкові повідомлення, що посилаються на AJP. Ось зразок випадкового повідомлення, яке ми бачимо (не в конкретному порядку):

[error] (70007)The timeout specified has expired: ajp_ilink_receive() can't receive header
[error] (104)Connection reset by peer: ajp_ilink_receive() can't receive header
[error] proxy: AJP: disabled connection for (localhost)
[error] ajp_read_header: ajp_ilink_receive failed
[error] (120006)APR does not understand this error code: proxy: read response failed from 127.0.0.1:8009 (localhost)
[error] ap_proxy_connect_backend disabling worker for (localhost)

Інша дивна річ, яку ми помітили на сервері більш високого трафіку, - це те, що перед початком проблеми запити до бази даних займають набагато довше, ніж раніше (2000-5000 мс проти звичайно 5-50 мс). Це триває лише 2-4 секунди до появи повідомлення MaxThreads. Я припускаю, що це результат того, що сервер раптом має справу з занадто великою кількістю даних / трафіку / потоків.

Довідкова інформація:
Ці два сервери працювали без проблем досить довгий час. Системи насправді налаштовувались за допомогою двох NIC протягом цього часу. Вони розділили внутрішній та зовнішній трафік. Після оновлення мережі ми перемістили ці сервери на єдині NIC (це було рекомендовано нам з міркувань безпеки / простоти). Після цієї зміни у серверів почалися проблеми.

Розв’язання
. Очевидним рішенням було б повернутися до налаштування двох NIC. Проблеми з цим полягають у тому, що це може спричинити деякі ускладнення при налаштуванні мережі, і здається, ігнорування проблеми. Ми вважаємо за краще спробувати запустити його на одній установці NIC.

Гуглінг різних повідомлень про помилки не дав нічого корисного (як старі рішення, так і не пов'язані з нашою проблемою).

Ми спробували коригувати різні тайм-аути, але це просто змусило сервер працювати трохи довше, перш ніж померти.

Ми не впевнені, де шукати, щоб далі діагностувати проблему. Ми досі розуміємо соломинку, яка проблема може бути:

1) Налаштування з AJP та Tomcat є неправильним або застарілим (тобто відомі помилки?)
2) Налаштування мережі (два NIC проти одного NIC) викликає плутанину або пропускну здатність.
3) Самі веб-сайти (немає загального коду, не використовуються платформи, просто основний код Java з сервлетами та JSP)

Оновлення 1:
Користуючись корисною порадою Девіда Пашлі, я зробив скидання стека / потоку під час випуску. Я виявив, що всі 200 ниток знаходилися в одному з наступних станів:

"TP-Processor200" daemon prio=1 tid=0x73a4dbf0 nid=0x70dd waiting for monitor entry [0x6d3ef000..0x6d3efeb0]
at  oracle.jdbc.pool.OracleConnectionCacheImpl.getActiveSize(OracleConnectionCacheImpl.java:988)
- waiting to lock <0x7e3455a0> (a oracle.jdbc.pool.OracleConnectionCacheImpl)
[further stack trace removed for brevity]

"TP-Processor3" daemon prio=1 tid=0x08f142a8 nid=0x652a waiting for monitor entry [0x75c7d000..0x75c7ddb0]
at oracle.jdbc.pool.OracleConnectionCacheImpl.getConnection(OracleConnectionCacheImpl.java:268)
- waiting to lock <0x7e3455a0> (a oracle.jdbc.pool.OracleConnectionCacheImpl)
[further stack trace removed for brevity]

Цікаво, що лише одна нитка з усіх 200 ниток була в цьому стані:

"TP-Processor2" daemon prio=1 tid=0x08f135a8 nid=0x6529 runnable [0x75cfe000..0x75cfef30]
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at oracle.net.ns.Packet.receive(Unknown Source)
at oracle.net.ns.DataPacket.receive(Unknown Source)
at oracle.net.ns.NetInputStream.getNextPacket(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
[further stack trace removed for brevity]

Можливо, драйвер Oracle у цій нитці змушує всі інші потоки чекати його завершення. Чомусь він повинен застрягнути в такому стані читання (сервер ніколи не відновлюється самостійно, він вимагає перезавантаження).

Це говорить про те, що вона повинна бути пов'язана або з мережею між сервером і базою даних, або з самою базою даних. Ми продовжуємо зусилля з діагностики, але будь-які поради будуть корисні.


По-перше, це дивовижно написане питання. Фантастична робота на деталях! По-друге, ви використовуєте proxy_ajp чи mod_jk для підключення серверів Apache та Tomcat?
Ophidian

Я використовую proxy_ajp для з'єднання двох.
Джорді Бум

Зробіть стрес-тести, використовуючи облогу, joedog.org/siege-home .
paalfe

Відповіді:


9

Виявляється, ця версія (класи12 - досить стара) драйвера Oracle мала в ньому різні помилки, які спричинили тупик (як це видно в цитованому вище стані TP-Processor2). Він не став активним, поки ми не перейшли до нового середовища. Оновлення до останньої версії (ojdbc14) вирішило проблему на первинному сервері.


Це привело мене до мого правильного рішення: я заблокував рядок DB ... і ніколи не отримав жодного
винятку

6

З опису я б припустив, що проблема може бути пов’язана із занадто довгими запитами до бази даних. Якщо запити тривають довше, запит займе більше часу, і тому у вас буде більше запущених запитів. Як бачите, у вас закінчуються темчасті нитки. Коли ви вирішите проблему з базою даних, ви повинні мати все в порядку.

  • Отримайте слід стека, використовуючи jstack або використовуючи kill -3 $ process_id. Подивіться, що роблять ваші нитки, коли вони вмирають. Якщо вони всі чекають на базі даних, це хороший вказівник на мою теорію. Вони, можливо, всі чекають на якомусь замці.
  • Встановіть LambdaProbe. Це безцінне для того, щоб дізнатися, чим займається твій томат.
  • Оновіть ваш tomcat. 5.5.8 неймовірно старий. Я думаю, вони зараз на 5.5.27.

Девіде, я оновив запитання (див. Оновлення 1) з новими висновками на основі пропозиції вашого відстеження потоку дамп / стек.
Джорді Бум

Я б припустив, що пул підключення до вашої бази даних є занадто малим у порівнянні з вашим максимальним значенням з'єднання tomcat. Здається, що більшість потоків чекають на підключення до бази даних.
Девід Пашлі

Єдина причина, що багато потоків - це те, що потоки, які зазвичай використовуються, залишаються в очікуванні того, що один потік намагається прочитати з сокета. Кількість з'єднань БД, які використовуються в будь-який час, становить від 1 до 3. Ніколи не потрібно більше, ніж стільки.
Джорді Бум

5

Додайте connectionTimeout та KeepAliveTimeout у свій роз'єм AJP, знайдений у /etc/tomcat7/server.xml.

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" 
           connectionTimeout="10000" keepAliveTimeout="10000" />

Інформація про роз'єм AJP на https://tomcat.apache.org/tomcat-7.0-doc/config/ajp.html

  • connectionTimeout = Кількість мілісекунд цей Роз'єм зачекає, після прийняття з'єднання, щоб представити рядок URI запиту. Значення за замовчуванням для роз'ємів протоколу AJP - -1 (тобто нескінченне).

  • holdAliveTimeout = Кількість мілісекунд цей Роз'єм чекатиме іншого запиту AJP перед тим, як закрити з'єднання. Значенням за замовчуванням є використання значення, встановленого для атрибуту connectionTimeout.

Якщо значення connectionTimeout і KeepAliveTimeout не визначені, то з'єднання AJP залишатимуться живими нескінченно. Завдяки багатьом потокам, максимум потоків за замовчуванням - 200.

Я рекомендую встановити psi-зонд - розширений менеджер і монітор для Apache Tomcat, роздвоєний від Lambda Probe. https://code.google.com/p/psi-probe/


4

Через те, як працює AJP, стійкі зв’язки між apache (використовуючи mod_proxy_ajp або mod_jk) можуть бути безпечно закриті лише клієнтом . У цьому випадку клієнт - це працівник apache, який відкривається, а потім тримає з'єднання з tomcat для життя для працівника .

Через таку поведінку у вас не може бути більше апаш-робітників, ніж у робочих ниток tomcat. Це призведе до того, що додаткові працівники http не зможуть підключитися до tomcat (оскільки черга на прийняття заповнена) і позначить ваш бекенд як ДОЛУ!


1
Вибачте за коментар після всіх цих років, але хіба цього не можна було гарантувати, встановивши максимальний прапор у конфігурації ProxyPass на кількість MaxThreads контейнера сервлетів?
Хорст Гутманн

2

Я мав кращі результати з mod_proxy замість mod_ajp з точки зору стабільності, тому спробуйте це рішення. Це неінвазивно - в кращому випадку це вирішить проблему, а в гіршому випадку виключить mod_ajp.

Крім того, це звучить так, що ваші Tomcats перестають відповідати, і всі потоки запитів пов'язані. Попросіть вашу команду розробників розібратися в тому, що відбувається - корисно взяти дамп із нитками та надіслати їх.


У мене було враження, що mod_proxy має деякі проблеми зі масштабуванням, незважаючи на те, що його простіше підключити. Здається, що фонд Apache рекомендує mod_jk ( wiki.apache.org/tomcat/FAQ/Connectors#Q2 )
Ophidian

Це не дає клейкої сесії, правда. Але крім цього у мене ніколи не було проблем з цим.
Роберт Мунтяну

1

Перше, що мені здається, коли я чую, що сервер працює деякий час, раптом сповільнюється, а потім починає виникати збої в сервісі, - це те, що у нього закінчується оперативна пам’ять і обмінюється своп. Мені не ясно, чи невдачі AJP, які ви бачите, можуть бути наслідком таймаутів, але це не здається абсолютно необгрунтованим; Однак я не бачу очевидного способу підключення до NIC. У будь-якому випадку я рекомендую вам сфотографувати, що відбувається з використанням вашої пам'яті, коли ці події відбуваються.

Якщо у вас не вистачає оперативної пам’яті, можливо, вам доведеться відключити Apache MaxClientsі збільшити свійListenBacklog .

До речі, дякую за те, що ви зробили ваше запитання настільки впорядкованим та завершеним.


Коли я спостерігаю "верх", поки це відбувається, використання пам'яті залишається досить послідовним. Принаймні немає шипів. Існує лише короткий момент високого використання процесора.
Джорді Бум

1

У мене були подібні помилки журналу в середовищі Redhat із proxy_ajp та Tomcat. Вирішено шляхом оновлення пакета httpd:

yum update httpd

від:

  • httpd-devel-2.2.3-43.el5_5.3.x86_64
  • httpd-2.2.3-43.el5_5.3.x86_64

до:

  • httpd-2.2.3-45.el5_6.3.x86_64
  • httpd-devel-2.2.3-45.el5_6.3.x86_64

Потім перезапустили апаш, після чого перезапустили Tomcat.

Це зафіксувало це для мене!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.