предисловие
В микросервисной архитектуре каждая служба представляет собой компонент, который можно разрабатывать и запускать независимо, а полная микросервисная архитектура состоит из ряда независимо работающих микросервисов. Клиенту может потребоваться вызвать несколько служб для выполнения функции, поэтому это также будет иметь некоторые последствия, такие как:
- Клиенту необходимо инициировать несколько запросов, что увеличивает стоимость сетевого взаимодействия и сложность обработки клиентов.
- Аутентификация службы будет распределена в каждой микрослужбе, и клиенту потребуется повторная аутентификация для каждого вызова службы.
- Во внутренней микросервисной архитектуре разные службы могут использовать разные протоколы, например HTTP и RPC. Если клиенту необходимо вызвать несколько служб, ему необходимо адаптироваться к различным протоколам.
Роль шлюза API
Для решения этой проблемы можно использовать шлюзы, добавив шлюз API между клиентом и сервером.
Шлюз не только выполняет переадресацию запросов и интеграцию услуг, но и с унифицированным входом шлюза также может выполнять следующие функции:
- для всех запросовЕдиная аутентификация,Ограничение,предохранитель,журнал.
- Преобразование соглашения.
- Единая обработка кодов ошибок.
- Запрос пересылается, и внутренняя и внешняя сеть могут быть изолированы на основе шлюза.
выпуск оттенков серого
Скорость итерации продукта у многих компаний очень высока.В режиме высокочастотной итерации часто возникают некоторые риски, такие как:
- Проблемы совместимости с недавно выпущенным кодом.
- После того, как новая функция будет выпущена, смогут ли пользователи принять ее, если нет, это приведет к потере пользователей.
- В коде есть скрытые ошибки, которые вызывают онлайн-сбои.
Чтобы избежать этих проблем, версия со значительными функциональными изменениями обычно выпускается в оттенках серого для обеспечения плавного перехода.
Так называемый выпуск в градациях серого означает, что функции, которые должны быть выпущены, сначала открываются для небольшого числа пользователей, а область влияния контролируется в очень узком диапазоне.Например, A / B Test - это способ выпуска в градациях серого, то есть некоторые пользователи продолжают использовать функцию А, а другая небольшая группа пользователей использует новую функцию Б. Проведя опрос удовлетворенности пользователей, использующих функцию B, и оценив показатели производительности и стабильности вновь выпущенного кода, выпуск новой версии будет постепенно увеличиваться до тех пор, пока не будет выполнен полный объем или версия не будет откатана.
Шлюз — это вход для всех запросов, поэтому на уровне шлюза некоторый трафик может быть направлен через правила оттенков серого, чтобы реализовать публикацию в оттенках серого. Как показано на рисунке ниже, после того, как шлюз перехватит запрос, он направит запрос в соответствии с правилами разгрузки, настроенными механизмом разгрузки.
Введение в Spring Cloud Gateway
Spring Cloud Gateway — это технология шлюза API, разработанная официальной командой Spring. Его цель — заменить Zuul и предоставить простой и эффективный шлюз API для микросервисов.
Spring Cloud Gateway — это шлюз, разработанный с использованием таких технологий, как Spring Boot 2.0, Spring WebFlux и Project Reactor, Он не только обеспечивает унифицированный способ маршрутизации запросов, но и предоставляет самые основные функции шлюза на основе цепочки фильтрации, такие как : безопасность, мониторинг, закапывание и ограничение тока и т.д.
преимущество
- Высокая производительность, в 1,6 раза выше, чем у Zuul
- Мощный, со встроенным множеством практичных функций, таких как переадресация, мониторинг, ограничение тока и т. д.
- Элегантный дизайн и простое расширение
недостаток
- Использование Netty и WebFlux, а не традиционной модели программирования сервлетов, требует определенных затрат на обучение.
- Он не может работать в контейнере сервлетов и не может быть встроен в пакет WAR, то есть его нельзя развернуть в контейнерах сервлетов, таких как Tomcat и Jetty, а можно выполнить только как пакет jar.
- Spring Boot 1.x не поддерживается, требуется 2.0 и выше
Анализ принципа Spring Cloud Gateway
Процесс запроса Spring Cloud Gateway показан на рисунке:
Есть несколько очень важных понятий:
- Маршрут: это основной компонент шлюза, состоящий из идентификатора, целевого URL-адреса, коллекции предикатов и коллекции фильтров.
- Predicate: это функциональный интерфейс, представленный в Java 8, который обеспечивает возможность утверждения. Он может соответствовать чему угодно в HTTP-запросе. Если совокупный результат оценки Predicate истинен, это означает, что запрос будет переадресован текущим маршрутизатором.
- Фильтр: обеспечивает предварительную и постфильтрацию запросов.
Битва шлюза Spring Cloud Gateway
Сначала подготовьте два приложения Spring Boot.
- spring-cloud-gateway-service имитирует микросервис.
- spring-cloud-gateway-sample — отдельная служба шлюза.
spring-cloud-gateway-service
Создайте приложение на основе каркаса Spring Boot и добавьте зависимость spring-boot-starter-web. СоздаватьHelloController
Класс публикует интерфейс и запускает приложение.
@RestController
public class HelloController {
@GetMapping("/say")
public String sayHello() {
return "[spring-cloud-gateway-service]:say Hello";
}
}
spring-cloud-gateway-sample
Создайте приложение Spring Boot и добавьте зависимости Spring Cloud Gateway.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
существуетapplication.yml
Добавьте конфигурацию маршрутизации шлюза в файл.
spring:
cloud:
gateway:
routes:
- id: config_route
predicates:
- Path=/gateway/** # 路径匹配
filters:
- StripPrefix=1 # 跳过前缀
uri: http://localhost:8080/say # spring-cloud-gateway-service 的访问地址
server:
port: 8088
Значения полей в приведенной выше конфигурации объясняются следующим образом:
- id: Идентификатор пользовательского маршрута, пусть он будет уникальным.
- uri: адрес целевого сервера, поддерживает общий URI и имя службы регистрации приложений lb://, последнее указывает на то, что адрес службы кластера получен из центра регистрации.
- predicates: условие маршрутизации, решить, следует ли выполнять маршрутизацию запроса в соответствии с результатом сопоставления.
-
filters: правила фильтрации, включая
pre
а такжеpost
фильтр. вStripPrefix=1
, указывающее, что шлюз удаляет часть префикса в пути URL-адреса в соответствии со значением конфигурации (здесь удалите префикс, то есть удалите шлюз из переадресованного целевого URL-адреса).
Запустите приложение, и вы можете получить следующую информацию в консоли.Вы можете видеть, что оно не зависит от Tomcat, а используетNettyWebServer
чтобы запустить прослушиватель службы.
2020-07-26 16:32:01.375 INFO 13544 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8088
Введите в браузереhttp://localhost:8088/gateway/say
,результат:
Введите в браузереhttp://localhost:8080/say
,результат:
Мы обнаружили, что результаты были одинаковыми.
Route Predicate Factories
Predicate
даJava 8
Предоставляет функциональный интерфейс, который принимает параметр и возвращает логическое значение, которое можно использовать для условной фильтрации и проверки параметра запроса.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Spring Cloud Gateway
Многие из них предоставляются по умолчаниюRoute Predicate Factory
,ЭтиPredicate
будет соответствоватьHTTP
различные свойства запроса и несколькоPredicate
в состоянии пройтиand
Логика объединения.
НижеHTTP
Запрашиваемый атрибут соответствуетPredicate
:
After Route Predicate
Запросы после указанного времени будут соответствовать этому маршруту.
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://localhost:8080/say
predicates:
- After=2020-07-26T16:30:00+08:00[Asia/Shanghai]
Before Route Predicate
Запросы до указанного времени будут соответствовать этому маршруту.
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://localhost:8080/say
predicates:
- Before=2020-07-26T16:30:00+08:00[Asia/Shanghai]
Between Route Predicate
Запросы в течение указанного интервала времени будут соответствовать этому маршруту.
spring:
cloud:
gateway:
routes:
- id: between_route
uri: http://localhost:8080/say
predicates:
- Between=2020-07-26T16:30:00+08:00[Asia/Shanghai], 2020-07-27T16:30:00+08:00[Asia/Shanghai]
Cookie Route Predicate
Запросы с указанным файлом cookie будут соответствовать этому маршруту.
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://localhost:8080/say
predicates:
- Cookie=username,macro
Используйте инструмент curl для отправки файла cookie какusername=macro
запросы могут соответствовать этому маршруту.
curl http://localhost:8088/gateway/say --cookie "username=macro"
Header Route Predicate
Запросы с указанными заголовками будут соответствовать этому маршруту.
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://localhost:8080/say
predicates:
- Header=X-Request-Id, \d+
Используйте инструмент curl для отправки заголовков запроса в видеX-Request-Id:123
запросы могут соответствовать этому маршруту.
curl http://localhost:8088/gateway/say -H "X-Request-Id:123"
Host Route Predicate
Запросы с указанным Хостом будут соответствовать этому маршруту.
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://localhost:8080/say
predicates:
- Host=**.macro.com
Используйте инструмент curl для отправки заголовков запроса в видеHost:www.macro.com
запросы могут соответствовать этому маршруту.
curl http://localhost:8088/gateway/say -H "Host:www.macro.com"
Method Route Predicate
Отправка запроса на указанный метод будет соответствовать этому маршруту.
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://localhost:8080/say
predicates:
- Method=GET
Отправить с помощью инструмента завитокGET
Запросы могут соответствовать этому маршруту.
curl http://localhost:8088/gateway/say
Отправить с помощью инструмента завитокPOST
Запрос не может соответствовать маршруту.
curl -X POST http://localhost:8088/gateway/say
Path Route Predicate
Отправка запроса по указанному пути будет соответствовать этому маршруту.
spring:
cloud:
gateway:
routes:
- id: path_route
uri: http://localhost:8080/say
predicates:
- Path=/gateway/**
Отправить с помощью инструмента завиток/gateway/
Запросы пути могут соответствовать этому маршруту.
curl http://localhost:8088/gateway/say
Отправить с помощью инструмента завиток/abc/
Запрос маршрута не может соответствовать маршруту.
curl http://localhost:8088/abc/say
Query Route Predicate
Запросы с указанными параметрами запроса могут соответствовать этому маршруту.
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://localhost:8080/say/getByUsername
predicates:
- Query=username
Отправьте ленту с помощью инструмента завитокusername=macro
Запросы с параметрами запроса могут соответствовать этому маршруту.
curl http://localhost:8088/gateway/say/getByUsername?username=macro
Отправка запроса с параметрами запроса или без них с помощью инструмента curl не может соответствовать этому маршруту.
curl http://localhost:8088/gateway/say/getByUsername
RemoteAddr Route Predicate
Запросы, исходящие с указанного удаленного адреса, могут соответствовать этому маршруту.
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://localhost:8080/say/
predicates:
- RemoteAddr=192.168.1.1/24
Использование инструмента curl для инициирования запроса от 192.168.1.1 может соответствовать этому маршруту.
curl http://localhost:8088/gateway/say/
Weight Route Predicate
Используйте вес для маршрутизации соответствующего запроса, следующее означает, что 80% запросов будут направлены наlocalhost:8080
, 20% будут направлены наlocalhost:8081
.
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: http://localhost:8080
predicates:
- Weight=group1, 8
- id: weight_low
uri: http://localhost:8081
predicates:
- Weight=group1, 2
Gateway Filter Factories
Фильтр разделен наPre
тип фильтра иPost
тип фильтра.
-
Pre
Фильтр типа выполняется до того, как запрос будет перенаправлен на внутренний сервер, вPer
Тип цепочки фильтров может сделатьАутентификация,Ограничениеи так далее. -
Post
Фильтры типов выполняются после завершения запроса, но до того, как результат будет возвращен клиенту.
В Spring Cloud Gateway встроено много фильтров, есть две реализации фильтра, а именноGatewayFilter
а такжеGlobalFilter
.GlobalFilter
применяется ко всем маршрутизаторам иGatewayFilter
Применяется только к одному маршрутизатору или группе маршрутизаторов.
AddRequestParameter GatewayFilter
Фильтр, добавляющий параметры к запросу.
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: http://localhost:8080
filters:
- AddRequestParameter=username, macro
predicates:
- Method=GET
Приведенная выше конфигурация добавит к запросу GETusername=macro
Параметры запроса проверяются инструментом curl с помощью следующей команды.
curl http://localhost:8088/gateway/say/getByUsername
Эквивалентно инициированию запроса:
curl http://localhost:8088/gateway/say/getByUsername?username=macro
StripPrefix GatewayFilter
Фильтр, который удаляет указанное количество префиксов пути.
spring:
cloud:
gateway:
routes:
- id: strip_prefix_route
uri: http://localhost:8080
predicates:
- Path=/gateway/**
filters:
- StripPrefix=1
Вышеупомянутая конфигурация начнется с/gateway/
Удалите один бит из пути запроса в начале и используйте следующую команду для проверки с помощью инструмента curl.
curl http://localhost:8088/gateway/say/
Эквивалентно инициированию запроса:
curl http://localhost:8080/say/
PrefixPath GatewayFilter
В отличие от фильтра StripPrefix, фильтр, добавляющий операции к исходному пути.
spring:
cloud:
gateway:
routes:
- id: prefix_path_route
uri: http://localhost:8080
predicates:
- Method=GET
filters:
- PrefixPath=/user
Приведенная выше конфигурация добавит ко всем запросам GET/user
Префикс пути, проверенный с помощью следующей команды с помощью инструмента curl.
curl http://localhost:8088/gateway/say
Эквивалентно инициированию запроса:
curl http://localhost:8080/user/gateway/say/
Hystrix GatewayFilter
Фильтры Hystrix позволяют добавлять функции прерывателя цепи к маршрутам шлюза, изолировать ваши службы от каскадных сбоев и обеспечивать обработку деградации службы.
-
Чтобы включить функцию автоматического выключателя, нам нужно
pom.xml
Добавьте связанные зависимости Hystrix в:<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
Затем добавьте соответствующий класс обработки перехода на более раннюю версию службы:
@RestController public class FallbackController { @GetMapping("/fallback") public Object fallback() { Map<String,Object> result = new HashMap<>(); result.put("data",null); result.put("message","Get request fallback!"); result.put("code",500); return result; } }
-
существует
application-filter.yml
Добавьте соответствующую конфигурацию в , и при возникновении ошибки маршрутизации она будет перенаправлена на контроллер для обработки перехода на более раннюю версию:spring: cloud: gateway: routes: - id: hystrix_route predicates: - Method=GET filters: - name: Hystrix args: name: fallbackcmd fallbackUri: forward:/fallback uri: http://localhost:8080/say
-
Закройте spring-cloud-gateway-service и вызовите этот адрес для тестирования:
http://localhost:8088/say
обнаружено, что была возвращена информация об обработке ухудшения обслуживания.
RequestRateLimiter GatewayFilter
Фильтр RequestRateLimiter можно использовать дляОграничение,использоватьRateLimiter
Реализовано, чтобы определить, следует ли разрешить выполнение текущего запроса, по умолчанию используется состояние HTTP 429 — слишком много запросов, если запрос слишком велик.
-
существует
pom.xml
Добавьте связанные зависимости к:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency>
-
Добавьте текущую политику ограничения, ограничьте ток в соответствии с IP-адресом доступа и определите текущий класс политики ограничения.
IpKeyResolver
, и вводится в пружинный контейнер.@Component public class IpKeyResolver implements KeyResolver { @Override public Mono<String> resolve(ServerWebExchange exchange) { return Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); } }
-
Мы используем Redis для ограничения тока, поэтому нам нужно добавить конфигурацию Redis и RequestRateLimiter, здесь все GET-запросы ограничены IP.
server: port: 8088 spring: redis: host: 123.57.45.66 port: 6379 cloud: gateway: routes: - id: requestratelimiter_route uri: http://localhost:8080/ filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1 # 每秒允许处理的请求数量 redis-rate-limiter.burstCapacity: 2 # 每秒最大处理的请求数量 key-resolver: "#{@ipKeyResolver}" # 限流策略,对应策略的Bean predicates: - Method=GET
-
Запросить адрес несколько раз:
http://localhost:8088/say
, будет возвращена ошибка с кодом состояния 429;
Retry GatewayFilter
Фильтр для повторной попытки запроса маршрутизации может определить, следует ли повторить попытку, в соответствии с кодом состояния HTTP, возвращенным запросом маршрутизации.
-
Измените файл конфигурации:
server: port: 8088 spring: cloud: gateway: routes: - id: retry_route uri: http://localhost:8080 predicates: - Method=GET filters: - name: Retry args: retries: 1 # 需要进行重试的次数 statuses: BAD_GATEWAY # 返回哪个状态码需要进行重试,返回状态码为5XX进行重试 backoff: firstBackoff: 10ms maxBackoff: 50ms factor: 2 basedOnPreviousValue: false
-
Исправлять
spring-cloud-gateway-service
серединаHelloController
из/say
метод, который вручную генерирует исключение.@GetMapping("/say") public String sayHello() { if (true) { throw new RuntimeException("/say, 异常"); } return "[spring-cloud-gateway-service]:say Hello"; }
-
Когда вызов вернет 500, он повторит попытку, посетите тестовый адрес:
http://localhost:8088/say
-
Его можно найти
spring-cloud-gateway-service
Консоль сообщает о 2 ошибках, указывающих на повторную попытку.2020-07-27 21:50:31.910 ERROR 11200 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: /say, 异常] with root cause java.lang.RuntimeException: /say, 异常 at com.autu.example.springcloudgatewayservice.HelloController.sayHello(HelloController.java:16) ~[classes/:na]
Пользовательская фабрика предикатов
Например, есть служба, которая ограничивает доступ пользователей только в период с 06:00 до 13:00.Predicate Fatory
Выполнить это требование невозможно, поэтому в настоящее время нам нужно настроить тот, который может выполнить это требование.Predicate Fatory
.
-
Создайте
TimeBetweenRoutePredicateFactory
наследование классовAbstractRoutePredicateFactory
-
существует
TimeBetweenRoutePredicateFactory
Написать статический внутренний классConfig
@Component public class TimeBetweenRoutePredicateFactory extends AbstractRoutePredicateFactory<TimeBetweenRoutePredicateFactory.Config> { private static final String START_KEY = "start"; private static final String END_KEY = "end"; public TimeBetweenRoutePredicateFactory() { super(TimeBetweenRoutePredicateFactory.Config.class); } @Override public Predicate<ServerWebExchange> apply(Config config) { LocalTime start = config.getStart(); LocalTime end = config.getEnd(); return serverWebExchange -> { LocalTime now = LocalTime.now(); return now.isAfter(start) && now.isBefore(end); }; } /** * 设置配置类与配置文件的关系 */ @Override public List<String> shortcutFieldOrder() { /* * 例如我们的配置项是:TimeBetween=上午6:00, 下午1:00 * 那么按照顺序,start对应的是上午6:00;end对应的是下午1:00 */ return Arrays.asList(START_KEY, END_KEY); } static class Config{ private LocalTime start; private LocalTime end; LocalTime getStart() { return start; } void setStart(LocalTime start) { this.start = start; } LocalTime getEnd() { return end; } void setEnd(LocalTime end) { this.end = end; } } }
-
конфигурационный файл
spring: cloud: gateway: routes: - id: customizer_predicate uri: http://localhost:8080/say/ predicates: - TimeBetween=上午6:00,下午1:00
-
настроить
Predicate Fatory
класс, судя поSpring Cloud Stream
, имя класса должно быть "Predicate
Название фабрики (в этом примере:TimeBetween
)" +RoutePredicateFactory
-
Формат времени не настраивается произвольно, а является форматом времени по умолчанию для Spring Cloud Gateway.
Пока что обычайPredicate Fatory
, если это не в пределах разрешенного периода времени доступа, доступ сообщит 404, и доступ:http://localhost:8088/say
, результат показан на следующем рисунке:
Фабрика пользовательских фильтров
Требование: записывать журналы доступа
-
настроить
Filter Factory
Добрый@Component public class LogCustomizerGatewayFilterFactory extends AbstractGatewayFilterFactory<LogCustomizerGatewayFilterFactory.Config> { private Logger logger= LoggerFactory.getLogger(LogCustomizerGatewayFilterFactory.class); private static final String NAME_KEY = "name"; public LogCustomizerGatewayFilterFactory() { super(Config.class); } @Override public List<String> shortcutFieldOrder() { return Arrays.asList(NAME_KEY); } @Override public GatewayFilter apply(Config config) { //Filter pre post return ((exchange,chain)->{ logger.info("[pre] Filter Request, name:"+config.getName()); //TODO return chain.filter(exchange).then(Mono.fromRunnable(()->{ //TODO logger.info("[post]: Response Filter"); })); }); } static class Config{ private String name; String getName() { return name; } void setName(String name) { this.name = name; } } }
-
Добавить связанную конфигурацию
spring: cloud: gateway: routes: - id: log_customizer uri: http://localhost:8080/say/ predicates: - Method=GET filters: - LogCustomizer=Hello Log Customizer
-
доступ:
http://localhost:8088/say
,spring-cloud-gateway-sample
Формируется следующий журнал:2020-07-27 22:45:54.606 INFO 11172 --- [ctor-http-nio-3] .a.e.s.LogCustomizerGatewayFilterFactory : [pre] Filter Request, name:Hello Log Customizer 2020-07-27 22:45:54.776 INFO 11172 --- [ctor-http-nio-4] .a.e.s.LogCustomizerGatewayFilterFactory : [post]: Response Filter
Оригинальный адрес:Осень200.com/2020/07/26/…