Всем привет, я не Кай Чен~
В последнее время многие друзья написали мне в личных сообщениях, чтобы призвать больше«Продвинутое весеннее облако», Чен Моу также резюмировал это.Последняя причина заключается в том, что Чен Моу пытался объяснить важные моменты знания компонента в статье ранее, что привело к длинной статье, долгому времени написания и утомительному чтению.
Поэтому Чен решил объединить каждый компонент в последующих статьях.расколотьОн разделен на разные части, и каждая статья вводит один или два пункта знаний, которые могут не только повысить эффективность письма, но и позволить друзьям учиться быстрее и своевременно.
Это«Продвинутое весеннее облако»первоеСемьСтатьи, предыдущие статьи следующие:
- Пятьдесят пять картинок расскажут вам, насколько силен Nacos, перевозчик душ микросервисов?
- Смертельная серия из 9 вопросов от openFeign, кто выдержит это?
- Али спросил в интервью: Как выбрать центры конфигурации Nacos, Apollo и Config? Эти 10 измерений говорят вам!
- Али провалился на собеседовании: как выбрать 5 типов центров регистрации микросервисов? Эти размеры говорят вам!
- Артефакт Али, ограничивающий ток, Sentinel убивает серию 17 вопросов?
- Сравнивая 7 схем распределенных транзакций, я все же предпочитаю Seata с открытым исходным кодом от Alibaba, которая действительно ароматна! (принцип + реальный бой)
Эта статья представляет важную роль в микросервисах: шлюз, Что касается того, как выбрать шлюз, поскольку Alibaba еще не запустила шлюз, конечно, выбран облачный шлюз Spring, в конце концов, это сын.
Каталог статей выглядит следующим образом:
Зачем нужен шлюз?
В традиционной монолитной архитектуре клиенту открыта только одна служба для вызова, но в микросервисной архитектуре система разделена на несколько микросервисов, так как же вызывать эти микросервисы в качестве клиента? При отсутствии шлюза адрес вызова каждой микрослужбы может быть записан только локально.
Архитектуры микросервисов без шлюза часто имеют следующие проблемы:
- Клиент несколько раз запрашивает разные микросервисы, что увеличивает сложность написания клиентского кода или конфигурации.
- Аутентификация сложна, и каждая служба требует независимой аутентификации.
- Существуют междоменные запросы, которые относительно сложно обрабатывать в определенных сценариях.
Основная функция шлюза?
Шлюз — это портал всех микросервисов, маршрутизация и переадресация — это только самые основные функции, кроме того, есть и другие функции, такие как:Сертификация,Аутентификация,предохранитель,Ограничение,мониторинг журналатак далее.........
Вышеупомянутые сценарии применения будут подробно представлены в последующих статьях, а не в центре внимания сегодняшнего дня.
Почему стоит выбрать Spring Cloud Gateway?
В версии 1.x использовался шлюз Zuul, но в версии 2.x обновление zuul продолжало отскакивать, и Spring Cloud, наконец, разработала шлюз для замены Zuul, то есть Spring Cloud Gateway.
Я должен выбрать своему сыну Spring Cloud Gateway.Многие его идеи позаимствованы у zuul.Так называемый синий лучше синего.Функционал и производительность однозначно лучше zuul.Иначе зачем Spring Cloud его публиковать?
Важная причина:
Spring Cloud Gateway построен на Spring Boot 2.x, Spring WebFlux и [Project Reactor.
Не беспокойтесь об интеграции, совместимости и производительности Spring Boot.
Какие термины необходимо знать для Spring Cloud Gateway?
- маршрут: основной строительный блок для шлюзов. Он состоит из идентификатора, целевого URI, набора утверждений и набора фильтров. Если совокупный результат утверждения верен, маршрут сопоставляется.
- Утверждать (Предикат): Обратитесь к новой функции Predicate в Java8, которая позволяет разработчикам сопоставлять любой контент в HTTP-запросах, например заголовки или параметры.
- фильтр: содержимое запросов и ответов можно изменить до или после возврата запроса.
Как построить шлюз?
Зачем вывешивать эту картинку?
Его необходимо адаптировать согласно версии на картинке выше, иначе будут непредвиденные баги.
новыйcloud-gateway9023
, добавьте следующие зависимости:
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
Уведомление: нужно удалить
spring-boot-starter-web
Зависимость, иначе запустить ошибку
Что ж, проект построен, собственно такая зависимость добавлена, подробная настройка описана ниже.
Что такое Предсказать?
Предикат исходит из интерфейса java8. Predicate принимает один входной параметр и возвращает логический результат. Этот интерфейс содержит несколько методов по умолчанию для объединения предикатов в другую сложную логику (например: И, ИЛИ, НЕ).
Его можно использовать для проверки параметров запроса интерфейса и для определения того, изменились ли новые и старые данные и нуждаются ли они в обновлении.
Spring Cloud Gateway имеет множество встроенных прогнозов, и исходный код этих прогнозов находится вorg.springframework.cloud.gateway.handler.predicate
В упаковке вы можете прочитать это, если вам интересно. Вот некоторые встроенные утверждения:
Вышеупомянутые 11 утверждений Чен не будет вводить здесь, как их настроить, и официальные документы очень ясны.
Официальная документация:docs.spring.IO/spring - уродливо...
Давайте возьмем последнее утверждение веса в качестве примера, чтобы представить, как его настроить. Конфигурация выглядит следующим образом:
spring:
cloud:
gateway:
## 路由
routes:
## id只要唯一即可,名称任意
- id: gateway-provider_1
uri: http://localhost:9024
## 配置断言
predicates:
## Path Route Predicate Factory断言,满足/gateway/provider/**这个请求路径的都会被路由到http://localhost:9024这个uri中
- Path=/gateway/provider/**
## Weight Route Predicate Factory,同一分组按照权重进行分配流量,这里分配了80%
## 第一个group1是分组名,第二个参数是权重
- Weight=group1, 8
## id必须唯一
- id: gateway-provider_2
## 路由转发的uri
uri: http://localhost:9025
## 配置断言
predicates:
## Path Route Predicate Factory断言,满足/gateway/provider/**这个请求路径的都会被路由到http://localhost:9024这个uri中
- Path=/gateway/provider/**
## Weight Route Predicate Factory,同一分组按照权重进行分配流量,这里分配了20%
## 第一个group1是分组名,第二个参数是权重
- Weight=group1, 2
routes
Ниже приведена настроенная политика маршрутизации, компоненты которой следующие:
-
id
: уникальный идентификатор маршрута, имя произвольное -
uri
: uri, пересылаемый по маршруту -
predicates
: Конфигурация утверждений, вы можете настроить несколько
Имена утверждений в Spring Cloud Gateway стандартизированы в формате:xxxRoutePredicateFactory
.
Например, взвешенное утверждение:WeightRoutePredicateFactory
, затем сразу взять предыдущееWeight
.
Если переадресация маршрута по умолчанию направлена на два, она перенаправляется в порядке конфигурации, а пути настраиваются выше:Path=/gateway/provider/**
, если вес не настроен, его необходимо перенаправить наhttp://localhost:9024
.
Но так как конфигурация настроена с весами и одинаковой группировкой, трафик распределяется по соотношению весов.
Что такое фильтр?
Концепция фильтра очень знакома, и она была затронута в Spring mvc Функция и жизненный цикл фильтра Gateway аналогичны.
Жизненный цикл шлюза:
- PRE: этот фильтр вызывается перед маршрутизацией запроса. Мы можем использовать этот фильтр для реализации аутентификации, выбора запрошенных микросервисов в кластере, регистрации отладочной информации и многого другого.
- POST: этот фильтр выполняется после маршрутизации в микросервис. Такие фильтры можно использовать для добавления стандартных заголовков HTTP к ответам, сбора статистики и метрик, отправки ответов от микросервисов клиентам и многого другого.
Фильтр шлюза можно разделить на два типа по сфере действия:
- GatewayFilter: Применить к одному маршруту или сгруппированному маршруту (необходимо настроить в файле конфигурации).
- GlobalFilter: применяется ко всем маршрутам (настройка не требуется, действует глобально)
GatewayFilter (локальный фильтр)
В Spring Cloud Gateway встроено множество локальных фильтров, как показано ниже:
Локальный фильтр должен быть настроен на указанном маршруте, чтобы он вступил в силу, и по умолчанию он не действует.
кAddResponseHeaderGatewayFilterFactory
Возьмите этот фильтр в качестве примера, добавьте заголовок к исходному ответу и настройте его следующим образом:
spring:
cloud:
gateway:
## 路由
routes:
## id只要唯一即可,名称任意
- id: gateway-provider_1
uri: http://localhost:9024
## 配置断言
predicates:
## Path Route Predicate Factory断言,满足/gateway/provider/**这个请求路径的都会被路由到http://localhost:9024这个uri中
- Path=/gateway/provider/**
## 配置过滤器(局部)
filters:
- AddResponseHeader=X-Response-Foo, Bar
Запрос браузера обнаружил, что в заголовке ответа уже естьX-Response-Foo=Bar
Эта пара ключ-значение, как показано ниже:
Уведомление: имя фильтра должно содержать только префикс, а имя фильтра должно бытьxxxGatewayFilterFactory
(в том числе на заказ).
Дополнительные настройки фильтров см. в официальной документации:docs.spring.IO/spring - уродливо...
Несмотря на то, что встроенные фильтры могут решить многие сценарии, неизбежно, что некоторые специальные требования должны быть настроены для фильтра.Давайте представим, как настроить локальные фильтры.
Сцены: Моделирование процесса проверки авторизации, если заголовок запроса или параметры запроса содержатtoken
затем отпустите, иначе сразу перехватите и верните401, код показан ниже:
/**
* 名称必须是xxxGatewayFilterFactory形式
* todo:模拟授权的验证,具体逻辑根据业务完善
*/
@Component
@Slf4j
public class AuthorizeGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthorizeGatewayFilterFactory.Config> {
private static final String AUTHORIZE_TOKEN = "token";
//构造函数,加载Config
public AuthorizeGatewayFilterFactory() {
//固定写法
super(AuthorizeGatewayFilterFactory.Config.class);
log.info("Loaded GatewayFilterFactory [Authorize]");
}
//读取配置文件中的参数 赋值到 配置类中
@Override
public List<String> shortcutFieldOrder() {
//Config.enabled
return Arrays.asList("enabled");
}
@Override
public GatewayFilter apply(AuthorizeGatewayFilterFactory.Config config) {
return (exchange, chain) -> {
//判断是否开启授权验证
if (!config.isEnabled()) {
return chain.filter(exchange);
}
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
//从请求头中获取token
String token = headers.getFirst(AUTHORIZE_TOKEN);
if (token == null) {
//从请求头参数中获取token
token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
}
ServerHttpResponse response = exchange.getResponse();
//如果token为空,直接返回401,未授权
if (StringUtils.isEmpty(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//处理完成,直接拦截,不再进行下去
return response.setComplete();
}
/**
* todo chain.filter(exchange) 之前的都是过滤器的前置处理
*
* chain.filter().then(
* 过滤器的后置处理...........
* )
*/
//授权正常,继续下一个过滤器链的调用
return chain.filter(exchange);
};
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Config {
// 控制是否开启认证
private boolean enabled;
}
}
Локальный фильтр должен быть настроен в маршруте, чтобы он вступил в силу.Конфигурация выглядит следующим образом:
spring:
cloud:
gateway:
## 路由
routes:
## id只要唯一即可,名称任意
- id: gateway-provider_1
uri: http://localhost:9024
## 配置断言
predicates:
## Path Route Predicate Factory断言,满足/gateway/provider/**这个请求路径的都会被路由到http://localhost:9024这个uri中
- Path=/gateway/provider/**
## 配置过滤器(局部)
filters:
- AddResponseHeader=X-Response-Foo, Bar
## AuthorizeGatewayFilterFactory自定义过滤器配置,值为true需要验证授权,false不需要
- Authorize=true
Прямой доступ в этот момент:http://localhost:9023/gateway/provider/port без токена возвращает результат, как показано ниже:
Параметры запроса приносят токен:http://localhost:9023/gateway/provider/port?token=abcdcdecd-ddcdeicd12, возвращен успешно, как показано ниже:
вышеупомянутыйAuthorizeGatewayFilterFactory
Это включает только предварительную обработку фильтра, а постобработка находится вchain.filter().then()
серединаthen()
Завершено в методе, вы можете увидеть подробности в исходном коде проектаTimeGatewayFilterFactory
, код больше не публикуется, как показано ниже:
Глобальный фильтр
Глобальные фильтры применяются ко всем маршрутам без настройки разработчика. Spring Cloud Gateway также имеет некоторые встроенные глобальные фильтры, как показано ниже:
GlobalFilter
Функция на самом деле такая же, какGatewayFilter
то же самое, простоGlobalFilter
Область действия — все конфигурации маршрутизации, не привязанные к указанной конфигурации маршрутизации. несколькоGlobalFilter
в состоянии пройти@Order
илиgetOrder()
способ указать каждыйGlobalFilter
Порядок выполнения , чем меньше значение ордера, темGlobalFilter
Чем выше приоритет выполнения.
Обратите внимание, что поскольку существует два типа фильтров, предварительный и пост-фильтр, если порядковое значение фильтра предварительного типа меньше, то оно должно быть на верхнем уровне цепочки предварительного фильтра, а если порядковое значение фильтра пост-типа меньше, то он должен быть в верхней части цепочки предварительных фильтров.Нижний слой цепочки предварительных фильтров. Схематическая диаграмма выглядит следующим образом:
Конечно, помимо встроенных глобальных фильтров, в реальной работе необходимы и пользовательские фильтры, давайте познакомимся с тем, как их настроить.
Сценарий: смоделируйте функцию журнала доступа Nginx и запишите соответствующую информацию о каждом запросе. код показывает, как показано ниже:
/**
* 实现GlobalFilter
*/
@Slf4j
@Component
@Order(value = Integer.MIN_VALUE)
public class AccessLogGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//filter的前置处理
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().pathWithinApplication().value();
InetSocketAddress remoteAddress = request.getRemoteAddress();
return chain
//继续调用filter
.filter(exchange)
//filter的后置处理
.then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
HttpStatus statusCode = response.getStatusCode();
log.info("请求路径:{},远程IP地址:{},响应码:{}", path, remoteAddress, statusCode);
}));
}
}
Что ж, глобальный фильтр не нужно настраивать на маршруте, его можно внедрить в контейнер IOC, чтобы он действовал глобально.
В этот момент выдается запрос, и консоль выводит следующую информацию:
请求路径:/gateway/provider/port,远程IP地址:/0:0:0:0:0:0:0:1:64114,响应码:200 OK
Как интегрировать реестр?
В приведенной выше демонстрации нет встроенного реестра, и каждая конфигурация маршрутизации указывает фиксированный URI службы, как показано ниже:
Какой вред в этом?
- После изменения IP-адреса службы необходимо изменить uri в конфигурации маршрутизации.
- Балансировка нагрузки не может быть достигнута в сервисном кластере
На этом этапе требуется интегрированный реестр, чтобы шлюз мог автоматически получать URI (балансировку нагрузки) из реестра.
Центр регистрации здесь, конечно же, выбирает Nacos, а незнакомые друзья, пожалуйста, прочитайте первую статью Чена «Spring Cloud Advanced»:Пятьдесят пять картинок расскажут вам, насколько силен Nacos, перевозчик душ микросервисов?
Зависимости Nacos добавляются в файл pom следующим образом:
<!--nacos注册中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
Включите функцию реестра в классе запуска, как показано ниже:
Указываем адрес реестра nacos в конфигурационном файле:
spring:
application:
## 指定服务名称,在nacos中的名字
name: cloud-gateway
cloud:
nacos:
discovery:
# nacos的服务地址,nacos-server中IP地址:端口号
server-addr: 127.0.0.1:8848
Единственная разница в конфигурации маршрутизации — это маршрутизация.uri
,Формат:lb://service-name
, это фиксированный способ записи:
-
lb
: фиксированный формат, который относится к получению микросервисов по имени от nacos и следованию стратегии балансировки нагрузки. -
service-name
: Имя службы реестра nacos, это не в форме IP-адреса.
Полная демонстрация конфигурации для интеграции реестра Nacos выглядит следующим образом:
spring:
application:
## 指定服务名称,在nacos中的名字
name: cloud-gateway
cloud:
nacos:
discovery:
# nacos的服务地址,nacos-server中IP地址:端口号
server-addr: 127.0.0.1:8848
gateway:
## 路由
routes:
## id只要唯一即可,名称任意
- id: gateway-provider_1
## 使用了lb形式,从注册中心负载均衡的获取uri
uri: lb://gateway-provider
## 配置断言
predicates:
## Path Route Predicate Factory断言,满足/gateway/provider/**这个请求路径的都会被路由到http://localhost:9024这个uri中
- Path=/gateway/provider/**
## 配置过滤器(局部)
filters:
- AddResponseHeader=X-Response-Foo, Bar
почему указаноlb
Вы можете включить балансировку нагрузки.Как упоминалось ранее, глобальный фильтрLoadBalancerClientFilter
Он отвечает за маршрутизацию и балансировку нагрузки.Вы можете увидеть следующий исходный код:
Как реализовать динамическую маршрутизацию?
Все приведенные выше примеры записывают ряд конфигураций шлюза в файл конфигурации проекта.После изменения маршрутизации проект необходимо перепроецировать, что очень дорого в обслуживании.
Фактически, мы можем настроить шлюз, хранящийся в центре распределения, чтобы унифицированное управление центральной конфигурацией после изменения маршрута требовалось только изменить центр конфигурации, который сможет достичьОдна модификация, более эффективнаяцель.
Конечно, мы должны использовать Nacos в качестве центра конфигурации.Добавьте зависимости следующим образом:
<!-- nacos配置中心的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
существуетbootstrap.yml
Файл указывает Nacos как некоторые связанные конфигурации центра конфигурации:
spring:
application:
## 指定服务名称,在nacos中的名字
name: cloud-gateway
cloud:
nacos:
## todo 此处作为演示,仅仅配置了后缀,其他分组,命名空间根据需要自己配置
config:
server-addr: 127.0.0.1:8848
## 指定文件后缀未yaml
file-extension: yaml
Создано в общедоступном пространстве имен в nacosdataId
заcloud-gateway.yaml
конфигурации (без указания среды), содержание конфигурации следующее:
На этом настройка завершена. Что касается эффекта, попробуйте своими руками. . . . . . .
Как настроить глобальную обработку исключений?
В предыдущем тесте мы можем увидеть явление: когда маршрутизируемый микросервис находится в автономном режиме или отключен, Spring Cloud Gateway напрямую возвращает страницу с ошибкой, как показано ниже:
Очевидно, что такая информация об исключении неудобна, и возвращаемая информация об исключении должна быть настроена в архитектуре разделения внешнего и внутреннего интерфейса.
Используются традиционные сервисы Spring Boot@ControllerAdvice
чтобы обернуть глобальную обработку исключений, но запрос не пришел, потому что служба была отключена.
Поэтому в шлюзе необходимо настроить уровень глобальной обработки исключений, чтобы взаимодействие с клиентом было более дружественным.
Spring Cloud Gateway предоставляет множество глобальных методов обработки, сегодня Чен представляет только один из них, и его реализация довольно элегантна.
Создать класс напрямуюGlobalErrorExceptionHandler
,выполнитьErrorWebExceptionHandler
, который переписываетhandle
метод, код выглядит следующим образом:
/**
* 用于网关的全局异常处理
* @Order(-1):优先级一定要比ResponseStatusExceptionHandler低
*/
@Slf4j
@Order(-1)
@Component
@RequiredArgsConstructor
public class GlobalErrorExceptionHandler implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@SuppressWarnings({"rawtypes", "unchecked", "NullableProblems"})
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
// JOSN格式返回
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getStatus());
}
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
//todo 返回响应结果,根据业务需求,自己定制
CommonResponse resultMsg = new CommonResponse("500",ex.getMessage(),null);
return bufferFactory.wrap(objectMapper.writeValueAsBytes(resultMsg));
}
catch (JsonProcessingException e) {
log.error("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}
Хорошо, глобальная обработка исключений была настроена. После тестирования данные JSON возвращаются нормально, как показано ниже:
Стиль JSON настраивается в соответствии с потребностями схемы.
Суммировать
Сегодня здесь представлен Spring Cloud Gateway, в основном представляющий следующие точки знаний:
- Зачем нужен шлюз? Основные функции шлюза
- Как создать шлюз микросервисов с нуля
- Концепция предсказания
- Концепция фильтров, фильтры, встроенные в Spring Cloud Gateway, и способы их настройки
- Как интегрировать реестр Nacos и добиться балансировки нагрузки
- Как интегрировать Nacos для достижения динамической маршрутизации, достижения одной модификации и более эффективных функций
- глобальная обработка исключений
Как вы думаете, Spring Cloud Gateway закончился? Невозможно, в дальнейшем будет более подробное и актуальное введение, в следующей статье будет представлен ......
Исходный код проекта загружен на Github, а официальный аккаунт [Code Ape Technology Column] отвечает ключевым словам:9528Получать!
Последнее слово (пожалуйста, обратите внимание, не проституируйте меня по пустякам)
Каждая оригинальная статья Чен Моу тщательно выводится, особенно статьи в колонке "Spring Cloud Advanced". Слишком много знаний. Если вы хотите рассказать об этом подробно, вы должны потратить много времени на подготовку, начиная с Знание указывает на демонстрацию исходного кода.
Если эта статья была для вас полезной или просветительской, пожалуйста, помогитеподобно,заглянуть,Вперед,собирать, Ваша поддержка - самая большая мотивация для меня продолжать!