Безостановочная реализация динамической маршрутизации

Безостановочная реализация динамической маршрутизации

Фон проекта и причины выбора Spring Cloud Gateway

Данный проект полностью использует архитектуру микросервисов (MSA), чтобы быстро реагировать на быстро меняющиеся требования бизнеса и обеспечить индивидуальную масштабируемость без вмешательства между сервисами. Особенно он реализуется в сотрудничестве с несколькими разработчиками, обладающими экспертными знаниями в своей области, в рамках структуры 'многопоставщиков (Multi-vendor)', что позволяет осуществлять параллельную разработку. Однако с точки зрения архитектуры возникает задача управления, связанная с фрагментацией границ сервиса и резким увеличением точек управления, что приводит к управленческой сложности.

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

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

  • Максимизация производительности через централизацию функций безопасности: Реализация аутентификации (Authentication) и авторизации (Authorization) для каждого сервиса по отдельности является дублированием разработки и приводит к уязвимостям безопасности во всей системе. Это требовало интегрированной реализации на уровне Gateway для синхронизации уровня безопасности всей службы.

  • Стабильная внешняя интеграция в инфраструктуре закрытой сети:Данная система была построена в закрытой сети для обеспечения конфиденциальности, но была необходима точка внешнего взаимодействия. Ключевым моментом стало 'Безопасный шлюз (Secure Gate)', который посредничал в связи, не подвергая прямому раскрытию внутренние службы и скрывая внутреннюю топологию.

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

Чтобы комплексно удовлетворить эти требования, мы выбрали Spring Cloud Gateway (SCG). SCG не только полностью интегрируется с экосистемой JVM, но и Асимметричная неблокирующая архитектура на основе Nettyобеспечивает стабильную обработку большого количества одновременных соединений даже с минимальными ресурсами.

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

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

На начальном этапе разработки мы выбрали самый стандартный способ, предлагаемый Spring Cloud Gateway (далее SCG), а именно статическую маршрутизацию (Static Routing). Это способ, при котором маршрутизационные правила явно указываются в файле настройки application.yml, как показано ниже.

spring:
   cloud:
       gateway:
           server:
               webflux:
                   routes:
                      - id: shared
                         predicates:
                             - Path=${server.servlet.context-path}/shared/**
                         uri: <http://foo-bar.com/api/shared>
                         filters:
                             - RewritePath=${server.servlet.context-path}/, /api/
                             - AddRequestHeader=xgate-id, gate1234
                         metadata:
                             response-timeout: 1800000 #30분
                             connect-timeout: 3000 #30초

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

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

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

public class GatewayRoute {
    private String id;
    private String routeName;
    private String pathPattern;
    private String rewriteFrom;
    private String rewriteTo;
    private String uri;
    private long responseTimeout;
    private long connectTimeout;
    private boolean active;
    private List<ApiPermission> apiPermissions;
    private boolean useQueue;
    private int maxQueueSize;
    private int maxConcurrent;
    private List<KeyValue> requestHeaders;
}

public class ApiPermission {
    private String path;
    private List<String> permittedServices;
}

Эта модель домена была расширена для комплексного управления всей информацией маршрутизации, которая ранее обрабатывалась в статическом режиме, а также метаданными для [интегрированной аутентификации и авторизации] и [управления очередью], которые будут описаны позже.

Также ключевой технический аспект применения динамически сохраненных данных к реальной маршрутизации заключается в использовании внутренних механизмов SCG. SCG автоматически обнаруживает реализации интерфейса RouteDefinitionLocator в момент инициализации и загружает список маршрутов.

Мы создали пользовательский класс GatewayRouteConfig и реализовали логику, которая запрашивает информацию о маршруте из базы данных и преобразует её в формат RouteDefinition, понятный SCG. Это позволило операторам настраивать значения через интерфейс администратора, применяя правила маршрутизации в реальном времени без перезапуска шлюза.

@Configuration
public class GatewayRouteConfig {

        @Bean
        public RouteDefinitionLocator dbRouteDefinitionLocator() {
        return () -> policySeek.findAllGatewayRoutes()
        .filter(GatewayRoute::isActive)
        .doOnNext(route -> {
                queueManager.updateRouteConfig(
                   route.getId();
                   route.getMaxQueueSize(),
                   route.getMaxConcurrent()
                );
           }
        .map(this::convertToRouteDefinition);
        }

        private RouteDefinition convertToRouteDefinition(GatewayRoute route){
        RouteDefinition definition = new RouteDefinition();
        definition.setId(route.getId());
        definition.setUri(route.getUri());
        String fullPath = normalizedContexPath() + route.getPathPattern();
        definition.setPredicates(List.of(new PredicateDefinition("Path=": + fullPath)));

        List<FilterDefinition> filters = new ArrayList<>();
        if (route.isUseQueue()){
                filters.add(new FilterDefinition("Queue"));
        }
        filters.add(new FilterDefinition("RewritePath=" + normalizeContextPath() + route.getRewriteFrom() + "," + route.getRewriteTo()));
        route.getRequestHeaders().forEach(keyValue -> filters.add((new FilterDefinition("AddRequestHeader=" + keyValue.getKey() + "," + keyValue.getValue())));
        definition.setFilters(filters);
     
        definition.getMetadata().put("response-timeout", route.getResponseTimeout());
        definition.getMetadata().put("connect-timeout", route.getConnectTimeout());
        }
}

В заключение

Теперь, используя Spring Cloud Gateway, мы отошли от существующего статического конфигурационного метода YAML и перешли на основанный на базе данных Безостановочная динамическая маршрутизация архитектурыМы рассмотрели процесс создания. Этот процесс модернизации, который повысил настройку инфраструктуры до уровня бизнес-данных, принес следующие явные преимущества на практике.

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

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

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

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

Hustle Paul

Site footer