Как выполнить оптимизацию производительности для Tomcat?

Для приложений, предоставляющих службы интерфейса, многие из них используют контейнер сервлетов SpringBoot по умолчанию Tomcat.
В начале запуска, поскольку большая часть трафика небольшая, мы не будем делать специальные настройки параметров для Tomcat.
Однако по мере увеличения трафика показатели производительности приложения становятся все хуже и хуже. В настоящее время большинство из нас предпочтут расширить возможности.
В дополнение к расширению емкости мы также можем выполнить настройку производительности на Tomcat, чтобы повысить производительность без увеличения затрат.
Сегодня мы расскажем, как выполнить простую настройку производительности на Tomcat для повышения производительности приложений.
Архитектура Tomcat.

Как видно из приведенного выше рисунка, Tomcat абстрагирует свой бизнес на такие компоненты, как Server, Service, Connector, Container и т. д., и у каждого компонента своя роль.
Компонент сервера является самым внешним компонентом Tomcat, который является абстракцией самого экземпляра Tomcat и представляет сам Tomcat. Компонент сервера может иметь один или несколько компонентов службы.
Компонент службы — это набор компонентов в Tomcat, которые предоставляют службы и обрабатывают запросы. Компонент службы может иметь несколько соединителей и контейнер. Несколько коннекторов указывают, что он может использовать несколько протоколов для одновременного получения пользовательских запросов.
Коннектор отвечает за обработку клиентских подключений и обеспечивает поддержку различных служебных протоколов, включая BIO, NIO, AIO и т. д. Ценность его существования заключается в защите сложности многопротокольных контейнеров и унификации стандартов обработки контейнеров.
Компонент Container — это контейнер, отвечающий за обработку конкретной бизнес-логики. Когда компонент Connector устанавливает соединение с клиентом, он перенаправляет запрос компоненту Engine компонента Container для обработки.
На данный момент основные компоненты Tomcat в основном готовы. На самом деле в компоненте «Контейнер» есть много подразделенных компонентов. На самом деле, если вам интересна абстракция бизнеса, вы можете продолжать смотреть на нее.
- Компонент Engine представляет собой исполняемый экземпляр Servlet, включая основные функции контейнера Servlet, который может иметь один или несколько виртуальных хостов (Host). Его основная функция — делегировать запрос соответствующему виртуальному хосту для обработки, то есть сопоставить соответствующий виртуальный хост для обработки в соответствии с конфигурацией пути URL.
- Компонент Host отвечает за запуск нескольких приложений и за установку этих приложений. Его основная функция — проанализировать файл web.xml и сопоставить его с соответствующим компонентом контекста.
- Компонент Context представляет само конкретное веб-приложение, и его наиболее важной функцией является управление экземплярами сервлетов внутри. Контекст может иметь один или несколько экземпляров сервлета.
- Компонент Wrapper представляет собой сервлет, который отвечает за управление сервлетом, включая загрузку сервлета, инициализацию, выполнение и восстановление ресурсов. Обертка является контейнером самого низкого уровня.
Видно, что Host — это абстракция виртуального хоста, Context — это абстракция приложения, Wrapper — это абстракция сервлета, а Engine — это абстракция уровня обработки.
Основные параметры.
Прежде чем разобраться с основными параметрами, нам необходимо получить общее представление о потоке обработки запросов Tomcat. Процесс обработки запросов Tomcat выглядит следующим образом.

На приведенной выше схематической диаграмме есть три очень важных основных параметра, которые также являются ключом к настройке производительности.
acceptCount: когда пул потоков контейнера достигает максимального числа и нет бездействующих потоков, а очередь соединителя достигает максимального числа, максимальное количество подключений, которое может принять операционная система.maxConnections: когда пул потоков контейнера достигает максимального числа и нет незанятых потоков, максимальное количество потоков, которое может получить очередь соединителя.maxThreads: максимальное количество потоков обработки в пуле потоков контейнера.
Из значения трех вышеуказанных параметров можно сделать следующие выводы.
Клиент не устанавливает прямой контакт с компонентом соединителя Tomcat, а сначала устанавливает связь с операционной системой, а затем передает ее соединителю.
Это очень важно, иначе вы не поймете параметр acceptCount.
Мало того, что в компоненте Connector есть очередь, так еще и в операционной системе есть очередь для временного хранения соединения с клиентом, что тоже является ключевым моментом.
Пул потоков, на который мы ссылаемся, относится к пулу потоков в контейнере Container.
Очень важно понимать значение этих трех основных параметров, иначе нет возможности провести последующую работу по настройке производительности.
макс. число потоков.
Мы знаем, что maxThreads относится к максимальному количеству потоков обработки запросов, которые по умолчанию равны 200 как в Tomcat7, так и в Tomcat8.
Настройка этого параметра должна быть скорректирована в соответствии с содержанием выполнения задачи. Вообще говоря, формула расчета такова: maximum number of threads = ((IO time + CPU time)/CPU time) * number of CPU cores.
Идея этой формулы на самом деле очень проста: максимально использовать ресурсы ЦП.
Потребление времени задачи делится на потребление времени ввода-вывода и потребление времени ЦП. По сути, наибольшее потребление времени ввода-вывода, и процессору в это время нечего делать.
Таким образом, если ЦП можно разрешить обрабатывать другие задачи, пока задача ожидает ввода-вывода, то загрузка ЦП увеличится.
Вообще говоря, поскольку потребление времени ввода-вывода намного больше, чем потребление времени ЦП, количество maxThreads, рассчитанное по формуле, будет намного больше, чем количество ядер ЦП, что является нормальным явлением.
Следует отметить, что это значение не чем выше, тем лучше. Потому что, когда потоков слишком много, ЦП необходимо выполнять переключение контекста, что потребляет часть ресурсов ЦП.
Поэтому лучше всего использовать приведенную выше формулу для расчета эталонного значения, а затем выполнить стресс-тест, чтобы настроить его на разумное значение.
Вообще говоря, если значение maxThreads увеличивается, но пропускная способность не увеличивается и не уменьшается, это может указывать на то, что достигнуто узкое место.
макс. количество подключений.
maxConnections относится к максимальному количеству подключений, которые может удерживать очередь в соединителе, когда потоки в пуле потоков достигают максимального значения и все заняты.
Вообще говоря, мы должны установить разумное значение, и мы не можем позволить ему накапливаться без ограничений.
Потому что вычислительная мощность Tomcat определенно ограничена, и он точно не сможет с ней справиться, когда достигнет определенного уровня. Поэтому бесполезно, если вы накапливаете слишком много. Вместо этого это вызовет накопление памяти и в конечном итоге приведет к переполнению памяти OOM.
Как правило, эмпирическое значение может иметь тот же размер, что и maxThreads.
Я думаю, что это более разумно, потому что соединению в очереди нужно только дождаться, пока поток максимально обработает задачу, он не будет ждать слишком долго, и время отклика не будет слишком большим.
Если вы хотите сократить время отклика, вы можете настроить maxConnections ниже, чем maxThreads, что может немного сократить время отклика.
Однако следует отметить, что если его опустить слишком низко, производительность может серьезно снизиться, а пропускная способность будет снижена.
acceptCount.
acceptCount относится к максимальному количеству подключений, которое может принять операционная система, когда пул потоков контейнера достигает максимального числа и нет незанятых потоков, а очередь соединителя достигает максимального числа.
Когда число в очереди достигнет максимального значения, все входящие запросы будут отклонены. Значение по умолчанию — 100. Это можно понимать как механизм самозащиты операционной системы. Если накоплений слишком много, с которыми невозможно справиться, то просто откажитесь от них, чтобы защитить собственные ресурсы.
Данных по настройке этого параметра сравнительно мало, но по его смыслу не рекомендуется, чтобы это значение было больше maxConnections.
Потому что соединение в этой очереди должно ждать. Если значение слишком велико, это означает, что будет много необработанных соединений.
Чем больше подключений, тем дольше он ждет и тем медленнее время отклика. Если вы хотите уменьшить время отклика, вам, вероятно, следует уменьшить это значение.
Некоторые студенты задаются вопросом, зачем нам acceptCount, когда у нас есть maxConnections? Это не повтор? По сути, в эпоху БИО эти два значения в основном совпадают.
Я предполагаю, что из-за появления NIO, AIO и других технологий позже операционная система может принимать больше клиентских подключений.
Таким образом, операционная система может сначала установить кэш соединения, а затем коннектор может напрямую получить соединение от операционной системы, так что нет необходимости ждать, пока операционная система установит трудоемкое TCP-соединение, тем самым повышая эффективность. .
Помимо вышеперечисленных трех параметров, есть еще несколько непрофильных параметров, но я думаю, что они все же имеют какое-то влияние.
connectionTimeout: указывает время ожидания после установления соединения. Если это время превышено, тайм-аут будет возвращен напрямую.minSpareThreads: Указывает минимальное количество выживших потоков, то есть, если нет запроса, то минимальное количество потоков должно оставаться в живых. Этот параметр связан с наличием пакетного потока. В случае импульсного потока, если значение слишком низкое, мгновенное время отклика будет относительно большим.
Обобщить.
Сегодня мы поделились основными компонентами Tomcat, а затем объяснили три основных параметра и возможности настройки Tomcat при обработке запросов.
Для параметра maxThreads, если его рассчитать по формуле, нам нужно получить время ввода-вывода и время процессора, но на самом деле эти два значения получить непросто.
Таким образом, в целом, мы можем получить более подходящее значение maxThreads с помощью опрессовки.
Для параметра maxConnections можно установить такое же значение, как и для maxThreads, а затем настроить его в соответствии с конкретной ситуацией.
Если вы хотите уменьшить время отклика, вы можете немного уменьшить его, в противном случае вы можете увеличить его.
Для параметра acceptCount его логика настройки аналогична maxConnections, которую можно установить аналогично maxConnections, а затем точно настроить в соответствии с соответствующими временными требованиями.
Если вам нравятся такие истории и вы хотите поддержать меня, пожалуйста, хлопните мне в ладоши.
Ваша поддержка очень важна для меня, спасибо.