Статьи

Как загрузить тестирование и настройку производительности в вашем API (часть II)

Эти посты основаны на презентации Марка на APIStrat / APIDays Berlin. Видео теперь доступно на  YouTube .

[Эта статья была написана Виктором Дельгадо]

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

Теперь мы рассмотрим способы безопасного предоставления доступа к вашему API-интерфейсу, чтобы убедиться, что его производительность не пострадает.

Как добавление слоя контроля доступа влияет на ваш API

На данный момент у нас есть достаточно высокопроизводительный API, но что произойдет, если кто-то начнет отправлять трафик со скоростью, превышающей 16 000 запросов в секунду? Как вы можете помешать любому из ваших пользователей API начать отправлять запросы к вашему API, влияя на всех других разработчиков, потребляющих его?

Существует несколько способов решения этой проблемы, наиболее очевидным из которых является встраивание системы ограничения скорости в ваш собственный сервер API. Мы верим в максимально возможное разделение проблем, поэтому мы считаем, что лучше иметь отдельный слой в вашем стеке, который будет играть эту роль.

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

В этом случае мы собираемся использовать наш собственный API-шлюз, который вместе с платформой 3scale позволяет провайдерам API безопасно открывать свои API для всего мира. Это простой способ начать требовать аутентификацию и установить ограничения скорости на каждой из ваших конечных точек API.

Существует  несколько способов  интеграции вашего API с 3scale, но мы собираемся выбрать локальный шлюз API на основе Nginx, высокопроизводительного прокси-сервера. Мы развернем шлюз API 3scale перед нашим сервером API. Причиной использования прокси-сервера является не только производительность: поскольку он развернут перед нашим API, трафик, который не прошел проверку подлинности или превышает допустимые пределы, будет отклонен, даже не достигнув нашего сервера API. Это значительно упрощает понимание нашего API-сервера, поскольку мы можем спроектировать его для известного объема трафика. Наш API-шлюз будет заниматься всем остальным. Конечно, мы всегда сможем настроить ограничения на панели администратора 3scale, увеличить их при увеличении емкости нашего API-сервера или добавить новые ограничения, если число пользователей неожиданно увеличится.

схема шлюза API

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

Мы развернем шлюз в том же типе экземпляра AWS, который мы используем для нашего API-сервера, c4.large с двумя виртуальными ядрами. Настройка шлюза выполняется практически  одним щелчком мыши, поскольку мы можем воспользоваться нашим AMI, доступным на  рынке AWS .

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

ПОЛУЧИТЕ http://our-api-gateway.com/question?user_key=ABCD123

Запустив тот же тест, что и раньше, со скоростью 10000 запросов в секунду, мы обнаружили несколько ошибок. Loader.io останавливает тест из-за большого количества ошибок.

Снимок экрана 2015-05-14 в 12.36.14 вечера

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

2015/04/12 23:07:10 [alert] 2573#0: *147508 256 worker_connections are not enough while connecting to upstream, client: ..., server: , request: "GET /question HTTP/1.1", upstream: "http://.../question", host: "..."

Nginx должен открывать как минимум два соединения для каждого запроса, одно к клиенту, а другое к прокси-серверу. Также необходимо открыть подключения к 3scale, хотя и не для каждого запроса (мы используем версию шлюза API 3scale для расширенной емкости, которая объединяет авторизацию и сообщает о вызовах 3scale).

Принимая это во внимание, при скорости 10000 об / с количество одновременных открытых соединений, которые Nginx должен будет поддерживать, будет намного выше 256.

Точная настройка вашего прокси API для максимальной производительности

Количество одновременных открытых соединений настраивается через директиву worker_connections . Этот параметр также сильно зависит от базовой системы: Nginx никогда не сможет открыть больше соединений, чем доступные сокеты, которые может предложить система. Если мы хотим увеличить это число, необходимо изменить пару параметров.

In /etc/security/limits.conf:
# increase the number of file descriptors available for
# any process (you will need to reboot to apply this)

# add these to the bottom of the file
* soft nofile 200000
* hard nofile 200000

In /etc/sysctl.conf:
# increase the system-wide limit on the number of open files for all processes
# apply the change running:  sudo sysctl -p /etc/sysctl.conf
fs.file-max = 5000000

After this, you can tell Nginx to use more connections by setting the following in the nginx.conf file:

...
worker_rlimit_nofile 100000;
events {
worker_connections  100000;
}
...

Существуют и другие оптимизации, которые можно выполнять как на уровне Nginx, так и на уровне системы. О некоторых из них вы можете прочитать в документации по  Nginx .

Настройки, которые принесли наибольшее преимущество в нашем случае, были связаны с оптимизацией использования сетевых ресурсов:

  • Включение  keepalive  для апстрима . Это требует установки протокола HTTP на версию 1.1, что нормально, так как это версия, используемая Node.js по умолчанию.
  • Сокращение tcp_fin_timeout для минимизации соединений в состоянии TIME_WAIT, чтобы у нас не было много устаревших соединений. Также включение tcp_tw_reuse с той же целью.
  • Установите для  worker_processes  значение «auto», чтобы Nginx всегда запускал один рабочий процесс для каждого доступного ядра ЦП.

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

Существуют и другие параметры, которые могут вводить в заблуждение, поскольку они могут оказывать гораздо большее влияние в среде нагрузочного тестирования, чем на производственный трафик. Например, увеличение значения   директивы keepalive_requests в Nginx позволяет отправлять больше запросов через одно соединение keep-alive. Поскольку в нашем бенчмарке вся нагрузка отправляется из одного источника, это имеет большее преимущество, чем сценарий, в котором нагрузка может исходить из нескольких источников.

Мы хотим узнать, вступили ли оптимизации в силу, поэтому мы запускаем тест производительности Loader.io со скоростью 10.000 запросов в секунду в течение 60 секунд. Вот вывод из пары этих тестовых прогонов:

Снимок экрана 2015-05-14 в 12.26.46 PMСнимок экрана 2015-05-14 в 12.03.55 вечера

Результаты в настоящее время хорошие, и мы достигаем ожидаемого уровня с фактически 0 ошибками. Есть небольшая разница, которая заключается в том, что есть некоторые всплески, где задержка увеличивается. Эти всплески происходят, когда Nginx необходимо сообщить в 3scale, что влияет на несколько входящих клиентских запросов, которые обрабатываются именно в это время. Пакетирование вызовов отчетов в шлюзе API выполняется для каждого клиента, поэтому влияние здесь очень заметно, поскольку мы производим большой объем трафика от одного клиента. Кроме того, во время этих испытаний дозирование проводилось довольно часто. Если мы установим период больше, чем продолжительность нашего теста, мы получим результат, который почти идентичен тому, когда мы напрямую обращались к API.

Снимок экрана 2015-05-14 в 12.02.23 вечера

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

Выполнение теста 1 м @ http: // api-gateway / question
4 потока и 1000 подключений
Статистика потоков Avg Stdev Max +/- Stdev
Latency 85,27ms 90,29ms 1,27s 92,76%
Треб. / Сек. 3,98k 588,91 4,27k 64,50%
Распределение задержек
50% 85,75 мс
75% 96,61 мс
90% 127,49 мс
99% 230,90 мс
955320 запросов за 1,00 м, 194,17 МБ запросов на чтение
/ сек: 15942,54
передачи / с: 3,23 МБ

Снимок экрана 2015-05-14 в 12.02.09 PM

Вывод

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

Наши основные выводы из этого опыта:

  • Всегда устанавливайте свои цели перед запуском теста. Лучше всего, если эти цели основаны на данных о вашем текущем трафике. Это будет означать цель производительности, которую вы должны достичь.
  • Потратьте время на подготовку и оценку хорошей и стабильной исходной среды. Результаты вашего теста бесполезны, если их нельзя сравнить с известной и надежной отправной точкой.
  • Не позволяйте инструменту тестирования становиться ограничивающим фактором. Изучите характеристики каждого инструмента (например, однопоточный или многопоточный), чтобы узнать.
  • Просматривайте все слои стека при точной настройке производительности ваших компонентов. Увеличение лимита на сервере API ничего не даст, если ограничивающий фактор находится на системном уровне.
  • Наличие уровня управления API обеспечит вам спокойствие, поскольку ваш API будет надежно защищен и будет получать только предсказуемый объем трафика. API-шлюз будет иметь дело со всем остальным.

Мы надеемся, что это практическое руководство окажется полезным для компаний, которые прыгнули на борт API wagon и задаются вопросом, как их API работает под нагрузкой.