Статьи

Анализ производительности сервисов REST / HTTP с помощью JMeter и Yourkit

В моем последнем посте описывалось, как выполнять стресс- или нагрузочное тестирование асинхронных служб REST / HTTP с помощью JMeter . Однако выполнение таких тестов часто показывает, что тестируемая система плохо справляется с увеличением нагрузки. Вопрос сейчас в том, как найти узкое место?

Углубленный взгляд на код для обнаружения подозрительных частей может быть одной из альтернатив. Но, учитывая потенциально огромную кодовую базу и, следовательно, множество возможностей, которые узкое место может скрыть 1, это может показаться не слишком многообещающим. К счастью, существуют инструменты, которые обеспечивают эффективные возможности анализа на основе телеметрии 2 . Запись и проверка таких измерений обычно называется профилированием, и этот пост дает небольшое представление о том, как сделать это с помощью Yourkit 3 .

Прежде всего, мы запускаем нашу SUT (системный тест) и используем JMeter для увеличения нагрузки на систему. Для этого JMeter может выполнить тестовый сценарий, который имитирует нескольких пользователей, отправляющих большое количество запросов в SUT. Тестовый сценарий определен в тестовом плане . Последний может содержать прослушиватели, которые позволяют фиксировать время выполнения запросов и предоставлять статистику, такую ​​как максимальная / минимальная / средняя продолжительность запроса, отклонение, пропускная способность и так далее. Вот как мы обнаруживаем, что наша система плохо масштабируется …

После этих выводов мы даем возможность Yourkit получить телеметрию. Поэтому виртуальная машина SUT запускается с помощью специального агента профилирования. Инструмент профилирования предоставляет несколько представлений, которые позволяют в реальном времени проверять загрузку процессора, потребление памяти и т. Д. Но для тщательного анализа, например, производительности SUT под нагрузкой, Yourkit необходимо получить информацию о процессоре, предоставленную агентом, с помощью так называемых снимков .

Рекомендуется запускать SUT, JMeter и Yourkit на отдельных машинах, чтобы избежать фальсификации результатов теста. Например, выполнение SUT и JMeter на одном компьютере может снизить пропускную способность, поскольку потоки JMeter могут потреблять много доступного времени вычислений.

Учитывая эту настройку, мы рассмотрим небольшой пример сеанса профилирования. Следующий фрагмент кода является выдержкой из службы 4 на основе JAX-RS, которую мы используем в качестве SUT.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
@Path( '/resources/{id}' )
public class ExampleResourceProvider {
 
  private List<ExampleResource> resources;
 
  [...]
 
  @Override
  @GET
  @Produces( MediaType.TEXT_PLAIN )
  public String getContent( @PathParam( 'id' ) String id ) {
    ExampleResource found = NOT_FOUND;
    for( ExampleResource resource : resources ) {
      if( resource.getId().equals( id ) ) {
        found = resource;
      }
    }
    return found.getMessage();
  }

Служба выполняет поиск в списке экземпляров ExampleResource . Объект ExampleResource просто отображает идентификатор на сообщение, представленное как String . Сообщение, найденное для данного идентификатора, возвращается. Поскольку сервис вызывается с GET запросами, вы можете проверить результат с помощью браузера:

Для наглядности клеевой код службы инициализирует список с 500000 элементов неупорядоченным образом.

Как только мы запустим SUT, мы можем установить его под нагрузкой, используя JMeter. Тест-план выполняет около 100 одновременных запросов одновременно. Как показано на рисунке ниже, среднее выполнение запроса занимает около 1 секунды.

Телеметрия ЦП, записанная Yourkit во время выполнения плана тестирования JMeter, выявляет причину длительного времени выполнения запроса. Выбор вкладки « Hot spots » профилированного снимка показывает, что около 72% загрузки ЦП было использовано при итерации списка. Глядя на представление Back Traces котором перечисляется дерево вызывающих абонентов выбранного метода горячей точки, мы обнаруживаем, что наш примерный метод обслуживания вызывает итерацию списка.

Из-за этого мы изменим реализацию службы на следующем шаге, чтобы использовать бинарный поиск в отсортированном списке для поиска в ExampleResource .

1
2
3
4
5
6
7
8
@Override
@GET
@Produces( MediaType.TEXT_PLAIN )
public String getContent( @PathParam( 'id' ) String id ) {
  ExampleResource key = new ExampleResource( id, null );
  int position = Collections.binarySearch( resources, key );
  return resources.get( position ).getMessage();
}

После этого мы повторно запускаем план тестирования JMeter:

Средний запрос теперь занимает около 3 мс, что является значительным улучшением.

И если посмотреть на Hot spots соответствующего сеанса профилирования ЦП, это подтверждает, что горлышко бутылки, вызванное нашим методом, исчезло.

По общему признанию проблема в примере выше, кажется, очень очевидна. Но мы нашли очень похожий код в нашем производственном коде, скрытом в глубине системы (позор мне…). Важно отметить, что проблема не стала очевидной до того, как мы начали наши нагрузочные и нагрузочные тесты 5 .

Я предполагаю, что мы потратили бы много времени, чтобы вручную изучить базу кода, прежде чем — если вообще когда-нибудь — найти причину. Однако сеанс профилирования указал нам прямо на корень всего зла. И, как правило, реальную проблему было не сложно решить. Таким образом, профилирование может помочь вам справиться с некоторыми вашими задачами более эффективно.

По крайней мере, это для меня — и, кстати, — это тоже очень весело

  1. Обратите внимание, что код, который вызывает узкое место, может принадлежать и третьим библиотекам.
  2. Поскольку я сейчас делаю такой анализ в проекте для клиентов, у меня возникла идея написать этот пост
  3. Я не делаю здесь никакой рекламы или рейтинга инструментов — я просто использую знакомые мне инструменты, чтобы дать воспроизводимый пример более абстрактной концепции. Существует большая вероятность того, что на рынке есть лучшие инструменты для ваших нужд
  4. Обратите внимание, что единственная цель фрагментов кода в этом посте — служить примером того, как найти и устранить узкое место в производительности. Фрагменты написаны плохо и не должны использоваться повторно!
  5. Из моего опыта довольно часто встречается, что недавно созданная база кода содержит некоторые из этих самородков. Поэтому наличие таких тестов является обязательным условием для выявления проблем с производительностью до того, как заказчик обнаружит их в производстве……

Ссылка: Анализ производительности REST / HTTP-сервисов с JMeter и Yourkit от нашего партнера JCG Фрэнка Аппеля в блоге Code Affine .