Проектирование высоконагруженных сервисов больше не является загадочным навыком, которым владеют только просвещенные сенсеи.
Сегодня для этого существуют устоявшиеся практики, которые комбинируются и модифицируются в зависимости от особенностей компании и ее бизнеса.
Мы хотим рассказать о наших лучших методах и инструментах для создания высоконагруженных сервисов.
Скажем сразу, что речь идет о заказной разработке.
Как нам завещал здравый смысл, мы начинаем проектирование с постановки бизнес-задач.
А еще определяем, будет ли система сильно загружена.
Понятно, что со временем она будет меняться (например, изменится профиль нагрузки), поэтому при описании бизнес-логики мы закладываем возможные пути развития там, где это возможно.
Скажем, если это какой-то клиентский сервис, то можно спрогнозировать изменение размера аудитории.
Иногда сразу понятно, как система будет меняться с течением времени, а некоторые контрольные точки предсказать сложно.
На начальном этапе мы можем посчитать объемы данных, которые нам необходимы для хранения, рассчитать начальную нагрузку на наш абстрактный сервис и скорость ее роста.
Также вы можете классифицировать данные по типу работы с ними: хранение, запись или чтение и в зависимости от этого оптимизировать процессы.
У нас есть KPI работы системы, на основании которых определяется допустимая степень ее деградации.
Для большинства систем время отклика имеет решающее значение.
В одних случаях оно может достигать нескольких секунд, а в других исчисляться миллисекундами.
Также на этом этапе определяется степень доступности системы.
Обычно мы разрабатываем системы высокой доступности или непрерывной работы: с уровнями доступности от 99,9% до 99,999%.
Определившись с бизнес-логикой, приемлемыми объемами данных и поведением системы, приступаем к построению диаграмм потоков данных.
Это необходимо для того, чтобы понять, какой шаблон проектирования лучше выбрать.
И после построения схем приступаем к самому проектированию.
Микросервисы – модное словечко нашего времени
В последнее время при создании высоконагруженных систем мы отдаем предпочтение сервис-ориентированной архитектуре.В частности, микросервисы, которые так модны в последние годы: мы берем функционал системы и разбиваем его на мелкие части — сервисы, каждый из которых выполняет какую-то небольшую задачу.
Сервисов может быть много, сотни или тысячи.
Такая архитектура упрощает обслуживание системы, а изменения, вносимые в отдельные сервисы, оказывают гораздо меньшее влияние на систему в целом.
Кроме того, еще одним важным преимуществом микросервисной архитектуры является хорошая масштабируемость.
Но помимо существенных преимуществ, у микросервисной архитектуры есть и существенный недостаток: не всегда возможно разделить бизнес-логику на достаточно маленькие функциональные части.
Эта задача сложна и, как правило, решается на стадии проектирования.
Другая сторона медали — межсервисная связь.
Необходимо позаботиться о том, чтобы накладные расходы на обмен сообщениями не были слишком высокими.
Достигается это в основном за счет выбора эффективных методов сериализации/десериализации данных (подробнее об этом ниже), перемещения взаимодействующих сервисов «ближе» друг к другу или объединения их в один микросервис.
Но если вы сможете успешно разделить логику на небольшие задачи, то к вашим услугам будут все преимущества микросервисной архитектуры.
Микросервисная архитектура позволяет размещать сервисы на большом количестве узлов и обеспечивать необходимую избыточность на случай выхода из строя некоторых узлов (тьфу-тьфу-тьфу).
Схема резервирования во многом зависит от исходных задач.
В некоторых местах паузы допускать нельзя и требуется немедленное восстановление сервиса — в этом случае используется схема Active/Active, когда балансировщик моментально отключает вышедший из строя узел.
В остальных случаях допускается небольшой простой простоя на время восстановления сервиса, и тогда используется схема Active/Standby, когда резервный узел становится доступным не сразу, а через небольшой промежуток времени, пока сервис автоматически переводится на резервный узел.
Очевидными примерами необходимости использования вариантов схемы Актив/Актив являются услуги дистанционного банковского обслуживания – Интернет и мобильный банкинг.
Традиционно Банк ВТБ выполняет достаточно большой объем заказной разработки программного обеспечения.
До недавнего времени для этих целей в основном использовались технологии .
NET и Java Enterprise. Сейчас мы рассматриваем эти инструменты как наследие.
Для новых проектов мы стали использовать подходы, основанные на микросервисной архитектуре (MSA — Microservice Architecture).
Точнее, новые системы в банке спроектированы как набор микросервисов на платформе Spring Boot. Основными причинами выбора Spring Framework стали его широкое распространение на рынке ИТ-услуг, а также относительная простота его использования при разработке сервисов и микросервисов.
Для взаимодействия с пользователем обычно используется веб-интерфейс, разработанный с использованием React или Angular, взаимодействующий с серверными компонентами через Rest API. Отдельная тема — взаимодействие компонентов MSA между собой, а также их интеграция с существующей ИТ-экосистемой.
Наряду с традиционными подходами, такими как очереди MQ, мы активно внедряем новые шаблоны взаимодействия, основанные на слабой связи и платформе Apache Kafka.
Языки и фреймворки
Сейчас существует довольно много технологий, позволяющих строить высоконагруженные системы с малым временем отклика.Но их состав постоянно меняется: постоянно что-то приходит из мира Open Source, разрабатываются новые внутренние проекты, часть из которых тоже переходит в лагерь open source. Однако мы отдаем предпочтение проверенным решениям от известных вендоров.
Наша задача – повысить компетентность в использовании таких систем.
Трудно представить высоконагруженную систему, не использующую кэширование данных.
Хотя на рынке здесь представлен большой выбор инструментов, мы обычно придерживаемся проверенной «классики» — Memcached и Redis. Но в последнее время люди начали обращать внимание на Apache Ignite; в некоторых проектах хорошо зарекомендовал себя как распределенный кэш.
Что касается выбора языков программирования, то здесь многое зависит от поставленной задачи.
Как правило, выбор падает на Java — существует большое количество фреймворков, позволяющих быстро и эффективно реализовать необходимый функционал, и большое количество настроек самой JVM, позволяющих добиться необходимых показателей производительности.
При проектировании MSA мы также придерживаемся стека технологий Java. Помимо унификации это позволяет нам легко интегрироваться с уже работающими в банке приложениями посредством традиционных механизмов интеграции — очередей MQ, веб-сервисов и многочисленных API на базе Java. Это также позволяет использовать разработанные инструменты разработки и управления микросервисами, доступные на этой платформе.
Немаловажно и то, что на российском рынке уже достаточно много специалистов, разрабатывающих MSA-приложения с использованием стека технологий на базе JVM.
Хранилище данных
Что касается хранения данных, то первый вопрос, который возникает при проектировании: какую модель данных должна использовать СУБД — реляционную или какую-то другую? От выбора зависят дальнейшие методы повышения эффективности обработки запросов к базе данных, ведь высоконагруженные сервисы априори должны иметь малое время отклика.Если мы говорим о реляционных СУБД, то их использование в высоконагруженных проектах требует решения задач повышения эффективности запросов.
Все начинается с анализа плана запроса.
Как правило, по его результатам мы меняем сами запросы и добавляем индексы.
К таблицам можно применить секционирование, уменьшив объем данных в выборках, ограничив их одной или несколькими секциями.
Также часто приходится использовать денормализацию данных, внося некоторую избыточность — так можно повысить эффективность запросов.
А с нереляционными СУБД приходится использовать практически индивидуальные подходы в зависимости от конкретного продукта и бизнес-сценариев.
Из общих подходов можно выделить лишь отложенную обработку трудоемких вычислений.
Конечно, все зависит от конкретной задачи, но по возможности мы используем Oracle, ведь написание эффективных запросов и настроек отнимает у наших инженеров меньше времени.
Кроме того, в последнее время мы часто обращаем внимание на Apache Ignite.
Масштабирование
Как уже говорилось выше, мы не всегда можем предсказать, какую нагрузку будет испытывать созданная система через полгода-год. И если не заложить возможность масштабирования, система очень быстро перестанет справляться с растущей нагрузкой.В зависимости от конкретного проекта применяется вертикальное или горизонтальное масштабирование.
Вертикальное — повышение производительности отдельных компонентов за счет добавления ресурсов внутри одного компонента/узла; горизонтальный – увеличение количества узлов/узлов для распределения нагрузки по ним.
Гораздо проще масштабировать сервис или компонент, если он использует парадигму без сохранения состояния, то есть не сохраняет контекст между запросами.
В этом случае мы легко можем развернуть в системе несколько копий этого компонента или сервиса, балансируя нагрузку между ними.
В случае парадигмы с отслеживанием состояния необходимо позаботиться о том, чтобы при балансировке учитывалось наличие состояний в компонентах/сервисах.
Физический мир
Тесты, тесты и еще раз тесты
Запуск высоконагруженной системы немыслим без нагрузочного тестирования.Нагрузочное тестирование подразумевает проверку достижения определенных KPI на входе.
По нашему опыту, длительное время обработки запроса чаще всего связано с неэффективностью алгоритма.
Также повышению производительности способствует кэширование данных для большого количества однотипных запросов.
Тесты позволяют нам выявить узкие места, которые мы могли упустить из виду при проектировании.
Веб-сервис, реализованный в рамках одного из проектов, показал низкую производительность при нагрузочном тестировании, несмотря на свою простоту.
Профилирование показало, что большая часть времени ушла на сериализацию и десериализацию данных, то есть мы по сути переводили структуры данных в битовое представление и обратно.
Решение было найдено довольно быстро — характер данных был таков, что они менялись крайне редко.
Нам удалось реализовать предварительную сериализацию данных (заранее подготовив сериализованное представление) и значительно сократить время отклика.
Оптимизация сериализации для внешнего API (предварительная сериализация)
Наряду с нагрузочным тестированием, в ходе которого мы проверяем систему на соответствие требованиям, мы также проводим нагрузочные тесты.
Цель таких испытаний – выяснить максимально возможную нагрузку, которую может выдержать система.
Мониторинг
Метрики действуют как датчики; любая серьезная система, особенно высоконагруженная, густо усыпана ими.Без метрик любая программа превращается в черный ящик с входами и выходами, непонятно как он работает внутри.
При проектировании в архитектуру встраиваются счетчики для формирования информации для системы мониторинга.
В некоторых проектах использовался ELK (Elastick Search Kibana) для визуализации активности системы и ее мониторинга.
Данный фреймворк имеет гибкий интерфейс, позволяющий легко и быстро настроить необходимую форму отчетности.
Мониторинг Кибаны
Заключение
Не существует серебряной пули при построении отказоустойчивых высоконагруженных систем.С нагрузками мы можем справиться не за счет технологий, а за счет правильно выбранной архитектуры системы.
Это всегда поиск каких-то компромиссов.
В некоторых случаях мы можем использовать существующие подходы и шаблоны, но в других нам приходится разрабатывать и реализовывать собственные архитектурные решения.
Теги: #ИТ-инфраструктура #Системное администрирование #Высокая производительность #микросервисная архитектура #сервисы с высокой нагрузкой
-
Десять Лучших «Пасхалок» Рунета
19 Oct, 24 -
Neonray: Рабочий Стол, Пойдем Со Мной!
19 Oct, 24 -
Телекоммуникации С 1 Января 2010 Г.
19 Oct, 24 -
Вр - Выпуск №50
19 Oct, 24 -
Что Такое Дизайн? Бьюсь Об Заклад
19 Oct, 24