Интеграция с внешними системами конфигурации:
Обработка событий обновления маршрутов:
#Java #middle #Spring_Cloud_Gateway
@Component
public class ExternalConfigRouteDefinitionRepository
implements RouteDefinitionRepository {
private final ConfigClient configClient;
private final AtomicReference<List<RouteDefinition>> cachedRoutes =
new AtomicReference<>(Collections.emptyList());
public ExternalConfigRouteDefinitionRepository(
ConfigClient configClient) {
this.configClient = configClient;
configClient.watchRoutes()
.doOnNext(this::updateRoutes)
.subscribe();
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(cachedRoutes.get());
}
private void updateRoutes(List<RouteConfig> routeConfigs) {
List<RouteDefinition> routeDefinitions = routeConfigs.stream()
.map(this::convertToRouteDefinition)
.collect(Collectors.toList());
cachedRoutes.set(routeDefinitions);
// Генерация события обновления маршрутов
SpringApplication.publishEvent(
new RefreshRoutesEvent(this)
);
}
private RouteDefinition convertToRouteDefinition(
RouteConfig config) {
RouteDefinition definition = new RouteDefinition();
definition.setId(config.getId());
definition.setUri(URI.create(config.getUri()));
// Конвертация предикатов
config.getPredicates().forEach((name, args) -> {
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName(name);
predicate.setArgs(args);
definition.getPredicates().add(predicate);
});
// Конвертация фильтров
config.getFilters().forEach((name, args) -> {
FilterDefinition filter = new FilterDefinition();
filter.setName(name);
filter.setArgs(args);
definition.getFilters().add(filter);
});
definition.setMetadata(config.getMetadata());
return definition;
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
// Делегирование сохранения во внешнюю систему
return route.flatMap(routeDef ->
configClient.saveRoute(convertToRouteConfig(routeDef))
).then();
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap(id ->
configClient.deleteRoute(id)
).then();
}
}
Обработка событий обновления маршрутов:
@Component
public class RouteUpdateListener {
private final RouteRefreshListener routeRefreshListener;
@EventListener
public void handleRefreshRoutesEvent(RefreshRoutesEvent event) {
// Логирование обновления маршрутов
logger.info("Routes refreshed by {}", event.getSource());
// Метрики
meterRegistry.counter("gateway.routes.refresh").increment();
// Уведомление подписчиков
routeRefreshListener.notifyRefresh();
}
@EventListener
public void handleApplicationEvent(ApplicationEvent event) {
if (event instanceof InstanceRegisteredEvent) {
// Обработка регистрации нового инстанса
updateRoutesForNewInstance(
((InstanceRegisteredEvent) event).getInstance()
);
}
}
}
#Java #middle #Spring_Cloud_Gateway
👍2
Predicates (условия маршрутизации)
Predicates в Spring Cloud Gateway — это функции, которые принимают ServerWebExchange и возвращают boolean, определяя, попадает ли входящий запрос под конкретный Route. Predicates — фундамент маршрутизации: маршрут считается подходящим только если все его предикаты возвращают true (логическое AND между перечисленными предикатами одного маршрута). Понимание механизмов работы предикатов критично для корректного и производительного построения маршрутов.
Ключевые принципы — механика и порядок
Маршрут выбран, если все его предикаты истинны.
В конфигурации YAML или Java DSL, когда у Route указано несколько предикатов, они объединяются логически через AND.
Порядок маршрутов важен.
Gateway перебирает доступные маршруты в порядке, определённом компонентом, который предоставляет Flux<Route> (обычно RouteDefinitionLocator → RouteDefinitionRouteLocator). Конкретный порядок определяется полем order (если задано) или порядком получения/создания маршрутов. Первый подходящий маршрут (в смысле совпадения предикатов) берётся в обработку. Следовательно, дешёвые «фильтрующие» предикаты (например, Host, Method) следует располагать раньше в конфигурации маршрутов по логике — не путать с порядком предикатов в одном маршруте: в одном маршруте порядок предикатов обычно не меняет семантику, но влияет на порядок выполнения (см. рекомендации по эффективности ниже).
Оценка предикатов — последовательная и краткая:
Для одного маршрута предикаты обычно вычисляются последовательно и, при первом false, дальнейшая проверка предикатов для этого маршрута останавливается (short-circuit). Тем не менее, поскольку маршрутов может быть много, Gateway продолжит проверять следующие маршруты до нахождения первого подходящего.
Рекомендации по производительности:
Помещайте дешёвые и часто отсекающие проверки (например, Host, Method, Path) до дорогих вычислений (например, проверки содержимого тела, обращения в внешние сервисы или сложных регулярных выражений).
Избегайте предикатов, которые делают блокирующие операции — predicated должны быть чисто вычислительными и быстрыми. При необходимости используйте Java DSL и инкапсулируйте асинхронную работу в фильтрах, а не в предикатах.
Если нужна сложная проверка, лучше вынести её в отдельный, оптимизированный RoutePredicateFactory (см. ниже).
Композиция логики (AND/OR):
AND — поведение по умолчанию: несколько предикатов в одном Route объединяются через AND.
OR — декларативно не представлен как оператор на уровне одной записи YAML; для выражения OR чаще используют либо несколько маршрутов с разными наборами предикатов, либо пишут кастомный предикат, который внутри реализует логическое OR. В Java DSL также проще объявить несколько маршрутов или написать композиционный предикат в коде.
Встроенные предикаты — семантика, YAML-примеры, Java DSL-примеры
Ниже даны наиболее часто используемые предикаты, их поведение и примеры конфигурации.
1) Path
Проверяет соответствие URL-пути. Поддерживает шаблоны в стиле Ant (/users/**, /api/*/items) и также поддерживает регулярные группы при использовании Rewrite и фильтров.
YAML:
Java DSL:
Замечание: Path — обычно самый дешёвый и самый часто используемый предикат; ставьте его в начале списка логических проверок маршрутов.
2) Host
Проверяет заголовок Host (например, api.example.com). Поддерживает подстановки (*.example.com).
YAML:
Java DSL:
Заметка: Host — особенно полезен при мульти-тенантной конфигурации.
#Java #middle #Spring_Cloud_Gateway
Predicates в Spring Cloud Gateway — это функции, которые принимают ServerWebExchange и возвращают boolean, определяя, попадает ли входящий запрос под конкретный Route. Predicates — фундамент маршрутизации: маршрут считается подходящим только если все его предикаты возвращают true (логическое AND между перечисленными предикатами одного маршрута). Понимание механизмов работы предикатов критично для корректного и производительного построения маршрутов.
Ключевые принципы — механика и порядок
Маршрут выбран, если все его предикаты истинны.
В конфигурации YAML или Java DSL, когда у Route указано несколько предикатов, они объединяются логически через AND.
Порядок маршрутов важен.
Gateway перебирает доступные маршруты в порядке, определённом компонентом, который предоставляет Flux<Route> (обычно RouteDefinitionLocator → RouteDefinitionRouteLocator). Конкретный порядок определяется полем order (если задано) или порядком получения/создания маршрутов. Первый подходящий маршрут (в смысле совпадения предикатов) берётся в обработку. Следовательно, дешёвые «фильтрующие» предикаты (например, Host, Method) следует располагать раньше в конфигурации маршрутов по логике — не путать с порядком предикатов в одном маршруте: в одном маршруте порядок предикатов обычно не меняет семантику, но влияет на порядок выполнения (см. рекомендации по эффективности ниже).
Оценка предикатов — последовательная и краткая:
Для одного маршрута предикаты обычно вычисляются последовательно и, при первом false, дальнейшая проверка предикатов для этого маршрута останавливается (short-circuit). Тем не менее, поскольку маршрутов может быть много, Gateway продолжит проверять следующие маршруты до нахождения первого подходящего.
Рекомендации по производительности:
Помещайте дешёвые и часто отсекающие проверки (например, Host, Method, Path) до дорогих вычислений (например, проверки содержимого тела, обращения в внешние сервисы или сложных регулярных выражений).
Избегайте предикатов, которые делают блокирующие операции — predicated должны быть чисто вычислительными и быстрыми. При необходимости используйте Java DSL и инкапсулируйте асинхронную работу в фильтрах, а не в предикатах.
Если нужна сложная проверка, лучше вынести её в отдельный, оптимизированный RoutePredicateFactory (см. ниже).
Композиция логики (AND/OR):
AND — поведение по умолчанию: несколько предикатов в одном Route объединяются через AND.
OR — декларативно не представлен как оператор на уровне одной записи YAML; для выражения OR чаще используют либо несколько маршрутов с разными наборами предикатов, либо пишут кастомный предикат, который внутри реализует логическое OR. В Java DSL также проще объявить несколько маршрутов или написать композиционный предикат в коде.
Встроенные предикаты — семантика, YAML-примеры, Java DSL-примеры
Ниже даны наиболее часто используемые предикаты, их поведение и примеры конфигурации.
1) Path
Проверяет соответствие URL-пути. Поддерживает шаблоны в стиле Ant (/users/**, /api/*/items) и также поддерживает регулярные группы при использовании Rewrite и фильтров.
YAML:
predicates:
- Path=/api/users/**
Java DSL:
.route("users", r -> r.path("/api/users/**")
.uri("lb://user-service"))Замечание: Path — обычно самый дешёвый и самый часто используемый предикат; ставьте его в начале списка логических проверок маршрутов.
2) Host
Проверяет заголовок Host (например, api.example.com). Поддерживает подстановки (*.example.com).
YAML:
predicates:
- Host=api.example.com, *.internal.example.org
Java DSL:
.route("host-route", r -> r.host("api.example.com", "*.internal.example.org")
.uri("http://localhost:8080"))Заметка: Host — особенно полезен при мульти-тенантной конфигурации.
#Java #middle #Spring_Cloud_Gateway
👍1
3) Method
Сравнивает HTTP-метод (GET/POST/PUT/...).
YAML:
Java DSL:
4) Header
Проверяет наличие и (при задании) значение заголовка. Поддерживает регулярные выражения на значение.
YAML:
Java DSL:
Если значение не указано — проверяется только наличие заголовка.
5) Query
Проверяет наличие параметра query и, при наличии второго аргумента, — значение (регулярное выражение).
YAML:
Java DSL:
6) RemoteAddr
Проверяет IP-адрес клиента — поддерживает одиночные адреса и CIDR. Важно: в случае проксирования через балансировщики/Gateway заранее убедитесь, какие IP попадают в RemoteAddr (реальный клиент или IP reverse-proxy). Иногда требуется проверка X-Forwarded-For.
YAML:
Java DSL:
7) Weight / Load Balancer predicates (логика распределения) — концепция и применение
Weight — предикат, встречающийся в экосистеме Spring Cloud Gateway в контексте динамических маршрутов и Discovery клиента. Его цель — поддержка взвешенной маршрутизации: когда один и тот же serviceId представлен несколькими маршрутами с разными весами (weight), Gateway на этапе выбора маршрута учитывает веса (для канареечных релизов, A/B, geo-aware routing и т.п.).
Практическая схема использования:
Discovery-driven маршрутизация (DiscoveryClientRouteDefinitionLocator) может генерировать маршруты для каждого экземпляра/группы с метаданными веса.
Weight-предикат в составе маршрута проверяет — подходит ли текущий запрос под данный «весовой» маршрут (обычно реализуется через случайное/round-robin решение, зависящее от weight).
Примечание по реализации и совместимости: синтаксис и встроенные реализации могут различаться между версиями Spring Cloud.
Если требуется взвешенная маршрутизация в production, часто применяют:
Spring Cloud LoadBalancer для распределения трафика по инстансам;
Weight-подход в комбинировании с DiscoveryClientRouteDefinitionLocator или кастомным RouteDefinitionRepository.
Если нужен пример конфигурации — лучше реализовать weight механизмы через Discovery + metadata на стороне сервисов или через кастомный RoutePredicateFactory (пример ниже покажет, как писать свой предикат).
Сложные комбинации — AND / OR / NOT
AND: стандартная комбинация — несколько предикатов в одном Route → все должны быть true. Шаблон конфигурирования одинаков как в YAML, так и в DSL.
OR: декларативного OR в одном Route нет.
Для выражения OR есть два варианта:
Несколько маршрутов: создайте два (или больше) маршрута, каждый с различным предикатом; порядок важен, если маршруты пересекаются.
Кастомный предикат (composite): напишите RoutePredicateFactory, который внутри реализует логику a || b, и используйте его как обычный предикат. Это предпочтительно, если хотите избежать дублирования конфигурации и централизовать логику.
NOT / Negation: также не представлен в YAML как отдельный оператор; реализуется кастомным предикатом или путём композиции (например, создание предиката NotHeader).
#Java #middle #Spring_Cloud_Gateway
Сравнивает HTTP-метод (GET/POST/PUT/...).
YAML:
predicates:
- Method=GET,POST
Java DSL:
.route("read-write", r -> r.method(HttpMethod.GET, HttpMethod.POST)
.uri("lb://some-service"))4) Header
Проверяет наличие и (при задании) значение заголовка. Поддерживает регулярные выражения на значение.
YAML:
predicates:
- Header=X-Client, ^mobile-.*
Java DSL:
.route("header-route", r -> r.header("X-Client", "^mobile-.*")
.uri("http://mobile-backend"))Если значение не указано — проверяется только наличие заголовка.
5) Query
Проверяет наличие параметра query и, при наличии второго аргумента, — значение (регулярное выражение).
YAML:
predicates:
- Query=version, ^v[0-9]+$
Java DSL:
.route("versioned", r -> r.query("version", "^v[0-9]+$")
.uri("lb://versioned-service"))6) RemoteAddr
Проверяет IP-адрес клиента — поддерживает одиночные адреса и CIDR. Важно: в случае проксирования через балансировщики/Gateway заранее убедитесь, какие IP попадают в RemoteAddr (реальный клиент или IP reverse-proxy). Иногда требуется проверка X-Forwarded-For.
YAML:
predicates:
- RemoteAddr=192.168.1.0/24, 10.0.0.10
Java DSL:
.route("internal-only", r -> r.remoteAddr("192.168.0.0/16", "10.0.0.0/8")
.uri("lb://internal-service"))7) Weight / Load Balancer predicates (логика распределения) — концепция и применение
Weight — предикат, встречающийся в экосистеме Spring Cloud Gateway в контексте динамических маршрутов и Discovery клиента. Его цель — поддержка взвешенной маршрутизации: когда один и тот же serviceId представлен несколькими маршрутами с разными весами (weight), Gateway на этапе выбора маршрута учитывает веса (для канареечных релизов, A/B, geo-aware routing и т.п.).
Практическая схема использования:
Discovery-driven маршрутизация (DiscoveryClientRouteDefinitionLocator) может генерировать маршруты для каждого экземпляра/группы с метаданными веса.
Weight-предикат в составе маршрута проверяет — подходит ли текущий запрос под данный «весовой» маршрут (обычно реализуется через случайное/round-robin решение, зависящее от weight).
Примечание по реализации и совместимости: синтаксис и встроенные реализации могут различаться между версиями Spring Cloud.
Если требуется взвешенная маршрутизация в production, часто применяют:
Spring Cloud LoadBalancer для распределения трафика по инстансам;
Weight-подход в комбинировании с DiscoveryClientRouteDefinitionLocator или кастомным RouteDefinitionRepository.
Если нужен пример конфигурации — лучше реализовать weight механизмы через Discovery + metadata на стороне сервисов или через кастомный RoutePredicateFactory (пример ниже покажет, как писать свой предикат).
Сложные комбинации — AND / OR / NOT
AND: стандартная комбинация — несколько предикатов в одном Route → все должны быть true. Шаблон конфигурирования одинаков как в YAML, так и в DSL.
OR: декларативного OR в одном Route нет.
Для выражения OR есть два варианта:
Несколько маршрутов: создайте два (или больше) маршрута, каждый с различным предикатом; порядок важен, если маршруты пересекаются.
Кастомный предикат (composite): напишите RoutePredicateFactory, который внутри реализует логику a || b, и используйте его как обычный предикат. Это предпочтительно, если хотите избежать дублирования конфигурации и централизовать логику.
NOT / Negation: также не представлен в YAML как отдельный оператор; реализуется кастомным предикатом или путём композиции (например, создание предиката NotHeader).
#Java #middle #Spring_Cloud_Gateway
👍1
Написание собственного Predicate — RoutePredicateFactory
Когда встроенных предикатов недостаточно по логике или производительности, пишут собственные RoutePredicateFactory. Ниже — полный пример: реализуем предикат, который проверяет наличие параметра X-Feature и сопоставляет его по списку допустимых значений, при этом конфигурируемый через YAML.
1) Структура — класс предиката
Ключевые моменты:
Наследуемся от AbstractRoutePredicateFactory<Config>. Это даёт поддержку автоконфигурируемой десериализации конфигурации из YAML в Config.
apply(Config) возвращает Predicate<ServerWebExchange>, который будет выполняться для каждого запроса.
Config — POJO с геттерами/сеттерами: Spring автоматически маппит значения из YAML (через Binder).
2) Регистрация (component или bean)
Если не использовать @Component, можно зарегистрировать как bean через конфигурацию.
3) Использование в YAML
В AbstractRoutePredicateFactory стандартная разбивка аргументов (если конфиг простой) позволит подставить список значений. Для более сложной настройки (ключ:значение) необходимо переопределить shortcutFieldOrder() или использовать вложенный Config с именованными полями.
4) Использование в Java DSL
Если хотите inline-предикат в коде (без фабрики), Java DSL позволяет:
Однако такой inline-предикат теряет преимущества конфигурируемости через YAML и переиспользуемости.
#Java #middle #Spring_Cloud_Gateway
Когда встроенных предикатов недостаточно по логике или производительности, пишут собственные RoutePredicateFactory. Ниже — полный пример: реализуем предикат, который проверяет наличие параметра X-Feature и сопоставляет его по списку допустимых значений, при этом конфигурируемый через YAML.
1) Структура — класс предиката
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.web.server.ServerWebExchange;
import java.util.function.Predicate;
import java.util.List;
public class XFeatureRoutePredicateFactory extends AbstractRoutePredicateFactory<XFeatureRoutePredicateFactory.Config> {
public XFeatureRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
List<String> allowed = config.getAllowedValues();
boolean ignoreCase = config.isIgnoreCase();
return exchange -> {
List<String> headerValues = exchange.getRequest().getHeaders().get("X-Feature");
if (headerValues == null || headerValues.isEmpty()) {
return false;
}
for (String hv : headerValues) {
for (String allowedVal : allowed) {
if (ignoreCase) {
if (hv.equalsIgnoreCase(allowedVal)) return true;
} else {
if (hv.equals(allowedVal)) return true;
}
}
}
return false;
};
}
public static class Config {
private List<String> allowedValues;
private boolean ignoreCase = true;
public List<String> getAllowedValues() { return allowedValues; }
public void setAllowedValues(List<String> allowedValues) { this.allowedValues = allowedValues; }
public boolean isIgnoreCase() { return ignoreCase; }
public void setIgnoreCase(boolean ignoreCase) { this.ignoreCase = ignoreCase; }
}
}
Ключевые моменты:
Наследуемся от AbstractRoutePredicateFactory<Config>. Это даёт поддержку автоконфигурируемой десериализации конфигурации из YAML в Config.
apply(Config) возвращает Predicate<ServerWebExchange>, который будет выполняться для каждого запроса.
Config — POJO с геттерами/сеттерами: Spring автоматически маппит значения из YAML (через Binder).
2) Регистрация (component или bean)
import org.springframework.stereotype.Component;
@Component
public class XFeatureRoutePredicateFactory extends AbstractRoutePredicateFactory<XFeatureRoutePredicateFactory.Config> {
// ... код как выше
}
Если не использовать @Component, можно зарегистрировать как bean через конфигурацию.
3) Использование в YAML
spring:
cloud:
gateway:
routes:
- id: feature-route
uri: http://feature-service
predicates:
- XFeature=allowed1,allowed2
В AbstractRoutePredicateFactory стандартная разбивка аргументов (если конфиг простой) позволит подставить список значений. Для более сложной настройки (ключ:значение) необходимо переопределить shortcutFieldOrder() или использовать вложенный Config с именованными полями.
4) Использование в Java DSL
Если хотите inline-предикат в коде (без фабрики), Java DSL позволяет:
.route("xfeature-inline", r -> r.p(exchange -> {
List<String> values = exchange.getRequest().getHeaders().get("X-Feature");
return values != null && values.stream().anyMatch(v -> v.equalsIgnoreCase("allowed1"));
}).uri("http://feature-service"))Однако такой inline-предикат теряет преимущества конфигурируемости через YAML и переиспользуемости.
#Java #middle #Spring_Cloud_Gateway
👍1
Работа с конфигурационными объектами и AbstractRoutePredicateFactory — тонкости
Shortcut configuration vs. full Config binding
Если RoutePredicateFactory использует короткий синтаксис (например: MyPred=val1,val2), то AbstractRoutePredicateFactory поддерживает маппинг по shortcutFieldOrder() — список полей Config, которые будут заполнены по порядку.
Для явной структуры лучше использовать именованные свойства в YAML и обычный биндинг в Config.
Валидация конфигурации
Валидируйте конфиг в конструкторе предиката или в apply() — например, проверить, что список не пуст. Ошибки конфигурации лучше бросать на старте приложения, а не при первом запросе.
Сериализация/десериализация сложных типов
Для сложных типов (например, Duration, Pattern, InetAddress[]) используйте соответствующие конвертеры или храните строковые представления и парсите в Config.
Потокобезопасность
Predicate возвращаемый apply() должен быть потокобезопасным и не содержать mutable state, зависящего от запроса; храните precomputed структуры (например Pattern), а не парсьте каждый раз.
Логирование и мониторинг
Для сложных предикатов логируйте причины отказа (на низком уровне) либо метрики отказов, чтобы упростить отладку некорректного маршрутизации.
Примеры: несколько реальных сценариев и лучшие практики
Пример 1 — комбинация Host + Path + Method (YAML)
Пример 2 — OR-логика через два маршрута (YAML)
Пример 3 — кастомный предикат с конфигом в YAML
#Java #middle #Spring_Cloud_Gateway
Shortcut configuration vs. full Config binding
Если RoutePredicateFactory использует короткий синтаксис (например: MyPred=val1,val2), то AbstractRoutePredicateFactory поддерживает маппинг по shortcutFieldOrder() — список полей Config, которые будут заполнены по порядку.
Для явной структуры лучше использовать именованные свойства в YAML и обычный биндинг в Config.
Валидация конфигурации
Валидируйте конфиг в конструкторе предиката или в apply() — например, проверить, что список не пуст. Ошибки конфигурации лучше бросать на старте приложения, а не при первом запросе.
Сериализация/десериализация сложных типов
Для сложных типов (например, Duration, Pattern, InetAddress[]) используйте соответствующие конвертеры или храните строковые представления и парсите в Config.
Потокобезопасность
Predicate возвращаемый apply() должен быть потокобезопасным и не содержать mutable state, зависящего от запроса; храните precomputed структуры (например Pattern), а не парсьте каждый раз.
Логирование и мониторинг
Для сложных предикатов логируйте причины отказа (на низком уровне) либо метрики отказов, чтобы упростить отладку некорректного маршрутизации.
Примеры: несколько реальных сценариев и лучшие практики
Пример 1 — комбинация Host + Path + Method (YAML)
routes:
- id: users-route
uri: lb://user-service
predicates:
- Host=api.example.com
- Path=/users/**
- Method=GET
Пояснение: типичный маршрут, дешёвые проверки (Host/Method/Path) — быстрый short-circuit.
Пример 2 — OR-логика через два маршрута (YAML)
routes:
- id: mobile-route
uri: lb://mobile-backend
predicates:
- Header=X-Client, ^mobile-.*
- Path=/api/**
- id: fallback-route
uri: lb://web-backend
predicates:
- Path=/api/**
Пояснение: первый маршрут отведёт мобильный трафик на mobile-backend; второй поймает остальные запросы по тому же Path — это простой способ построить OR-поведение без кастомных предикатов.
Пример 3 — кастомный предикат с конфигом в YAML
predicates:
- XFeature=allowedA,allowedB
(см. реализацию XFeatureRoutePredicateFactory выше).
#Java #middle #Spring_Cloud_Gateway
👍1