Потоки блокируются в состоянии TIMED_WAITING в Tomcat

Вопрос или проблема

Изучение блокирующего и неблокирующего ввода-вывода вызвало у меня некоторые сомнения.

Я пришел к тому, чтобы прочитать заявление, написанное здесь (https://mail-archives.apache.org/mod_mbox/tomcat-users/201402.mbox/%[email protected]%3E), где говорится, что “даже HTTP-коннектор NIO должен использовать блокирующий ввод-вывод для чтения тела запроса и записи ответа”.

Может кто-то объяснить это?

Во-вторых, я проводил нагрузочное тестирование jmeter на сервере tomcat для одного из API, используемого для получения некоторых данных о пользователе в нашем проекте. Изначально я делал 2-3 запроса в секунду, и приложение могло поддерживать эту пропускную способность, но как только я попытался сделать 15-20 запросов в секунду, время работы сервера tomcat увеличилось экспоненциально. Дамп потоков показывает, что потоки соединителя Nio ожидают записи данных, пожалуйста, предоставьте решение, чтобы потоки не блокировались.

"http-nio-8080-exec-1502" - Поток t@103682
   java.lang.Thread.State: TIMED_WAITING
        at sun.misc.Unsafe.park(Нативный метод)
        - парковка для ожидания <400f1e16> (java.util.concurrent.CountDownLatch$Sync)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos(AbstractQueuedSynchronizer.java:1037)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(AbstractQueuedSynchronizer.java:1328)
        at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:277)
        at org.apache.tomcat.util.net.NioEndpoint$KeyAttachment.awaitLatch(NioEndpoint.java:1381)
        at org.apache.tomcat.util.net.NioEndpoint$KeyAttachment.awaitWriteLatch(NioEndpoint.java:1384)
        at org.apache.tomcat.util.net.NioBlockingSelector.write(NioBlockingSelector.java:114)
        at org.apache.tomcat.util.net.NioSelectorPool.write(NioSelectorPool.java:172)
        at org.apache.coyote.http11.InternalNioOutputBuffer.writeToSocket(InternalNioOutputBuffer.java:139)
        at org.apache.coyote.http11.InternalNioOutputBuffer.addToBB(InternalNioOutputBuffer.java:197)
        - заблокировано <24b951cc> (org.apache.coyote.http11.InternalNioOutputBuffer)
        at org.apache.coyote.http11.InternalNioOutputBuffer.access$000(InternalNioOutputBuffer.java:41)
        at org.apache.coyote.http11.InternalNioOutputBuffer$SocketOutputBuffer.doWrite(InternalNioOutputBuffer.java:320)
        at org.apache.coyote.http11.filters.ChunkedOutputFilter.doWrite(ChunkedOutputFilter.java:118)
        at org.apache.coyote.http11.AbstractOutputBuffer.doWrite(AbstractOutputBuffer.java:256)
        at org.apache.coyote.Response.doWrite(Response.java:491)
        at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:391)
        at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:426)
        at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:339)
        at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:421)
        at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:409)
        at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:97)
        at com.fasterxml.jackson.core.json.UTF8JsonGenerator._flushBuffer(UTF8JsonGenerator.java:2039)
        at com.fasterxml.jackson.core.json.UTF8JsonGenerator.writeString(UTF8JsonGenerator.java:454)
        at com.fasterxml.jackson.databind.ser.std.StringSerializer.serialize(StringSerializer.java:49)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
        at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
        at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
        at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
        at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
        at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
        at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18)
        at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704)
        at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)
        at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
        at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:292)
        at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1429)
        at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:951)
        at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:264)
        at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:100)
        at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:167)
        at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:100)
        at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:166)
        at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:80)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:127)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:806)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:729)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476)
        - заблокировано <368bdc01> (org.apache.tomcat.util.net.NioChannel)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)

Я не могу вам сказать. В трассировке стека я вижу некоторые библиотеки http и json. Что я бы сделал, так это использовать профайлер Java, который покажет, где приложение тратит свое время. Таким образом, вы сможете узнать, какая строка в вашем коде заблокирована. В качестве альтернативы вы можете вставить логирующие операторы в ваш код, которые записывают время, когда они вызываются. И так вы увидите, где приложение ждет. Но я полагаю, что tomcat вызывает ваше приложение (а не наоборот), и поэтому может быть так, что ожидание происходит вне вашего приложения.

Ответ или решение

Блокировка потоков в состоянии TIMED_WAITING на сервере Tomcat

При работе с сервером приложений Apache Tomcat, вы можете столкнуться с проблемами блокировки потоков в состоянии TIMED_WAITING. Это состояние может свидетельствовать о том, что ваш сервер испытывает затруднения при обработке множественных запросов, особенно когда под нагрузкой увеличивается количество параллельных соединений и операций ввода-вывода. В данной статье предложим глубокий анализ данной ситуации и способы её решения.

Основы блокировки и неблокирующего ввода-вывода

Для начала важно осознать разницу между блокирующим и неблокирующим ввода-выводом. При блокирующем вводе-выводе поток ожидания фиксируется, пока операция не будет завершена, что может привести к снижению производительности при увеличении числа запросов, как в вашем случае с нагрузочным тестированием с помощью JMeter. Неблокирующий ввод-вывод используется для повышения производительности, позволяя потокам продолжать работу, не ожидая завершения I/O операций.

Тем не менее, даже при использовании NIO (Non-blocking I/O), Tomcat всё равно использует блокирующий ввод-вывод при чтении тела запроса и записи результата. Это объясняется тем, что для применения новых методов требуется много изменений в архитектуре и логике обработки данных.

Анализ состояния TIMED_WAITING

Согласно приведённому вами стеку вызовов, поток "http-nio-8080-exec-1502" находится в состоянии TIMED_WAITING, что указывает на ожидание Reacting на определенную синхронизацию через CountDownLatch. Это может произойти, когда потоки ожидают возможность записи данных в соединение. Ниже приведены основные причины, по которым это состояние может возникать:

  1. Проблемы с производительностью приложения: Если ваши API выполняют долгие операции или задерживаются на уровне базы данных, это может привести к тому, что потоки будут ожидать завершения обработки запросов.

  2. Заполнение соединений: При интенсивных нагрузках, если количество создаваемых соединений превышает лимит, это может привести к нехватке доступных потоков для обработки запросов.

  3. Индивидуальные конфигурации Tomcat: Настройки, такие как размер пула потоков и его таймауты, также могут значительно повлиять на производительность сервера.

Рекомендации по решению проблемы

Для устранения проблем с блокировкой потоков в случае накопления запросов и увеличения времени отклика можно воспользоваться следующими рекомендациями:

  1. Профилирование производительности: Используйте инструменты профилирования, такие как VisualVM или YourKit, для мониторинга того, как ваше приложение использует ресурсы и где оно может блокироваться. Это поможет выявить узкие места.

  2. Увеличение пула потоков Tomcat: Проверьте текущие настройки пула потоков и рассмотрите возможность его увеличения, если это допустимо. Это можно настроить в конфигурационном файле server.xml:

    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="200" minSpareThreads="10"/>
  3. Оптимизация операций ввода-вывода: Убедитесь, что обработка запросов и операций записи выполнена оптимально. Возможно, стоит рассмотреть дебазирование и профилирование ваших методов работы с базой данных.

  4. Использование асинхронных методик: Применение асинхронных обработчиков запросов, таких как Servlet 3.0 async, может помочь уменьшить блокировку потоков, позволяя им продолжать выполнение других операций, пока идет ожидание I/O.

  5. Мониторинг и логирование: Постоянно ведите журнал выполнения ваших методов, чтобы быстро реагировать на возможные проблемы с производительностью, и анализируйте, на каких шагах ваши запросы испытывают задержки.

Заключение

Проблема блокировки потоков в состоянии TIMED_WAITING на сервере Tomcat может быть вызвана многими факторами, включая конфигурационные ошибки и особенности вашего приложения. Понимание этих аспектов и выражение их в реальных действиях, таких как оптимизация мониторинга и управление потоками, позволит вам значительно улучшить производительность вашего сервера. Регулярная практика анализа и исправления узких мест — ключ к успешной работе вашего приложения под нагрузкой.

Оцените материал
Добавить комментарий

Капча загружается...