Шаблон современного веб-приложения для Java

В этой статье описывается, как реализовать шаблон современного веб-приложения. Шаблон современного веб-приложения определяет, как модернизировать облачные веб-приложения и внедрить архитектуру, ориентированную на обслуживание. Шаблон предоставляет предписательную архитектуру, код и рекомендации по настройке, которые соответствуют принципам Azure Well-Architected Framework. Этот шаблон основан на шаблоне Reliable Web App.

Преимущества шаблона современного веб-приложения

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

Реализация шаблона современного веб-приложения

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

  • Руководство по архитектуре. Узнайте, как модульизировать компоненты веб-приложения и выбрать соответствующую платформу в качестве службы (PaaS).

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

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

Подсказка

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

Руководство по архитектуре

Шаблон современного веб-приложения основан на шаблоне Reliable Web App. Для этого требуется несколько дополнительных архитектурных компонентов. Вам нужна очередь сообщений, платформа контейнеров, служба хранилища и реестр контейнеров, как показано на следующей схеме.

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

Для более высокого уровня обслуживания (SLO) можно добавить второй регион в архитектуру веб-приложения. Настройте подсистему балансировки нагрузки для маршрутизации трафика во второй регион для поддержки активно-активной или пассивной конфигурации в зависимости от потребностей бизнеса. Оба региона требуют одинаковых служб, но один регион включает виртуальную сеть концентратора. Используйте топологию сети концентратора и периферийной сети для централизации и совместного использования ресурсов, таких как брандмауэр сети. Доступ к репозиторию контейнеров через виртуальную сеть концентратора. Если у вас есть виртуальные машины (ВМ), добавьте бастионный хост в центральную виртуальную сеть для управления ими с повышенной безопасностью. На следующей схеме показана эта архитектура.

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

Схема, демонстрирующая архитектуру шаблона современного веб-приложения с двумя регионами Azure. В каждом регионе есть веб-приложение на службе приложений, служба очереди сообщений, такая как Service Bus, распределенная служба на платформе контейнеров и независимые решения для хранения данных. Подсистема балансировки нагрузки распределяет трафик по обоим регионам, поддерживая конфигурации active-active или active-passive. Один регион включает в себя виртуальную сеть концентратора, которая централизованно использует общие ресурсы, например брандмауэр сети и узел бастиона для безопасного доступа к виртуальной машине. Доступ к реестру контейнеров можно получить через центральную сеть. Периферийные виртуальные сети в каждом регионе изолируют ресурсы приложений и подключаются к концентратору для общих сервисов. Архитектура поддерживает аварийное переключение между регионами, централизованный контроль безопасности и автономное масштабирование служб в каждом регионе.

Отделить архитектуру

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

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

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

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

  • Развертывание служб Azure. Выберите и разверните службы Azure, необходимые для поддержки службы веб-приложений, которую вы планируете извлечь. Дополнительные сведения см. в разделе "Выбор правильных служб Azure".

  • Разъедините службу веб-приложения. Определите четкие интерфейсы и API, которые недавно извлеченные веб-службы приложений могут использовать для взаимодействия с другими частями системы. Разработка стратегии управления данными, которая позволяет каждой службе управлять собственными данными, но обеспечивает согласованность и целостность. Дополнительные сведения о конкретных стратегиях реализации и шаблонах проектирования во время этого процесса извлечения см. в руководстве по коду.

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

  • Реализуйте отдельные конвейеры развертывания для каждой отдельной службы. При реализации отдельных конвейеров развертывания каждая служба может быть обновлена в соответствии с собственным расписанием. Если разные команды или организации в вашей компании имеют разные службы, использование отдельных конвейеров развертывания дает каждому команду контроль над собственными развертываниями. Используйте средства непрерывной интеграции и непрерывной доставки (CI/CD), такие как Jenkins, GitHub Actions или Azure Pipelines для настройки этих конвейеров.

  • Изменение элементов управления безопасностью. Убедитесь, что ваши меры безопасности обновлены с учётом новой архитектуры, включая правила брандмауэра и контроль доступа.

Выберите нужные службы Azure

Для каждой службы Azure в архитектуре обратитесь к соответствующему руководству по службе Azure в Well-Architected Framework. Для шаблона современного веб-приложения требуется система обмена сообщениями для поддержки асинхронного обмена сообщениями, платформы приложений, поддерживающей контейнеризацию и репозиторий образов контейнеров.

  • Выберите очередь сообщений. Очередь сообщений является важным компонентом архитектуры, ориентированной на обслуживание. Он отделяет отправителей сообщений и получателей для включения асинхронного обмена сообщениями. Выберите службу обмена сообщениями Azure , которая поддерживает ваши потребности в проектировании. В Azure есть три службы обмена сообщениями: Сетка событий Azure, Центры событий Azure и служебная шина. Сначала используйте Service Bus, и выберите один из двух других вариантов, если Service Bus не соответствует вашим потребностям.

    Услуга Сценарий использования
    Service Bus Выберите служебную шину для надежной, упорядоченной и, возможно, транзакционной доставки сообщений с высоким уровнем ценности в корпоративных приложениях.
    Event Grid Выберите сетку событий, когда необходимо эффективно обрабатывать большое количество дискретных событий. Система Эвент Грид масштабируется для приложений, ориентированных на события, которые направляют множество небольших независимых событий, таких как изменения состояния ресурсов, подписчикам посредством модели публикации и подписки с низкой задержкой.
    Event Hubs Выберите Центры событий для массового приема данных с высокой пропускной способностью, например телеметрии, журналов или аналитики в режиме реального времени. Служба Event Hubs оптимизирована для сценариев потоковой передачи, которые непрерывно принимают и обрабатывают большие объемы данных.
  • Реализуйте службу контейнеров. Для элементов приложения, который требуется контейнеризировать, требуется платформа приложений, поддерживающая контейнеры. Дополнительные сведения см. в статье "Выбор службы контейнеров Azure". В Azure есть три основных службы контейнеров: Приложения контейнеров Azure, Служба Azure Kubernetes (AKS) и Служба приложений Azure. Начните с приложений контейнеров и используйте один из двух других вариантов, если контейнерные приложения не соответствуют вашим потребностям.

    Услуга Сценарий использования
    Container Apps Выберите контейнерные приложения, если вам нужна бессерверная платформа, которая автоматически масштабирует контейнеры и управляет контейнерами в приложениях на основе событий.
    AKS Выберите AKS, если вам нужен подробный контроль над конфигурациями Kubernetes и расширенными функциями масштабирования, сети и безопасности.
    Веб-приложение для контейнеров Выберите веб-приложение для контейнеров в службе приложений для простого интерфейса PaaS.
  • Реализуйте репозиторий контейнеров. При использовании службы вычислений на основе контейнеров необходимо иметь репозиторий для хранения образов контейнеров. Вы можете использовать общедоступный реестр контейнеров, например Docker Hub или управляемый реестр, например Реестр контейнеров Azure. Дополнительные сведения см. в разделе "Общие сведения о реестре контейнеров".

Руководство по коду

Чтобы успешно разделить и извлечь независимую службу, необходимо обновить код веб-приложения с использованием паттернов Strangler Fig, Queue-Based Load Leveling, Competing Consumers, мониторинга конечных точек работоспособности и шаблонов повторных попыток. На следующей схеме показаны роли этих шаблонов.

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

  1. Шаблон Strangler Fig: Шаблон Strangler Fig постепенно переносит функциональные возможности из монолитного приложения в службу, разделенную. Реализуйте этот шаблон в основном веб-приложении для постепенной миграции функций в независимые службы путем направления трафика на основе конечных точек.

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

  3. Шаблон конкурирующих потребителей: Шаблон конкурирующих потребителей позволяет нескольким экземплярам развязанной службы независимо считывать из одной очереди сообщений и конкурировать за обработку сообщений. Реализуйте этот шаблон проектирования в отсоединенном сервисе, чтобы распределять задачи между несколькими экземплярами.

  4. Шаблон мониторинга конечных точек работоспособности: Шаблон мониторинга конечных точек работоспособности предоставляет конечные точки для мониторинга состояния и работоспособности различных компонентов веб-приложения. (4a) Реализуйте этот шаблон в основном веб-приложении. (4b) Кроме того, реализуйте его в отдельной службе для отслеживания работоспособности конечных точек.

  5. Шаблон повторных попыток: Шаблон повторных попыток обрабатывает временные сбои путем повторных попыток, которые могут периодически завершаться ошибкой. (5a) Реализуйте этот шаблон в основном веб-приложении во всех исходящих вызовах других служб Azure, таких как вызовы очереди сообщений и частные конечные точки. (5b) Кроме того, реализуйте этот шаблон в отдельной службе для обработки временных сбоев в вызовах частных конечных точек.

Каждый шаблон проектирования предоставляет преимущества, которые соответствуют одному или нескольким основным элементам платформы Well-Architected Framework.

Конструктивный шаблон Расположение реализации Надежность (RE) Безопасность (SE) Оптимизация затрат (CO) Операционное превосходство (OE) Эффективность производительности (PE) Поддержка принципов платформы Well-Architected
Шаблон Strangler Fig Основное веб-приложение RE:08
CO:07
CO:08
OE:06
OE:11
шаблон выравнивания нагрузкиQueue-Based Производитель отдельно функционирующей службы RE:06
RE:07
CO:12
РЕ:05
Шаблон конкурирующих потребителей Разделённая служба RE:05
RE:07
CO:05
CO:07
РЕ:05
PE:07
Шаблон мониторинга конечных точек работоспособности Основное веб-приложение и отделяемая служба RE:07
RE:10
OE:07
РЕ:05
шаблон повторных попыток Основное веб-приложение и отделяемая служба RE:07

Реализация шаблона Strangler Fig

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

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

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

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

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

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

Реализация шаблона проектирования балансировки нагрузки на основе очереди

Реализуйте шаблон выравнивания нагрузки на основе очереди на стороне производителя разъединенной службы для асинхронной обработки задач, которые не нуждаются в немедленном ответе. Этот шаблон повышает общую скорость реагирования системы и масштабируемость с помощью очереди для управления распределением рабочих нагрузок. Она позволяет разделённой службе обрабатывать запросы с постоянной скоростью. Чтобы эффективно реализовать этот шаблон, следуйте приведенным ниже рекомендациям.

  • Используйте очередь сообщений без блокировки. Убедитесь, что процесс, отправляющий сообщения в очередь, не блокирует другие процессы во время ожидания отсоединяемой службы для обработки сообщений в очереди. Если процессу требуется результат операции отсоединяемой службы, реализуйте альтернативный способ обработки ситуации, ожидая завершения операции в очереди. Например, в Spring Boot класс можно использовать StreamBridge для асинхронной публикации сообщений в очереди без блокировки вызывающего потока:

    private final StreamBridge streamBridge;
    
    public SupportGuideQueueSender(StreamBridge streamBridge) {
        this.streamBridge = streamBridge;
    }
    
    // Asynchronously publish a message without blocking the calling thread
    @Override
    public void send(String to, String guideUrl, Long requestId) {
        EmailRequest emailRequest = EmailRequest.newBuilder()
                .setRequestId(requestId)
                .setEmailAddress(to)
                .setUrlToManual(guideUrl)
                .build();
    
        log.info("EmailRequest: {}", emailRequest);
    
        var message = emailRequest.toByteArray();
        streamBridge.send(EMAIL_REQUEST_QUEUE, message);
    
        log.info("Message sent to the queue");
    }
    

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

  • Реализация повторных попыток и удаления сообщений. Реализуйте механизм для повторной обработки очередных сообщений, которые невозможно обработать успешно. Если сбои сохраняются, эти сообщения следует удалить из очереди. Например, Service Bus имеет встроенные функции повторной попытки и очередь неподтверждённых сообщений.

  • Настройте обработку сообщений с идемпотентным свойством. Логика, обрабатывающая сообщения из очереди, должна быть идемпотентной для обработки случаев, в которых сообщение может обрабатываться несколько раз. В Spring Boot используйте функциональную модель программирования с Spring Cloud Stream, определяя Function<> или Consumer<> бины, consume метод которых порождает одинаковый результат при многократном выполнении. Можно также использовать @KafkaListener с уникальным идентификатором сообщения, чтобы предотвратить повторную обработку. Чтобы дополнительно обеспечить идемпотентность, рассмотрите возможность отслеживания обработанных идентификаторов сообщений в постоянном хранилище и пропуска дубликатов. Список параметров, которые управляют поведением потребления сообщений, см. в разделе Spring Cloud Stream с служебной шиной.

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

Реализация шаблона конкурирующих потребителей

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

  • Последовательность обработки сообщений не имеет решающего значения.

  • Очередь остаётся неизменной при наличии неправильно сформированных сообщений.

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

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

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

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

  • Используйте режимы надежной обработки сообщений. Используйте надежный режим обработки, например peek-lock, который автоматически повторяет сообщения, которые завершаются сбоем. Режим обеспечивает более высокую надежность по сравнению с методами, основанными на первоочередном удалении. Если не удается обработать сообщение, другой работник должен иметь возможность обработать его без ошибок, даже если сообщение обрабатывается несколько раз.

  • Реализуйте обработку ошибок. Перенаправите неправильные или необработанные сообщения в отдельную очередь недоставленных сообщений. Эта конструкция предотвращает повторяющуюся обработку. Например, можно перехватывать исключения во время обработки сообщений и перемещать проблемные сообщения в отдельную очередь. Служебная шина перемещает сообщения в очередь отложенных сообщений после указанного количества попыток доставки или при явном отклонении приложением.

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

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

  • Используйте очередь ответа на сообщение. Если вашей системе требуются уведомления для обработки отправленных сообщений, настройте выделенную очередь ответов. Эта настройка отделяет операционные сообщения от процессов уведомлений.

  • Используйте службы без отслеживания состояния. Рассмотрите возможность использования служб без отслеживания состояния для обработки запросов из очереди. Такой подход позволяет легко масштабировать и эффективно использовать ресурсы.

  • Настройка ведения журнала. Интегрируйте ведение журнала и обработку конкретных исключений в рабочий процесс обработки сообщений. Сосредоточьтесь на захвате ошибок сериализации и перенаправлении этих проблемных сообщений в механизм обработки недоставленных сообщений. Эти журналы предоставляют ценные аналитические сведения для устранения неполадок.

Эталонная реализация использует шаблон 'Борющиеся потребители' в бессостояточном сервисе, который выполняется в Container Apps (контейнерных приложениях) для обработки запросов на доставку электронной почты из очереди служебной шины.

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

@Configuration
public class EmailProcessor {

    private static final Logger log = LoggerFactory.getLogger(EmailProcessor.class);

    @Bean
    Function<byte[], byte[]> consume() {
        return message -> {

            log.info("New message received");

            try {
                EmailRequest emailRequest = EmailRequest.parseFrom(message);
                log.info("EmailRequest: {}", emailRequest);

                EmailResponse emailResponse = EmailResponse.newBuilder()
                        .setEmailAddress(emailRequest.getEmailAddress())
                        .setUrlToManual(emailRequest.getUrlToManual())
                        .setRequestId(emailRequest.getRequestId())
                        .setMessage("Email sent to " + emailRequest.getEmailAddress() + " with URL to manual " + emailRequest.getUrlToManual())
                        .setStatus(Status.SUCCESS)
                        .build();

                return emailResponse.toByteArray();

            } catch (InvalidProtocolBufferException e) {
                throw new RuntimeException("Error parsing email request message", e);
            }
        };
    }
}

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

Реализуйте шаблон мониторинга конечных точек работоспособности в основном коде приложения и в независимом коде службы для отслеживания работоспособности конечных точек приложения. Оркестраторы, такие как AKS или контейнерные приложения, могут опрашивать эти конечные точки, чтобы проверить работоспособность службы и перезапустить неработоспособные экземпляры. Spring Boot Actuator предоставляет встроенные возможности для проверок состояния системы. Эта система может предоставлять конечные точки проверки работоспособности для ключевых зависимостей, например, баз данных, брокеров сообщений и систем хранения. Чтобы реализовать шаблон мониторинга конечных точек работоспособности, выполните следующие рекомендации.

  • Реализуйте проверки работоспособности. Используйте Spring Boot Actuator для предоставления конечных точек проверки работоспособности. Actuator предоставляет конечную точку /actuator/health , которая включает встроенные индикаторы работоспособности и пользовательские проверки для различных зависимостей. Чтобы включить конечную spring-boot-starter-actuator точку работоспособности, добавьте зависимость в файлpom.xml.build.gradle

    <!-- Add Spring Boot Actuator dependency -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

    Настройте конечную точку работоспособности в application.properties, как это показано в эталонной реализации.

        management.endpoints.web.exposure.include=metrics,health,info,retry,retryevents
    
  • Проверка зависимостей. Spring Boot Actuator включает индикаторы работоспособности для различных зависимостей, таких как базы данных, брокеры сообщений (RabbitMQ или Kafka) и службы хранения. Чтобы проверить доступность служб Azure, таких как хранилище BLOB-объектов Azure или служебная шина, рассмотрите возможность использования библиотек, таких как Micrometer или OpenTelemetry, которые предоставляют индикаторы работоспособности и наблюдаемость для этих служб. Если вам нужны пользовательские проверки, вы можете реализовать их, создав пользовательский HealthIndicator bean:

    import org.springframework.boot.actuate.health.Health;
    import org.springframework.boot.actuate.health.HealthIndicator;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CustomAzureServiceBusHealthIndicator implements HealthIndicator {
        @Override
        public Health health() {
            // Implement your health check logic here (for example, ping Service Bus).
            boolean isServiceBusHealthy = checkServiceBusHealth();
            return isServiceBusHealthy ? Health.up().build() : Health.down().build();
        }
    
        private boolean checkServiceBusHealth() {
            // Implement health check logic (pinging or connecting to the service).
            return true; // Placeholder. Implement the actual logic.
        }
    }
    
  • Настройте ресурсы Azure. Настройте ресурс Azure для использования URL-адресов проверки работоспособности приложения для подтверждения активности и готовности. Например, вы можете использовать Terraform для подтверждения активности и готовности приложений, развернутых в контейнерных приложениях. Дополнительные сведения см. в разделе Пробы работоспособности в Контейнерных приложениях.

Реализация шаблона повторных попыток

Шаблон повторных попыток позволяет приложениям восстанавливаться после временных сбоев. Этот шаблон является центральным шаблоном Reliable Web App, поэтому веб-приложение уже должно использовать шаблон повторных попыток. Примените шаблон повторных попыток к запросам к системам обмена сообщениями и запросам, выданным несвязанными службами, извлеченными из веб-приложения. Чтобы реализовать шаблон повторных попыток, следуйте приведенным ниже рекомендациям.

  • Настройка параметров повтора. Примените соответствующие параметры повторных попыток к клиенту, который отвечает за взаимодействие с очередью сообщений. Укажите такие параметры, как максимальное количество повторных попыток, задержка между повторными попытками и максимальной задержкой.

  • Используйте экспоненциальную задержку. Реализуйте стратегию экспоненциального отката для повторных попыток. Эта стратегия включает увеличение времени между каждым повтором экспоненциально, что помогает снизить нагрузку на систему в периоды высоких показателей сбоев.

  • Используйте функцию повторных попыток пакета SDK. Для служб, которые имеют специализированные пакеты SDK, такие как служебная шина или хранилище BLOB-объектов, используйте встроенные механизмы повторных попыток. Эти встроенные механизмы оптимизированы для типичных вариантов использования службы, могут эффективно обрабатывать повторные попытки и требовать меньшей конфигурации.

  • Используйте стандартные библиотеки устойчивости для HTTP-клиентов. Для HTTP-клиентов можно использовать Resilience4j вместе с Spring RestTemplate или WebClient для обработки повторных попыток HTTP-запросов. Вы можете обернуть RestTemplate логикой повторных попыток из Resilience4j, чтобы эффективно обрабатывать временные ошибки HTTP.

  • Обработка блокировки сообщений. Для систем на основе сообщений реализуйте стратегии обработки сообщений, поддерживающие повторные попытки без потери данных. Например, используйте режимы блокировки для просмотра, когда они доступны. Убедитесь, что неудачные сообщения повторно обрабатываются эффективно и перемещаются в очередь безвозвратных сообщений после повторяющихся сбоев.

Руководство по настройке

В следующих разделах приведены рекомендации по реализации обновлений конфигурации. Каждый раздел соответствует одному или нескольким основным элементам платформы Well-Architected Framework.

Конфигурация Надежность (RE) Безопасность (SE) Оптимизация затрат (CO) Операционное превосходство (OE) Эффективность производительности (PE) Поддержка принципов платформы Well-Architected
Настройка проверки подлинности и авторизации SE:05
OE:10
Реализация независимого автомасштабирования RE:06
CO:12
РЕ:05
Контейнеризация развертывания службы CO:13
PE:09
PE:03

Настройка проверки подлинности и авторизации

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

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

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

  • Используйте инфраструктуру в качестве кода (IaC). Используйте Bicep или аналогичное средство IaC, например Terraform, для определения облачных ресурсов и управления ими. IaC обеспечивает согласованное применение конфигураций безопасности в развертываниях и позволяет управлять версиями настройки инфраструктуры.

Чтобы настроить проверку подлинности и авторизацию для пользователей (удостоверений пользователей), выполните следующие рекомендации.

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

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

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

resource "azurerm_role_assignment" "container_app_acr_pull" {
  principal_id         = var.aca_identity_principal_id
  role_definition_name = "AcrPull"
  scope                = azurerm_container_registry.acr.id
}

resource "azurerm_user_assigned_identity" "container_registry_user_assigned_identity" {
  name                = "ContainerRegistryUserAssignedIdentity"
  resource_group_name = var.resource_group
  location            = var.location
}

resource "azurerm_role_assignment" "container_registry_user_assigned_identity_acr_pull" {
  scope                = azurerm_container_registry.acr.id
  role_definition_name = "AcrPull"
  principal_id         = azurerm_user_assigned_identity.container_registry_user_assigned_identity.principal_id
}


# For demo purposes, allow the current user to access the container registry.
# Note: When running as a service principal, this is also needed.
resource "azurerm_role_assignment" "acr_contributor_user_role_assignement" {
  scope                = azurerm_container_registry.acr.id
  role_definition_name = "Contributor"
  principal_id         = data.azuread_client_config.current.object_id
}

Настройка независимого автомасштабирования

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

  • Используйте службы без отслеживания состояния. Убедитесь, что ваши службы статичны. Если ваше веб-приложение содержит состояние сеанса внутри процесса, переместите его в распределенный кэш, например, управляемый Redis Azure или базу данных, например, SQL Server.

  • Настройте правила автомасштабирования. Используйте конфигурации автомасштабирования, которые обеспечивают наиболее экономичный контроль над службами. Для контейнерных служб масштабирование на основе событий, например Kubernetes Event-Driven Autoscaler (KEDA), часто предоставляет детализированный контроль, который позволяет масштабироваться на основе метрик событий. Контейнерные приложения и AKS поддерживают KEDA. Для служб, не поддерживающих KEDA, таких как служба приложений, используйте функции автомасштабирования, предоставляемые самой платформой. Эти функции часто включают масштабирование на основе правил на основе метрик или HTTP-трафика.

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

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

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

Например, эталонная реализация использует масштабировщик KEDA для Service Bus для автоматического масштабирования контейнерных приложений на основе длины очереди Service Bus. Правило масштабирования с именем service-bus-queue-length-rule регулирует количество реплик сервиса на основе количества сообщений в указанной очереди Service Bus. Параметр messageCount имеет значение 10, которое настраивает масштабировщик для добавления одной реплики для каждых 10 сообщений в очереди. Максимальное число реплик (max_replicas) имеет значение 10. Минимальное число реплик неявно равно 0, если оно не переопределено. Эта конфигурация позволяет службе масштабироваться до нуля, если в очереди нет сообщений. Строка подключения для очереди Service Bus хранится в Azure в качестве секрета с именем azure-servicebus-connection-string, который используется для проверки подлинности масштабировщика в Service Bus. Ниже приведен код Terraform:

max_replicas = 10
min_replicas = 1

custom_scale_rule {
  name             = "service-bus-queue-length-rule"
  custom_rule_type = "azure-servicebus"
  metadata = {
    messageCount = 10
    namespace    = var.servicebus_namespace
    queueName    = var.email_request_queue_name
  }
  authentication {
    secret_name       = "azure-servicebus-connection-string"
    trigger_parameter = "connection"
  }
}

Развертывание службы containerize

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

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

  • Создайте образы Docker. При создании образов Docker для служб Java используйте официальные базовые образы OpenJDK. Эти образы содержат только минимальный набор пакетов, которые должен выполнять Java. Они сводят к минимуму размер пакета и площадь поверхности атаки.

  • Используйте многоэтапные файлы Dockerfiles. Используйте многоэтапный Dockerfile для разделения ресурсов на этапе сборки от образа контейнера на этапе выполнения. Этот тип файла помогает сохранить рабочие образы небольшими и безопасными. Можно также использовать предварительно настроенный сервер сборки и скопировать JAR-файл в образ контейнера.

  • Запуск от имени нерутового пользователя. Запускайте контейнеры Java от имени пользователя, который не является root (через имя пользователя или UID $APP_UID), чтобы соответствовать принципу наименьших привилегий (PoLP). Этот подход ограничивает потенциальные последствия скомпрометированного контейнера.

  • Осуществлять прослушивание на порту 8080. При запуске контейнеров от имени пользователя без прав root настройте приложение для прослушивания на порту 8080. Эта практика распространена среди непривилегированных пользователей.

  • Инкапсулировать зависимости. Убедитесь, что все зависимости, необходимые приложению, инкапсулируются в образе контейнера Docker. Инкапсуляция позволяет приложению надежно развертываться на различных узлах.

  • Выберите правильные базовые изображения. Выбранный базовый образ зависит от среды развертывания. Например, при развертывании в контейнерных приложениях необходимо использовать образы Docker для Linux.

Эталонная реализация демонстрирует процесс сборки Docker для контейнеризации приложения Java. Dockerfile использует одноэтапную сборку с базовым образом OpenJDK,mcr.microsoft.com/openjdk/jdk:17-ubuntu который предоставляет необходимую среду выполнения Java.

Dockerfile включает следующие действия.

  1. Укажите объем. Определяется временный том (/tmp). Этот том предоставляет временное хранилище файлов, отдельное от основной файловой системы контейнера.

  2. Копирование артефактов. JAR-файлemail-processor.jar приложения копируется в контейнер вместе с агентом Application Insights (applicationinsights-agent.jar), который используется для мониторинга.

  3. Задайте точку входа. Контейнер настроен для запуска приложения с включенным агентом Application Insights. Код используется java -javaagent для мониторинга приложения во время выполнения.

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

# Use the OpenJDK 17 base image on Ubuntu as the foundation.
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu

# Set a working directory.
WORKDIR /app

# Define a volume for temporary files.
VOLUME /tmp

# Copy the packaged JAR file into the container.
COPY target/email-processor.jar app.jar

# Copy the Application Insights agent for monitoring.
COPY target/agent/applicationinsights-agent.jar applicationinsights-agent.jar

# Create a nonroot user and switch to it.
RUN useradd -m appuser && chown -R appuser /app
USER appuser

# Set the entrypoint to run the application with the Application Insights agent.
ENTRYPOINT ["java", "-javaagent:applicationinsights-agent.jar", "-jar", "app.jar"]

Развертывание эталонной реализации

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

На следующей схеме показана архитектура эталонной реализации.

Схема, демонстрирующая архитектуру эталонной реализации.

Скачайте файл Visio для этой архитектуры.

Следующий шаг