вторник, 22 июля 2014 г.

Оптимизация производительности сервиса на уровне Jetty


Перейти к концу метаданных
Переход к началу метаданных
Настройка параметров Jetty для работы в условиях высокой нагрузки описаны в данной статье. Перевод статьи приведён ниже.
Документация проекта Jetty располагается здесь.

Настройка Jetty для работы в условиях высокой нагрузки (при нагрузочном тестировании или в промышленной эксплуатации), требует комплексного подхода, с настройкой операционной системы, сети, JVM, Jetty, генератора нагрузки, а также разрабатываемого приложения.

Генерация нагрузки для нагрузочного тестирования 

  • Машины с генератором нагрузки должны быть оптимизированы также, как и серверные машины: ОС, JVM, и так далее.
  • Нагрузка не должна генерироваться на серверной машине, поскольку у такого варианта характеристики производительности и задержки будут далеки от реальности, равно как будут отличаться размеры сетевых пакетов и другие характеристики транспортного уровня.
  • Генератор нагрузки должен создавать реалистичную нагрузку на приложение:
    • Распространённой ошибкой является то, что генераторы нагрузки часто используют сравнительно мало открытых сетевых соединений. Все эти соединения бывают предельно заняты отправкой сообщений. Это вызывает ограничение измеряемой пропускной способности задержкой запроса. (анализ данного случая можно найти в статье "Lies Damned Lies and Benchmarks")
    • Другой распространённой ошибкой является использование множества TCP/IP соединений с коротким временем жизни. Это часто вызывает заполнение очередей на установление соединений, нехватку файловых дескрипторов, нехватку портов.
  • Генератор нагрузки должен реалистично моделировать трафик типичного клиента сервера. Так для браузеров, это от 2-х до 6-ти соединений, которые в основном в состоянии ожидания, со всплесками запросов на чтение в случайные моменты времени. Соединения обычно долго живущие, типа HTTP/1.1.
  • Генераторы нагрузки должны быть написаны в соответствии с асинхронной моделью работы, таким образом, чтобы ограниченное число потоков генератора не ограничивало максимальное количество имитируемых клиентов сервера. Если генератор не является асинхронным, то например пул из 2000 нитей может имитировать максимум 500 клиентов сервера. Идеальной основой генератора нагрузки является Jetty HttpClient, поскольку он асинхронный и может использоваться для имитации тысяч подключений (см. пример реалистичного генератора нагрузки в статье Cometd Load Tester)

Оптимизация операционной системы

Оптимизация для поддержки множества TCP/IP соединений и высокой пропускной способности должна быть выполнена как на сервере с сервером приложений, так и на сервере с генератором нагрузки.

Linux

ОС Linux достаточно хорошо оптимизирует параметры протокола TCP/IP, однако есть несколько значений по умолчанию и ограничений, которые стоит увеличить. В основном, они могут быть настроены в /etc/security/limits.conf или при помощи вызовов sysctl.

Размеры буферов TCP

Размеры буферов TCP должны быть увеличены минимум до 16Мб для соединений 10Гбит/с, и должна быть оптимизирована автоматическая настройка (однако стоит иметь ввиду разрастание сетевых буферов)
 sysctl -w net.core.rmem_max=16777216
 sysctl -w net.core.wmem_max=16777216
 sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
 sysctl -w net.ipv4.tcp_wmem="4096 16384 16777216"

Размеры очередей

Системный параметр net.core.somaxconn управляет размером очереди входящих соединений. Значением данного параметра по умолчанию является 128. Если сервер подвергается высокой нагрузке и возникают отказы в подключениях на уровне TCP, тогда следует увеличить этот параметр. Слишком большое значение данного параметра приводит к проблемам с ресурсами, поскольку он пытается оповещать сервер о большом числе соединений, большая часть которых останутся в состоянии ожидания необслуженными. Слишком малое значение параметра приведёт к отказам в соединении.
 sysctl -w net.core.somaxconn=4096
Системный параметр net.core.netdev_max_backlog управляет размером очереди входящих пакетов для обработки на верхнем уровне (например, в Java). Значение по умолчанию (2048) может быть увеличено, совместно с другими связанными параметрами:
 sysctl -w net.core.netdev_max_backlog=16384
 sysctl -w net.ipv4.tcp_max_syn_backlog=8192
 sysctl -w net.ipv4.tcp_syncookies=1

Порты

Если создано много исходящих подключений (например, на сервере с генератором нагрузки), то операционная система может столкнуться с нехваткой портов. Следовательно, лучше всего увеличить диапазон используемых системой портов, и включить повторное использование портов в TIME_WAIT:
 sysctl -w net.ipv4.ip_local_port_range="1024 65535"
 sysctl -w net.ipv4.tcp_tw_recycle=1

Файловые дескрипторы

Сильно нагруженные серверы приложений и генераторы нагрузки могут столкнуться с нехваткой файловых дескрипторов, поскольку системные значения по умолчанию для ограничений по файловым дескрипторам сравнительно низки. Они могут быть увеличены для конкретного пользователя в конфигурационном файле /etc/security/limits.conf:
 theusername  hard nofile 40000
 theusername  soft nofile 40000

Управление перегрузкой

Операционная система Linux поддерживает подключаемые алгоритмы управления перегрузкой на уровне сетевого стека TCP. Для получения списка алгоритмов управления перегрузкой, доступных в ядре, выполните:
 sysctl net.ipv4.tcp_available_congestion_control
Если отсутствуют алгоритмы cubic и/или htcp, то необходимо исследовать имеющиеся алгоритмы управления перегрузкой. Алгоритм cubic может быть включен командой:
 sysctl -w net.ipv4.tcp_congestion_control=cubic

Оптимизация сети

  • Сетевые посредники, такие как nginx, могут использовать непостоянные соединения HTTP/1.0. Убедитесь, что используются постоянные соединения HTTP/1.1.

Оптимизация JVM

  • Настройте Сборку Мусора .
  • Выделите достаточное количество памяти.
  • Используйте режим работы "-server".

Оптимизация Jetty

Коннекторы

Акцепторы

Количество акцепторов соединений Jetty должно удовлетворять следующему неравенству:
количество акцепторов >= 1 <= количество ядер ЦП

Ограничения при нехватке ресурсов

Не должны устанавливаться в значение менее чем число ожидаемых одновременных подключений.

Пул потоков

Очень важно ограничивать очередь задач Jetty. По умолчанию, данная очередь не имеет ограничений. В результате при большой нагрузке и превышении скорости обработки запросов приложением над Jetty, множество запросов будут ожидать обработки в очереди. Даже если нагрузка спадёт, Jetty будет вести себя так, будто новые запросы не обрабатываются, поскольку в очереди будет ещё много старых запросов.
Высоконадёжная система должна немедленно отвергать излишние запросы (принцип "fail fast") при помощи очереди с ограниченным размером. Максимальная глубина очереди должны быть рассчитана в соответствии с приемлемым временем "отсутствия ответа". Например, если приложение может обрабатывать 100 запросов в секунду, и ему разрешается восстанавливаться после повышенной нагрузки в течение одной минуты, то может быть установлен размер очереди 60*100=6000. Если размер очереди слишком мал, приложение будет отклонять запросы слишком рано, и не сможет обработать нормальный пик нагрузки.
Ниже приведен пример конфигурации Jetty - jetty.xml. В среде JBoss FUSE / ServiceMix / Karaf по умолчанию, jetty.xml располагается в каталоге etc. Ссылка на jetty.xml указывается в файле конфигурации etc/org.ops4j.pax.web.cfg, в параметре org.ops4j.pax.web.config.file.

<Configure id="Server" class="org.eclipse.jetty.server.Server">
    <Set name="ThreadPool">
      <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
        <!-- specify a bounded queue -->
        <Arg>
           <New class="java.util.concurrent.ArrayBlockingQueue">
              <Arg type="int">6000</Arg>
           </New>
      </Arg>
        <Set name="minThreads">10</Set>
        <Set name="maxThreads">200</Set>
        <Set name="detailedDump">false</Set>
      </New>
    </Set>
</Configure>

Количество потоков следует настраивать в соответствии с приложением. Т.е. исходя из того, сколько потоков требуется приложению для достижения лучшей производительности. Настройка пула соединений должна принимать во внимание максимальное потребление памяти. Типовой размер пула - от 50 до 500 потоков.

Комментариев нет:

Отправить комментарий