предисловие
В микросервисной архитектуре каждая служба представляет собой компонент, который можно разрабатывать и запускать независимо, а полная микросервисная архитектура состоит из ряда независимо работающих микросервисов. Клиенту может потребоваться вызвать несколько служб для выполнения функции, поэтому это также будет иметь некоторые последствия, такие как:
- Клиенту необходимо инициировать несколько запросов, что увеличивает стоимость сетевого взаимодействия и сложность обработки клиентов.
- Аутентификация службы будет распределена в каждой микрослужбе, и клиенту потребуется повторная аутентификация для каждого вызова службы.
- Во внутренней микросервисной архитектуре разные службы могут использовать разные протоколы, например 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/…