Исследование объёма используемых ресурсов и стабильности приложения при переходе на реактивные компоненты в случае увеличения времени ответа от внешних источников данных
Введение 3
Постановка задачи 5
1. Реактивный подход к программированию 6
1.1. Синхронный подход к программированию 6
1.2. Концепция реактивного программирования 8
1.3. Способы написания кода в реактивном стиле в Java и Kotlin 9
1.4. Реактивные компоненты и драйверы баз данных 11
2. Тестовый стенд для нагрузочного тестирования 13
2.1. Архитектура тестового стенда для нагрузочного тестирования 13
2.2. Ресурсы и изолированность тестовой среды 14
2.3. Тестируемое приложение 16
2.4. Нагрузочное приложение 17
2.5. Сбор и визуализация метрик 19
3. Проведение экспериментов 21
3.1. Рассматриваемые метрики 21
3.2. Обращение к внешнему сервису 22
3.3. Обращение к базе данных 25
3.4. Обработка записей из базы данных 30
3.5. Обработка записей из базы данных внешним сервисом 35
3.6. Выводы 38
Заключение 40
Список литературы 41
Реактивное программирование как идея появилось ещё в двадцатом веке. Однако, долгое время рост производительности систем происходил в первую очередь за счёт роста производительности процессоров. Но в конце 2000-х годов этот рост сильно замедлился, из-за чего фокус внимания в области улучшения производительности систем был вновь обращён на асинхронное и реактивное программирование, как на способ улучшить производительность систем при использовании тех же ресурсов.
Традиционный (синхронный) подход к программированию представляет собой набор последовательных блокирующих операций и вызовов внешних систем (например, обращений к базам данных или вызовов сторонних сервисов). Такой подход зачастую может быть неэффективным из-за того, что в момент ожидания ответа от внешней системы, ресурсы вызывающей системы заблокированы и простаивают.
Одним из вариантов улучшения производительности является переход к концепции реактивного программирования. Реактивное программирование предполагает создание системы, реагирующей на изменение в данных в режиме реального времени. Такой подход подразумевает возможность начать обработку не сразу всех данных, а лишь той части, о которой системе уже известно. Например, если необходимо извлечь из базы данных большое количество записей, а затем обработать каждую из них, в традиционном подходе сначала были бы получены все записи путём запроса к базе данных, затем каждая запись была бы обработана и лишь затем результат обработки всех данных был бы подан на выход системы. В случае же использования реактивного подхода, реактивный драйвер базы данных будет подавать на вход системе обработки не сразу все записи, а последовательно те, которые уже найдены и извлечены, затем система обработки будет обрабатывать последовательно данные, получаемые от драйвера, при этом такая обработка не будет дожидаться получения всех данных, а будет реагировать на каждую новую поступающую на вход запись, а затем система будет подавать на выход обработанные данные по мере готовности.
В 2011 году компанией Microsoft был представлен первый крупный реактивный фреймворк - ReactiveX [1]. В языке программирования Java (и других JVM-языках) фреймворк получил наибольшую популярность в виде расширения RxJava после выхода 8-й версии Java в 2014-м году, поддерживающей лямбда-выражения, которые удобны для работы в реактивном стиле. В 2018-м году было представлено расширение для языка Kotlin, позволяющее использовать корутины для написания реактивного кода. Это значительно понизило порог входа для написания реактивных приложений.
Наиболее популярным универсальным фреймворком для разработки сервисов в мире JVM является Spring Framework. В 2017-м году в Spring появилась поддержка реактивного программирования. Однако, разработка реактивных приложений на Spring упиралась в отсутствие реактивного драйвера для разных баз данных, в том числе для одной из самых популярных - PostgreSQL. В 2019-м году разработчики Spring представили такой драйвер, который получил название R2DBC, а в середине 2022 года была выпущена первая стабильная версия этого драйвера для взаимодействия с базами данных на основе PostgreSQL - 1.0.0 [2], что окончательно позволило разрабатывать полностью реактивные системы с использованием Spring.
Теоретически, во многих случаях использование реактивного подхода улучшает производительность системы [3]. Однако, так ли это на реальных примерах? В каких ситуациях выгодно использовать реактивных подход, а где синхронный стиль написания кода показывает себя лучше? Насколько эффективные реактивные драйверы базы данных по сравнению с традиционными?
Данная работа призвана ответить на простой по формулировке вопрос: "В каких сценариях работы приложения выгодно сделать его реактивным"?
В ходе работе были выполнены следующие задачи:
• Разработан изолированный тестовый стенд, а также написан код для его быстрого автоматического развёртывания и проведения нагрузочного тестирования по описанному сценарию;
• На тестовом стенде проведены эксперименты, изучающие и сравнивающие поведение нереактивной и реактивной реализации приложений в нескольких сценариях обработки запроса;
• На основе проведенных экспериментов исследованы стабильность и утилизация ресурсов приложения с реактивными компонентами и без них, в том числе при росте времени ответа внешних сервисов и источников данных;
• Получены выводы о целесообразности использования реактивных компонентов в тех или иных ситуациях, подробно описанные в разделе 3.6.