Зачем использовать API-шлюз

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

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

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

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

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

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

Концептуальный обзор Zuul

Zuul - это API-шлюз или пограничный сервер, разработанный Netflix. Он способен выполнять следующие задачи.

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

Фильтры

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

  • pre - фильтры запускаются до маршрутизации запроса.
  • route - фильтры могут обрабатывать фактическую маршрутизацию запроса.
  • post - фильтры запускаются после маршрутизации запроса.
  • error - фильтры запускаются при возникновении ошибки в процессе обработки запроса.

Интеграция с Eureka

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

Реализация шлюза Zuul с помощью Spring Cloud

Как уже было сказано выше, Zuul изначально был разработан Netflix. Однако Spring разработала оболочку для Netflix Zuul, чтобы ее можно было легко включить в приложения на основе Spring. Эта оболочка входит в состав фреймворка Spring Cloud. В приведенном ниже примере мы будем использовать версию Zuul Spring Cloud.

Обзор проекта

В этом примере мы создадим три артефакта, чтобы продемонстрировать процесс маршрутизации запросов Zuul. Вы можете найти готовый исходный код этого примера в GitHub.

  • Курс микросервис
  • Zuul API Gateway
  • Реестр услуг Eureka

Создание микросервиса курса

Курс представляет собой простой микросервис, написанный на Spring Boot для предоставления конечной точки для получения списка курсов. Кроме того, микросервис Course будет настроен как клиент обнаружения, так что его сетевое расположение может быть обнаружено через Eureka.

Создайте проект с помощью Spring Initializr

Вы можете использовать Spring Initializr для генерации начальной структуры проекта.

Конфигурации Maven (pom.xml)

Добавление модельного класса (Course.java)

Добавление контроллера REST (CourseController.java)

Особые примечания:

  • Как видите, одна конечная точка (/ курсы) была предоставлена ​​для получения списка курсов.

Добавление основного класса (CourseApplication.java)

Особые примечания:

  • Аннотации @EnableEurekaClient использовались, чтобы сделать эту службу клиентом Eureka.

Настройка свойств приложения (application.properties)

Реализация сервера Eureka

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

Создайте проект с помощью Spring Initializr

Вы можете использовать Spring Initializr для генерации начальной структуры проекта.

Конфигурации Maven (pom.xml)

Добавление основного класса (EurekaApplication.java)

Особые примечания:

  • Аннотация @EnableEurekaServer творит чудеса, превращая это простое приложение в сервер Eureka.

Настройка свойств приложения (application.properties)

Особые примечания:

  • Приложения Spring Boot по умолчанию запускаются на порту 8080. Я переопределил это на 8761, чтобы сервер Eureka не конфликтовал с портом микросервиса курса (микросервис курса будет работать на 8080).
  • Когда Eureka запускается, он пытается зарегистрироваться как клиент. Для удобства я использовал конфигурацию eureka.client.register-with-eureka = false, чтобы сервер Eureka не регистрировался на сервере при запуске.
  • В реальном сценарии у нас может быть несколько узлов сервера Eureka, действующих вместе как одноранговые реестры. Когда сервер Eureka запускается, по умолчанию он ищет другие одноранговые реестры. Чтобы предотвратить это в нашей локальной настройке, я использовал конфигурацию eureka.client.fetch-registry = false.
  • Я использовал свойства logging.level.com.netflix.eureka = OFF и logging.level.com.netflix.discovery = OFF, чтобы отключить подробное ведение журнала.

Настроить Zuul Proxy

В качестве последнего шага мы должны реализовать прокси-сервер Zuul для маршрутизации запросов к микросервису Course.

Создайте проект с помощью Spring Initializr

Вы можете использовать Spring Initializr для генерации начальной структуры проекта.

Добавление основного класса (ZuulApplication.java)

Особые примечания:

  • @EnableZuulProxy добавляет возможность работы в качестве прокси и маршрутизации запросов к внутренним API.
  • @EnableEurekaClient делает это приложение клиентом Eureka, чтобы оно могло взаимодействовать с реестром служб и получать необходимую информацию (сетевые расположения серверных API-интерфейсов) для маршрутизации запросов.

Настройка свойств приложения (application.properties)

Особые примечания:

  • Следующее свойство заставит прокси Zuul работать на порту с номером 8762.
server.port = 8762
  • URL-адрес сервера Eureka должен быть настроен следующим образом.
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka
  • Для получения / получения сведений о зарегистрированной службе Zuul Proxy не требуется регистрироваться на сервере Eureka. Следующее свойство следует использовать для предотвращения регистрации Zuul на сервере Eureka.
eureka.client.registerWithEureka=false
  • Несмотря на то, что Zuul не должен регистрироваться в Eureka, ему все равно необходимо получать записи реестра для маршрутизации запросов к серверным API. Следующее свойство позволяет Zuul взаимодействовать с сервером Eureka и получать записи реестра.
eureka.client.fetchRegistry=true
  • Ниже приведены два наиболее важных свойства, которые позволяют Zuul выполнять маршрутизацию запросов.
zuul.routes.courseservice.path=/course-service/**
zuul.routes.courseservice.serviceId=course-service

Если запрос поступает по URL-адресу /course-service/*, этот запрос будет направлен в службу, зарегистрированную с идентификатором службы course-service. При перенаправлении Zuul по умолчанию удаляет /course-service/ часть URL-адреса. Например, если запрос приходит на /course-service/courses URL, Zuul перенаправит этот запрос на микросервис курса с URL /courses.

Тестирование приложения

Чтобы протестировать прокси Zuul, вы должны сначала запустить указанные ниже службы, которые мы реализовали в указанных выше разделах.

  • Микросервис курса
  • Эврика сервер
  • Zuul прокси

Впоследствии вы можете использовать клиент REST, например Postman, для вызова конечной точки /course-service/, настроенной в Zuul, и проверки успешности возврата списка курсов.

Расширение функциональности Zuul с помощью фильтров

Как уже говорилось в предыдущем разделе, фильтры - это способ расширения функциональности Zuul по умолчанию и добавления ваших собственных функций. Существует четыре типа фильтров, а именно pre, post, route и error. Ниже приведены примеры реализации каждого из этих типов фильтров.

  • Предварительный фильтр
  • Фильтр сообщений
  • Фильтр маршрута
  • Фильтр ошибок

Классы фильтров реализуют четыре метода:

  • filterType(): возвращает String, обозначающий тип фильтра - в данном случае pre. (Для фильтра маршрутизации это было бы route.)
  • filterOrder(): Указывает порядок, в котором этот фильтр должен запускаться относительно других фильтров.
  • shouldFilter(): содержит логику, определяющую, когда запускать этот фильтр (этот конкретный фильтр запускается всегда).
  • run(): Содержит функции фильтра.

Заключение

Цель этой статьи - познакомить вас с маршрутизацией запросов через Zuul от Netflix. Мы использовали оболочку Spring Cloud для Zuul и создали образец приложения для наблюдения за процессом маршрутизации запросов. Надеюсь, эта статья была вам полезна.

Спасибо за прочтение!