Это 29-й день моего участия в августовском испытании обновлений. Узнайте подробности события:Испытание августовского обновления
🌈 Введение в колонку
Спасибо за чтение, я надеюсь, что это может быть полезно для вас.Если есть какие-либо недостатки в сообщении в блоге, пожалуйста, оставьте сообщение в области комментариев или добавьте меня в личное представление на главной странице, чтобы пообщаться со мной в частном порядке.Спасибо. за ваш щедрый совет. Я XiaoLin, мальчик, который может писать ошибки и петь рэп. В этой колонке в основном представлено самое распространенное решение для микросервисов, SpringCloudAlibaba, которое будет представлено в компонентах. Адрес столбца: SpringCloudAlibaba.
- Наиболее полное объяснение SpringCloudAlibaba 6️⃣ Sentinel (рекомендуемая коллекция)
- Наиболее полное объяснение SpringCloudAlibaba 5️⃣ Feign (рекомендуемая коллекция)
- Наиболее полное объяснение SpringCloudAlibaba о 4️⃣ ленте (рекомендуемая коллекция)
- Наиболее полное объяснение SpringCloudAlibaba о 3️⃣ Nacos (рекомендуемая коллекция)
- Самое полное объяснение SpringCloudAlibaba во всей сети 2️⃣ (рекомендуемая подборка)
- Самое полное объяснение SpringCloudAlibaba во всей сети 1️⃣ (рекомендуемая подборка)
9. Сервисный шлюз: шлюз
9.1 Введение в шлюз
Всем известно, что в микросервисной архитектуре система будет разбита на множество микросервисов. Так как же вызвать такое количество микросервисов в качестве клиента? Если нет шлюза, мы можем только записать адрес каждого микросервиса на стороне клиента, а потом вызывать его отдельно.
У такой архитектуры много проблем:
- Клиент несколько раз запрашивает разные микросервисы, что увеличивает сложность написания клиентского кода или конфигурации.
- Аутентификация сложна, и каждая служба требует независимой аутентификации.
- Существуют междоменные запросы, которые относительно сложно обрабатывать в определенных сценариях.
Gateway был создан, чтобы решить эти проблемы. Так называемый API-шлюз относится к единому входу в систему, инкапсулирует внутреннюю структуру прикладной программы и предоставляет унифицированные сервисы для клиента, здесь может быть реализована некоторая публичная логика, не связанная с функциями самого бизнеса, например аутентификация, аутентификация, мониторинг, маршрутизация и т. д.
9.2. Часто используемые шлюзы
9.2.1 Ngnix+lua
Использование обратного прокси-сервера nginx и балансировки нагрузки может обеспечить балансировку нагрузки и высокую доступность для серверов API.
Lua — это язык сценариев, на котором можно написать простую логику, nginx поддерживает сценарии lua.
9.2.2. Конг
На основе разработки Nginx + Lua, высокой производительности и стабильности существует несколько доступных подключаемых модулей (ограничение тока, аутентификация и т. д.), которые можно использовать «из коробки».
Его недостатки:
- Поддерживается только протокол Http.
- Вторичное развитие, трудно свободно расширяться.
- Предоставляет API управления, не имеет более простых в использовании методов управления и настройки.
9.2.3, Зуул
Netflix Devices Gateways, богатые функции, используйте Java Development, легко развиваться.
Его недостатки:
- Отсутствие контроля и отсутствие динамической настройки.
- Есть много зависимых компонентов.
- Обработка запросов Http зависит от веб-контейнера, а производительность не так хороша, как у Nginx.
9.2.4, Весенний облачный шлюз
Служба шлюза, разработанная Spring для замены Zuul, стек технологий Alibaba Spring Cloud не предоставляет собственный шлюз, мы можем использовать Spring Cloud Gateway в качестве шлюза.
9.3 Введение в шлюз
Spring Cloud Gateway — это шлюз, разработанный Spring на основе таких технологий, как Spring 5.0, Spring Boot 2.0 и Project Reactor, с целью предоставления простого и эффективного унифицированного метода управления маршрутизацией API для микросервисной архитектуры. Его цель — заменить Netflix Zuul, который не только предоставляет унифицированный метод маршрутизации, но и предоставляет базовые функции шлюза на основе цепочки Filter, такие как: безопасность, мониторинг и ограничение тока.
Его основные функции:
- Делайте перенаправление вперед.
- Вначале все классы должны выполнять операции инициализации.
- Сетевая изоляция.
9.4. Быстрый старт
Требование: Получите доступ к шлюзу API через браузер, а затем перенаправьте запрос в стандартную микрослужбу через шлюз.
9.4.1, базовая версия
Создайте модуль API-шлюза и импортируйте следующие зависимости.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Shop-parent</artifactId>
<groupId>cn.linstudy</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api-gateway</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--gateway网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
написать файл конфигурации
server:
port: 9000 # 指定网关服务的端口
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: product_route # 当前路由的标识, 要求唯一
uri: http://localhost:8081 # 请求要转发到的地址
order: 1 # 路由的优先级,数字越小级别越高
predicates: # 断言(就是路由转发要满足的条件)
- Path=/product-serv/** # 当请求路径满足Path指定的规则时,才进行路由转发
filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 # 转发之前去掉1层路径
контрольная работа
9.4.2, версия обновления
Мы обнаружили, что в обновленной версии есть большая проблема, то есть адрес пути переадресации прописан мертвым в файле конфигурации, и нам нужно получить адрес в центре регистрации.
Добавить зависимость nacos
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Shop-parent</artifactId>
<groupId>cn.linstudy</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api-gateway</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--gateway网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
Добавьте аннотации к основному классу
@SpringBootApplication
@EnableDiscoveryClient
public class GateWayServerApp {
public static void main(String[] args) {
SpringApplication.run(GateWayServerApp.class,args);
}
}
Изменить файл конфигурации
server:
port: 9000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
routes:
- id: product_route # 路由的名字
uri: lb://product-service # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/product-serv/** # 符合这个规定的才进行1转发
filters:
- StripPrefix=1 # 将第一层去掉
Мы также можем настроить несколько правил маршрутизации.
spring:
application:
gateway:
routes:
- id: product_route
uri: lb://product-service
predicates:
- Path=/product-serv/**
filters:
- StripPrefix=1
- id: order_route
uri: lb://order-service
predicates:
- Path=/order-serv/**
filters:
- StripPrefix=1
9.4.3, сокращенная версия
Наш профиль не нужно писать менее сложные функции можно добиться, есть сокращенная версия.
server:
port: 9000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
Мы обнаружили, что если вы обращаетесь к нему в формате адрес шлюза/имя микросервиса/интерфейс, вы можете получить успешный ответ.
9.5 Архитектура ядра шлюза
9.5.1 Основные понятия
Маршрут — один из самых основных компонентов шлюза, представляющий собой конкретный носитель маршрутной информации. В основном он определяет следующую информацию:
- id: идентификатор маршрута, отличный от других маршрутов.
- uri: URI назначения, на который указывает маршрут, то есть микрослужба, на которую в конечном итоге перенаправляется запрос клиента.
- Заказ: используется для сортировки нескольких маршрутов. Чем меньше значение, тем выше сортировка и тем выше соответствующий приоритет.
- предикат: Роль утверждения заключается в выполнении условного суждения.Только когда утверждение возвращает истину, маршрут будет фактически выполнен.
- фильтр: Фильтры используются для изменения информации запроса и ответа.
- предикат: утверждения используются для условного суждения.Только когда утверждения возвращают истину, маршрут будет фактически выполнен.
9.5.2 Принцип выполнения
- Получите запрос пользователя, передайте обработчик запроса обработчику сопоставления и вернитесь в цепочку выполнения.
- Обработчик запроса вызывает веб-обработчик, где обрабатывается наш путь 1. Предположим 1, что наш путь 1:http://localhost:9000/product-serv/get?id=1Согласно сконфигурированным правилам маршрутизации, найдите соответствующую информацию о обслуживании локально: IP-хост, соответствующий услугу продукта, является 192.168.10.130.
- Выбираем ноду исходя из стратегии балансировки нагрузки 1ribbon, затем сращиваем, заменяем Product-Serv в пути на 192.168.10.130:8081, если вы настроили Filter, то он будет ходить по Filter.
- Если у вас нет собственного маршрута, шлюз по умолчанию удалит для вас первый уровень. Порт шлюза от этого
/начать вторым/Начните считать первый уровень.
9.6. Фильтры
Роль фильтра шлюза состоит в том, чтобы делать некоторые трюки с запросом и ответом в процессе доставки запроса.
Жизненный цикл фильтра шлюза:
- PRE: этот фильтр вызывается перед маршрутизацией запроса. Мы можем использовать этот фильтр для реализации аутентификации и выбора в кластере. Запросить микросервис, записать отладочную информацию и т. д.
- POST: этот фильтр выполняется после маршрутизации в микросервис. Этот фильтр можно использовать для добавления стандартного HTTP к ответу. Заголовок, сбор статистики и метрик, отправка ответов от микросервисов клиентам и т.д.
Фильтр шлюза можно разделить на два типа: воротфильтр и GlobalFilter:
- GatewayFilter: применяется к одному маршруту или пакету.
- GlobalFilter: применяется ко всем маршрутам.
9.6.1 Локальный фильтр
Локальные фильтры — это фильтры для одного маршрута. Он делится на встроенные фильтры и настраиваемые фильтры.
9.6.1.1, встроенный фильтр
В Spring Cloud Gateway встроено множество различных типов фильтров маршрутов шлюза.
9.6.1.1.1, локальное содержимое фильтра
| завод фильтров | эффект | параметр |
|---|---|---|
| AddRequestHeader | Добавить заголовок к исходному запросу | Имя и значение заголовка |
| AddRequestParameter | Добавьте параметры запроса в исходный запрос | Имя параметра и значение |
| AddResponseHeader | Добавить исходный ответ заголовка | Имя и значение заголовка |
| DedupeResponseHeader | Обрезать повторяющиеся значения в заголовках ответов | Имена заголовков и стратегии дедупликации, которые необходимо дедуплицировать |
| Hystrix | Автоматический выключатель защиты для маршрутизации введения Hystrix | Имя HystrixCommand |
| FallbackHeaders | Добавьте конкретную информацию об исключении в заголовок запроса fallbackUri. | Название заголовка |
| PrefixPath | Префикс исходного пути запроса | префиксный путь |
| PreserveHostHeader | Добавьте в запрос свойство preserveHostHeader=true, фильтр маршрута проверит это свойство, чтобы решить, следует ли отправлять исходный узел. | никто |
| RequestRateLimiter | Используется для ограничения потока запросов. Алгоритм ограничения потока — это ведро токенов. | ключ Resolver, скоростьLimiter, код статуса, отрицатьEmptyKey, emptyKeyStatus |
| RedirectTo | Перенаправить оригинальный запрос на указанный URL | код состояния http и URL-адрес перенаправления |
| RemoveHopByHopHeadersFilter | Удалить ряд заголовков, указанных организацией IETF для исходного запроса. | Если будет включен по умолчанию, вы можете удалить только какой заголовок удалять только настройкой |
| RemoveRequestHeader | Удалить заголовок исходного запроса | Название заголовка |
| RemoveResponseHeader | Удалить заголовок для исходного ответа | Название заголовка |
| RewritePath | Перепишите исходный путь запроса | Исходное регулярное выражение пути и переписанное регулярное выражение пути |
| RewriteResponseHeader | Переопределить заголовок в исходном ответе | Имя заголовка, регулярное выражение для значения, перезаписанное значение |
| SaveSession | Прежде чем пересылать запрос, включитеWebSession::saveдействовать |
никто |
| secureHeaders | Добавьте серию заголовков ответа безопасности к исходному ответу. | Нет, поддержка изменения значения этих заголовков ответов безопасности |
| SetPath | Изменить исходный путь запроса | Измененный путь |
| SetResponseHeader | Изменить значение заголовка в исходном ответе | Название заголовка, измененное значение |
| SetStatus | Измените исходный код состояния ответа | HTTP-код состояния, может быть номером или строкой |
| StripPrefix | Путь, используемый для усечения исходного запроса | Используйте число для количества путей для усечения |
| Retry | Повторите попытку с другим ответом | повторы, статусы, методы, серии |
| RequestSize | Устанавливает размер максимального пакета запроса, который может быть получен. Если размер пакета запроса превышает установленное значение, вернуть 413 Payload Too Large. | Размер пакета запроса, в байтах, значение по умолчанию 5M |
| ModifyRequestBody | Измените исходное содержимое тела запроса перед пересылкой запроса. | Измененное содержимое тела запроса |
| ModifyResponseBody | Изменить содержимое исходного тела ответа | Модифицированный ответ отклика |
9.6.1.1.2 Использование локальных фильтров
server:
port: 9000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
routes:
- id: product_route # 路由的名字
uri: lb://product-service # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/product-serv/** # 符合这个规定的才进行1转发
filters:
- StripPrefix=1 # 将第一层去掉
- SetStatus=2000 # 这里使用内置的过滤器,修改返回状态
9.6.1.2, локальный настраиваемый фильтр
Часто встроенные фильтры не могут удовлетворить наши потребности, и в это время мы должны настраивать локальные фильтры. Мы предполагаем, что требование состоит в том, чтобы: подсчитывать время, затрачиваемое на вызовы службы заказа.
Напишите класс, который реализует логику
Имя имеет фиксированный формат xxxGatewayFilterFactory.
@Component
public class TimeGatewayFilterFactory extends AbstractGatewayFilterFactory<TimeGatewayFilterFactory.Config> {
private static final String BEGIN_TIME = "beginTime";
//构造函数
public TimeGatewayFilterFactory() {
super(TimeGatewayFilterFactory.Config.class);
}
//读取配置文件中的参数 赋值到 配置类中
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("show");
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (!config.show){
// 如果配置类中的show为false,表示放行
return chain.filter(exchange);
}
exchange.getAttributes().put(BEGIN_TIME, System.currentTimeMillis());
/**
* pre的逻辑
* chain.filter().then(Mono.fromRunable(()->{
* post的逻辑
* }))
*/
return chain.filter(exchange).then(Mono.fromRunnable(()->{
Long startTime = exchange.getAttribute(BEGIN_TIME);
if (startTime != null) {
System.out.println(exchange.getRequest().getURI() + "请求耗时: " + (System.currentTimeMillis() - startTime) + "ms");
}
}));
}
};
}
@Setter
@Getter
static class Config{
private boolean show;
}
}
написать приложение.xml
server:
port: 9000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的微服务
routes:
- id: product_route # 路由的名字
uri: lb://product-service # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/product-serv/** # 符合这个规定的才进行1转发
filters:
- StripPrefix=1 # 将第一层去掉
- id: order_route
uri: lb://order-service
predicates:
- Path=/order-serv/**
filters:
- StripPrefix=1
- Time=true
Путь доступа:http://localhost:9000/order-serv/getById?o=1&pid=1
9.6.2, глобальный фильтр
Глобальные фильтры применяются ко всем маршрутам, настройка не требуется. Через глобальный фильтр могут быть реализованы такие функции, как унифицированная проверка разрешений и проверка безопасности. Внутри Spring Cloud Gateway также обрабатывает переадресацию всего маршрута с помощью ряда встроенных глобальных фильтров.
Логика аутентификации в разработке:
- Когда клиент запрашивает услугу в первый раз, сервер выполняет информационную аутентификацию (логин) пользователя.
- После прохождения аутентификации информация о пользователе шифруется для формирования маркера, который возвращается клиенту в качестве учетных данных для входа.
- Для каждого последующего запроса клиент несет токен аутентификации.
- Сервер для расшифровки токена, чтобы определить, является ли действительным.
Давайте моделируем требование: для достижения функции унифицированной аутентификации, нам нужно судить, содержит ли запрос токен в шлюзе и, если нет, не пересылать маршрут и выполнить нормальную логику, если есть.
Напишите глобальный фильтр
@Component
public class AuthGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isBlank(token)) {
System.out.println("鉴权失败");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}
9.6.3 Ограничение тока шлюза
Шлюз является общим входом для всех запросов, можно ограничить поток в шлюзе, а также во многих ограничивающих вариантах, мы используем эти изученные компоненты Sentinel для достижения вышеупомянутого ограничения шлюза. Sentinel поддерживает ограничения SpringCloud Gateway, Zuul и других основных шлюзов.
Начиная с версии 1.6.0, Sentinel предоставляет модуль адаптации для Spring Cloud Gateway, который может обеспечить текущее ограничение в двух измерениях ресурсов:
- измерение маршрута: запись маршрута, настроенная в файле конфигурации Spring, имя ресурса соответствует идентификатору маршрута.
- Пользовательский параметр API: пользователи могут использовать API, предоставляемый Sentinel, для настройки некоторых групп API.
9.6.3.1, интеграция со шлюзом Sentinel
добавить зависимости
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
Напишите классы конфигурации, чтобы ограничить
Суть класса конфигурации заключается в замене текущего лимита графического интерфейса nacos кодом.
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
// 配置限流的异常处理器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
// 初始化一个限流的过滤器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
//增加对商品微服务的限流
@PostConstruct
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("product_route")
.setCount(3) // 三次
.setIntervalSec(1) // 一秒,表示一秒钟1超过了三次就会限流
);
GatewayRuleManager.loadRules(rules);
}
}
Изменить формат возврата текущего лимита по умолчанию
Если мы не хотим возвращать ошибку по умолчанию, когда ток ограничен, нам нужно настроить ошибку и указать собственный формат возврата. Нам просто нужно добавить часть конфигурации в класс.
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map map = new HashMap<>();
map.put("code", 0);
map.put("message", "接口被限流了");
return ServerResponse.status(HttpStatus.OK).
contentType(MediaType.APPLICATION_JSON).
body(BodyInserters.fromValue(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
контрольная работа
9.6.3.2 Группировка пользовательских API
Мы можем обнаружить, что приведенное выше определение ограничивает поток всего сервиса, а степень детализации недостаточно высока. Пользовательское группирование API — это более детальное определение правил ограничения тока, которое может реализовать детальное ограничение тока для метода.
Добавьте ApiController в проект Shop-order-server.
@RestController
@RequestMapping("/api")
public class ApiController {
@RequestMapping("/hello")
public String api1(){
return "api";
}
}
Добавьте конфигурацию в GatewayConfiguration
@PostConstruct
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition("order_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
add(new ApiPathPredicateItem().setPattern("/order-serv/api/**"). setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
definitions.add(api1);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
@PostConstruct
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("product_route")
.setCount(3)
.setIntervalSec(1)
);
rules.add(new GatewayFlowRule("order_api").
setCount(1).
setIntervalSec(1));
GatewayRuleManager.loadRules(rules);
}
контрольная работа
прямое интервьюhttp://localhost:8082/api/helloСлучится ли это, посетитеhttp://localhost:9000/order-serv/api/helloТам будет ограниченный поток.