Секреты разработки высокопроизводительных приложений и микросервисов

Преимущества микросервисов

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

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

Ниже представлены общие преимущества микросервисной архитектуры.

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

Рекомендации по повышению производительности микросервисов

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

Поддержание общей производительности приложения становится первостепенной задачей по мере добавления микросервисов в приложение (или проект). Еще одна непростая задача  —  устранение сбоев в работе микросервисов.

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

Вот некоторые методы, которых я придерживаюсь при написании и анализе кода.

Дизайн, коммуникации и безопасность

  • Для разработки микросервиса выбирайте наиболее подходящую технологию и используйте рекомендованные методы.

Выберите стек технологий на основе функционального назначения и бизнес-функций/вариантов использования. У некоторых разработчиков формируются технологические предубеждения (эмоциональная привязанность) к любимому стеку технологий. Затем они пытаются разрабатывать на нем все микросервисы.

Например, сервисы на основе ИИ и МО можно создавать на Python или аналогичном языке. Если попытаться создать модели ИИ и МО на JAVA, их рабочая производительность может оказаться ниже ожидаемой.

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

  • Проектируйте микросервисы с использованием принципов SOLID.

Принципы проектирования SOLID являются основой для разработки хорошей и эффективной объектно-ориентированной программы или приложения. Я сторонник использования принципов SOLID при разработке микросервисов.

  • Разрабатывайте архитектуру микросервисов с целью повышения производительности и безопасности.

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

  • Обеспечьте обмен данными между микросервисами с помощью асинхронных (неблокируемых) запросов.

Синхронные запросы блокируются и могут вызывать проблемы с производительностью. Асинхронный обмен данными/информацией/сообщениями между двумя или более сервисами не требует от них немедленного ответа. Проще говоря, во время сеанса связи не требуется запуск и работа взаимодействующих сервисов.

Добиться этого можно с помощью очередей сообщений (Messaging queues) и опроса базы данных (Database polling). Kafka  —  одно из широко используемых решений для обмена данными между микросервисами внутри веб-сервиса.

Секреты разработки высокопроизводительных приложений и микросервисов
  • Ограничивайте объем доступной памяти.

Размеры доступной памяти и основного кода микросервисов должны быть небольшими и с быстрым доступом. Микросервис должен решать конкретный сценарий использования и ничего более. Производительность в этом случае возрастает.

Хороший микросервис не должен раскрывать методы/функции, не связанные с ним напрямую (например, продажи и закупки).

Если мы интегрируемся с микросервисом для конкретного сценария использования, задействуя при этом менее 30-40% его функций, то, вероятно, это не вызов микросервиса. Это больше похоже на низкопроизводительные мини-монолитные сервисы.

  • Кэшируйте токены OAuth и Kerberos.

Создание токенов безопасности требует затрат времени и усилий. При вызове систем рекомендуется кэшировать токены OAuth и/или Kerberos, чтобы избежать частого обращения к генерирующему токены API. Я предпочитаю время кэширования от 60 до 180 минут, но это зависит от уровня безопасности, который нужно обеспечить в микросервисах.

Spring хорошо интегрируется в качестве неблокирующего фонового процесса для кэширования и обновления токенов. Если в приложении используются распределенные сервисы сообщений (например, Kafka), то их токен аутентификации также должен быть кэширован для повышения производительности.

Базы данных

  • Выбирайте подходящий тип/технологию базы данных.

Время отклика микросервисов напрямую зависит от источника данных и времени отклика основной базы данных. Выбор и моделирование базы данных очень важны. Архитектура микросервисов может включать реляционную базу данных (RDBMS), хранилище ключ-значение и/или неструктурированные данные (например, изображения, видео) и пр.

Для повышения производительности структурированные данные должны храниться в реляционных базах данных, а неструктурированные  —  в хранилищах NoSQL, например Mongo DB и Cassandra.

Тип базы данных (RDBMS, No-SQL, Object DB и др.) должен выбираться исходя из требований проекта. Не следует придерживаться принципа “одно решение на все случаи”.

  • Кэширование базы данных.

Нужно кэшировать запросы и ответы на редко изменяемые (или справочные) значения в базе данных. Это уменьшит количество обращений к базе данных и предотвратит ее перегрузку. EHCache  —  сервис с открытым исходным кодом, который хорошо интегрируется с Hibernate, Spring и JPA.

Кроме того, нужно подобрать подходящую стратегию индексации/разделения в таблицах базы данных. Для соотношения размеров база данных/кэш я предпочитаю значения 80/20 или 70/30. Таким образом, хранение 20-30% извлекаемых данных повышает производительность.

Секреты разработки высокопроизводительных приложений и микросервисов
  • Оптимизируйте вызовы/запросы к базе данных.

Избегайте извлечения из базы данных всей строки (кортежа). Предположим, API обращается к таблице базы данных и предоставляет 10 атрибутов, а таблица содержит 40 атрибутов (или более). Если выполнить запрос “Выбрать все” или “Выбрать * по id”, он вернет всю строку/кортеж.

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

  • Обеспечьте пул соединений с базой данных.

Поддержка DBCP (Database Connection Pool) позволяет сэкономить на издержках создания и закрытия соединений. Создание нового соединения по каждому запросу требует ресурсов и времени, а DBCP позволяет использовать одно и то же соединение с базой данных для нескольких запросов.

Кроме того, стоит определить количество свободных/резервных соединений. Так имеющиеся ресурсы будут использоваться правильно.

  • Используйте кластеризацию базы данных.

Кластеризация с балансировкой нагрузки позволяет базе данных быстрее реагировать на запросы. Для этого есть следующие способы.

1. Конфигурация Master-Slave, в которой подчиненные устройства доступны только для чтения и в конечном итоге согласованы.

2. Конфигурация Master-Master  —  она немного медленнее по сравнению с Master-Slave.

  • Настройте базу данных и выберите подходящую стратегию индексации и/или разделения.

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

При использовании JPA рекомендуется регистрировать и просматривать сгенерированные JPA на этапе разработки запроса, так как иногда они добавляют ненужные соединения и самосоединения.

Кэширование и масштабирование сервера

  • Настройте кэширование на стороне сервера.

Правильная стратегия кэширования способствует повышению производительности. Я рекомендую кэшировать отклик микросервисов в зависимости от запроса и его параметров. Редко изменяющиеся отклики (например, изображения, видео и детали элемента) можно кэшировать на основе входных параметров. Это улучшит производительность, поскольку бизнес-логика/вычисления не должны выполняться для аналогичных/одинаковых запросов.

Memcachedи Redis —  хорошие примеры расположенных в памяти кэшей. В них ключ-значение хранится между приложением и базой данных. Redis  —  это распределенный в памяти и расширенный инструмент кэширования, который также позволяет выполнять резервное копирование и восстановление. Оба они хорошо интегрируются с микросервисами на базе Spring. Для видео (клипы, фильмы и пр.) хорошим решением является CDN.

  • Обеспечьте масштабирование.

Чтобы справиться с возросшей нагрузкой на микросервис применяют вертикальное (scaling up) и горизонтальное (scaling out) масштабирование.

Вертикальное масштабирование означает увеличение объема памяти одного сервиса. При этом требуется перезапуск микросервиса (приостановка работы). Такое масштабирование сильно зависит от доступности базовой системы хранения.

Горизонтальное масштабирование означает добавление нового узла для обслуживания запросов. Это можно сделать на одном или разных хост/облачных пулах. Для одного пула распространенным сервисом является Auto-scaler, доступный у большинства облачных провайдеров. Автоматическое масштабирование можно настроить на основе пропускной способности HTTP, использования памяти и т. д.

На различных хостах используется балансировщик нагрузки для распределения трафика между микросервисами, запущенными на нескольких узлах/облачных пулах. Это может быть Pure Geographic, Round Robin и более настраиваемый балансировщик.

Шлюзы API, ограничители скорости и прокси

  • Установите ограничители скорости.

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

Заключение

Хорошая разработка  —  это скорее привычка, чем разовая задача. Если при написании любого кода, будь то микросервис или другая программа, учитывать передовые методики и рекомендации, то в конечном итоге получится эффективный и высокопроизводительный код.

Читайте также:

Читайте нас в Telegram, VK и Дзен

Перевод статьи Saurabh Gupta: Developing High-Performance Applications & Microservices

Сообщение Секреты разработки высокопроизводительных приложений и микросервисов появились сначала на NOP::Nuances of programming.