Directorio de artículos
-
- 1 Caso de inicio de puerta de enlace de servicio
-
- 1.1 Agregar modulr gateway service api-gateway en el proyecto principal
- 1.2 El archivo pom introduce la dependencia del paquete jar de la puerta de enlace
- 1.3 Agregar configuración básica al archivo yml
- 1.5 Iniciar el servicio de puerta de enlace y el servicio de producto
- 1.6 Solicitud del navegador `http: // localhost: 7000 / product-serv / product / 1` verificación de prueba
- 2 La pasarela de servicios integra nacos para leer la información del servicio
-
- 2.1 archivo pom agregado a nacos-discovery dependiente de nacos
- 2.2 Agregue la anotación de descubrimiento de servicios nacos @EnableDiscoveryClient a la clase de inicio
- 2.3 archivo yml integrado nacos, modificar enrutamiento y reenvío de información
- 2.4 Solicitud del navegador `http: // localhost: 7000 / product-serv / product / 1` verificación de prueba
- 3 Uso de aserción de pasarela de servicio
- 4 Uso del filtro de puerta de enlace de servicio
- 5 Límite de corriente centinela integrado en la puerta de enlace
1 Caso de inicio de puerta de enlace de servicio
1.1 Agregar modulr gateway service api-gateway en el proyecto principal
1.2 El archivo pom introduce la dependencia del paquete jar de la puerta de enlace
<dependencies>
<!--引入gateway网关依赖,注:不能同时引入依赖starter-web-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
1.3 Agregar configuración básica al archivo yml
# 服务基础配置
server:
port: 7000
spring:
application:
name: api-gateway
# 网关配置
cloud:
gateway:
routes: # 路由数组,支持多个路由,路由配置参照RouteDefinition
- id: route-product # 路由标识
uri: http://localhost:8081 # 要转发到的远程服务地址
order: 0 # 排序,数字越小优先级越高
predicates: #断言数组,满足条件实现转发,支持自定义断言
- Path=/product-serv/**
filters: #过滤数组,转发前服务处理,支持前置过滤和后置过滤,支持自定义过滤(局部过滤、全局过滤)
- StripPrefix=1 # 转发前去掉一层访问路径
### 1.4 Agregar la clase de inicio ApiGateWayApplication
package cn.hzp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class);
}
}
1.5 Iniciar el servicio de puerta de enlace y el servicio de producto
1.6 http://localhost:7000/product-serv/product/1
Verificación de prueba de solicitud de navegador
2 La pasarela de servicios integra nacos para leer la información del servicio
Sobre la base del caso 1, para optimizar el valor de enrutamiento y reenvío de uri http://localhost:8081
, integre nacos para leer la información del servicio
2.1 archivo pom agregado a nacos-discovery dependiente de nacos
<!--引入nacos依赖,实现网关注册到nacos同时从nacos读取其他服务信息-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.2 Agregue la anotación de descubrimiento de servicios nacos @EnableDiscoveryClient a la clase de inicio
2.3 archivo yml integrado nacos, modificar enrutamiento y reenvío de información
# 服务基础配置
server:
port: 7000
spring:
application:
name: api-gateway
# 网关配置
cloud:
# 增加nacos配置信息
nacos:
discovery:
server-addr: localhost:8848
gateway:
# 使gateway可以读取nacos中服务信息
discovery:
locator:
enabled: true
# 路由数组,支持多个路由,路由配置参照RouteDefinition
routes:
- id: route-product # 路由标识
# uri: http://localhost:8081 # 要转发到的远程服务地址
uri: lb://service-product # lb支持负载均衡(loadBalance)
order: 0 # 排序,数字越小优先级越高
predicates: #断言数组,满足条件实现转发,支持自定义断言
- Path=/product-serv/**
filters: #过滤数组,转发前服务处理,支持前置过滤和后置过滤,支持自定义过滤(局部过滤、全局过滤)
- StripPrefix=1 # 转发前去掉一层访问路径
2.4 http://localhost:7000/product-serv/product/1
Verificación de prueba de solicitud de navegador
3 Uso de aserción de pasarela de servicio
3.1 Aserción de puerta de enlace incorporada
Todas estas clases de aserción heredan AbstractRoutePredicateFactory y usan la URL de referenciahttps://www.cnblogs.com/wgslucky/p/11396579.html
3.2 afirmación personalizada
Consulte la clase de aserción incorporadaPathRoutePredicateFactory
3.2.1 El archivo Pom introduce la dependencia de lombok
<!--引入lombok,方便日志输出-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
3.2.2 Agregar clase de aserción
package cn.hzp.predicates;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
/**
* 断言类名称必须是"断言的配置属性"+RoutePredicateFactory
* 必须继承AbstractRoutePredicateFactory<配置类>
*/
@Slf4j
@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {
/**
* 构造函数
*/
public AgeRoutePredicateFactory() {
super(AgeRoutePredicateFactory.Config.class);
}
/**
* 读取配置文件的参数信息,赋值到配置类的属性上
*/
@Override
public List<String> shortcutFieldOrder() {
// 参数顺序需要和配置文件参数顺序一致
return Arrays.asList("minAge", "maxAge");
}
/**
* 断言逻辑
*/
@Override
public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
return serverWebExchange -> {
String ageStr = serverWebExchange.getRequest().getQueryParams().getFirst("age");
if (StringUtils.isNotEmpty(ageStr)) {
int age = Integer.parseInt(ageStr);
if (age >= config.getMinAge() && age <= config.getMaxAge()) {
return true;
}
}
return false;
};
}
/**
* 配置类,接收配置文件中的参数
*/
@Data
public static class Config {
private int minAge;
private int maxAge;
}
}
3.2.3 Agregar aserción de edad al archivo yml
# 服务基础配置
server:
port: 7000
spring:
application:
name: api-gateway
# 网关配置
cloud:
# 增加nacos配置信息
nacos:
discovery:
server-addr: localhost:8848
gateway:
# 使gateway可以读取nacos中服务信息
discovery:
locator:
enabled: true
# 路由数组,支持多个路由,路由配置参照RouteDefinition
routes:
- id: route-product # 路由标识
# uri: http://localhost:8081 # 要转发到的远程服务地址
uri: lb://service-product # lb支持负载均衡(loadBalance)
order: 0 # 排序,数字越小优先级越高
predicates: #断言数组,满足条件实现转发,支持自定义断言
- Path=/product-serv/**
- Age=18,60 #自定义断言,实现服务只能被18-60岁之间的人访问
filters: #过滤数组,转发前服务处理,支持前置过滤和后置过滤,支持自定义过滤(局部过滤、全局过滤)
- StripPrefix=1 # 转发前去掉一层访问路径
3.2.4 Verificación de la prueba del navegador
- Solicitar
http://localhost:7000/product-serv/product/1?age=61
devoluciones 404 - Solicitud
http://localhost:7000/product-serv/product/1?age=60
para volver a la normalidad
4 Uso del filtro de puerta de enlace de servicio
- El prefiltro puede verificar la identidad y registrar la información de depuración. Como StripPrefix
- El postfiltro puede modificar el encabezado estándar de respuesta, recopilar estadísticas, etc. Como SetStatus
- Incluyendo filtros locales y filtros globales
4.1 Filtro local incorporado y filtro global incorporado
- Todos los filtros locales integrados heredan AbstractGatewayFilterFactory, como SetStatusGatewayFilterFactory
- Los filtros globales incorporados heredan GlobalFilter, Ordered, como LoadBalancerClientFilter
4.2 Filtro local personalizado
El método es el mismo que la aserción personalizada, refiriéndose a SetStatusGatewayFilterFactory, diseñando un filtro parcial para controlar la salida del registro
4.2.1 Nueva clase de filtro local LogGatewayFilterFactory
package cn.hzp.filters;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* 网关gateway-自定义过滤器类
*/
@Slf4j
@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {
/**
* 构造函数
*/
public LogGatewayFilterFactory() {
super(LogGatewayFilterFactory.Config.class);
}
/**
* 将配置文件的属性信息赋值到配置类中
*/
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("consoleLog", "cacheLog");
}
/**
* 自定义过滤逻辑
*/
@Override
public GatewayFilter apply(LogGatewayFilterFactory.Config config) {
return (exchange, chain) -> {
if (config.isConsoleLog()) {
log.info("输出控制台日志");
}
if (config.isCacheLog()) {
log.info("输出缓存日志");
}
return chain.filter(exchange);
};
}
/**
* 配置类
*/
@Data
@NoArgsConstructor
public static class Config {
private boolean consoleLog;
private boolean cacheLog;
}
}
4.2.2 Agregar registro de atributos de filtro al archivo yml
# 服务基础配置
server:
port: 7000
spring:
application:
name: api-gateway
# 网关配置
cloud:
# 增加nacos配置信息
nacos:
discovery:
server-addr: localhost:8848
gateway:
# 使gateway可以读取nacos中服务信息
discovery:
locator:
enabled: true
# 路由数组,支持多个路由,路由配置参照RouteDefinition
routes:
- id: route-product # 路由标识
# uri: http://localhost:8081 # 要转发到的远程服务地址
uri: lb://service-product # lb支持负载均衡(loadBalance)
order: 0 # 排序,数字越小优先级越高
predicates: #断言数组,满足条件实现转发,支持自定义断言
- Path=/product-serv/**
# - Age=18,60 #自定义断言,实现服务只能被18-60岁之间的人访问
filters: #过滤数组,转发前服务处理,支持前置过滤和后置过滤,支持自定义过滤(局部过滤、全局过滤)
- StripPrefix=1 # 转发前去掉一层访问路径
- Log=true, false # 自定义过滤,控制日志输出
4.2.3 Verificación de la prueba
El navegador solicita http://localhost:7000/product-serv/product/1
, la consola genera un "registro de salida de la consola" para indicar el éxito.
4.3 filtro global personalizado
Diseñar una lógica de autenticación unificada, que el centro de autenticación ignora
4.3.1 Nueva clase de filtro global AuthGlobalFilter
package cn.hzp.filters;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class AuthGlobalFilter implements GlobalFilter, Ordered {
/**
* 自定义过滤器逻辑
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 统一鉴权逻辑
String token = exchange.getRequest().getQueryParams().getFirst("token");
if(!StringUtils.equals("admin", token)) {
log.info("认证失败");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
/**
* 当前过滤器优先级,值越小优先级越高
*/
@Override
public int getOrder() {
return 0;
}
}
4.3.2 Verificación de la prueba
- Solicitud del navegador
http://localhost:7000/product-serv/product/1?token=admin
, devolver el resultado normalmente - Solicitud del navegador
http://localhost:7000/product-serv/product/1?token=admins
, devuelva 401
5 Límite de corriente centinela integrado en la puerta de enlace
- 1 Límite actual de dimensión de ruta, de acuerdo con la identificación de ruta para el límite actual
- 2 Límite de corriente de dimensión de API personalizado, de acuerdo con la agrupación de API para el límite de corriente
5.1 Caso límite de corriente de dimensión de ruta
5.1.1 Archivo Pom mayor dependencia sentinel-spring-cloud-gateway-adapter
<!--gateway集成sentinel实现限流-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
5.1.2 Escribir la clase de configuración GatewaySentinelConfiguration
Inyectar SentinelGatewayFilter y SentinelGatewayBlockExceptionHandler
package cn.hzp.config;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import javafx.beans.property.ObjectProperty;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import java.util.*;
@Configuration
public class GatewaySentinelConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewaySentinelConfiguration(ObjectProvider<List<ViewResolver>> vewResolverProvider, ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = vewResolverProvider.getIfAvailable(Collections:: emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
/**
* 初始化一个限流过滤器
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter(){
return new SentinelGatewayFilter();
}
/**
* 配置初始化的限流参数
*/
@PostConstruct
public void initGatewayRules(){
Set<GatewayFlowRule> rules = new HashSet<>();
// resource为路由id,count为阈值,interval为统计时间窗口,默认1秒
rules.add(new GatewayFlowRule("route-product")
.setCount(1)
.setIntervalSec(1));
GatewayRuleManager.loadRules(rules);
}
/**
* 配置异常处理器
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(){
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
/**
* 自定义限流异常返回页面
*/
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {
Map map = new HashMap<>();
map.put("code", 0);
map.put("message", "接口被限流了");
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(map));
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
5.1.3 Prueba de inicio
El navegador solicita http://localhost:7000/product-serv/product/1?token=admin
, la página se actualiza varias veces y aparece el límite actual, y la prueba es exitosa
5.2 Breve descripción del límite de corriente de dimensión api
5.2.1 Modificar la clase de configuración
Agregue un nuevo grupo de api personalizado de método en la clase de configuración, modifique el método de límite actual de inicialización initGatewayRules
package cn.hzp.config;
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import javax.annotation.PostConstruct;
import java.util.*;
@Configuration
public class GatewaySentinelConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewaySentinelConfiguration(ObjectProvider<List<ViewResolver>> vewResolverProvider, ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = vewResolverProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
/**
* 初始化一个限流过滤器
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
/**
* 配置初始化的限流参数
*/
@PostConstruct
public void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
// 以路由id为限流维度,resource为路由id,count为阈值,interval为统计时间窗口,默认1秒
// rules.add(new GatewayFlowRule("route-product").setCount(1).setIntervalSec(1));
// 以api分组为限流维度
rules.add(new GatewayFlowRule("product-api1").setCount(1).setIntervalSec(1));
rules.add(new GatewayFlowRule("product-api2").setCount(1).setIntervalSec(1));
GatewayRuleManager.loadRules(rules);
}
/**
* 配置异常处理器
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
/**
* 自定义限流异常返回页面
*/
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {
Map map = new HashMap<>();
map.put("code", 0);
map.put("message", "接口被限流了");
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(map));
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
/**
* api维度限流,自定义api分组
*/
@PostConstruct
public void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
// api分组,apiName自定义唯一即可
ApiDefinition api1 = new ApiDefinition("product-api1")
.setPredicateItems(new HashSet<ApiPredicateItem>() {
{
add(new ApiPathPredicateItem()
.setPattern("/product-serv/api1/**")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)
);
}});
ApiDefinition api2 = new ApiDefinition("product-api2")
.setPredicateItems(new HashSet<ApiPredicateItem>() {
{
add(new ApiPathPredicateItem()
.setPattern("/product-serv/api2/demo2")
);
}});
definitions.add(api1);
definitions.add(api2);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
}
5.2.2 Agregar método de prueba a ProductController en el servicio del producto
/**
* 测试gateway的api分组限流
*/
@RequestMapping("/api1/demo1")
public String api1Demo1() {
return "/api1/demo1";
}
/**
* 测试gateway的api分组限流
*/
@RequestMapping("/api1/demo2")
public String api1Demo2() {
return "/api1/demo2";
}
/**
* 测试gateway的api分组限流
*/
@RequestMapping("/api2/demo1")
public String api2Demo1() {
return "/api2/demo1";
}
/**
* 测试gateway的api分组限流
*/
@RequestMapping("/api2/demo2")
public String api2Demo2() {
return "/api2/demo2";
}
5.2.3 Verificación de la prueba
- 1 Si el navegador hace solicitudes locas
http://localhost:7000/product-serv/api1/demo1
, le pedirá que se restrinja - 2 Si el navegador hace solicitudes locas
http://localhost:7000/product-serv/api1/demo2
, le pedirá que se restrinja - 3 El navegador hace solicitudes locas
http://localhost:7000/product-serv/api2/demo1
, ha sido normal
questMapping ("/ api1 / demo2")
public String api1Demo2 () { return “/ api1 / demo2”; } / **
- Pruebe el límite actual de agrupación de API de la puerta de enlace
/
@RequestMapping ("/ api2 / demo1")
public String api2Demo1 () { return "/ api2 / demo1"; } /
* - Prueba el límite actual de agrupación de API de la puerta de enlace
* /
@RequestMapping ("/ api2 / demo2")
public String api2Demo2 () { return "/ api2 / demo2"; }
- Pruebe el límite actual de agrupación de API de la puerta de enlace
#### 5.2.3 测试验证
- 1 浏览器疯狂请求`http://localhost:7000/product-serv/api1/demo1`,会提示被限流
- 2 浏览器疯狂请求`http://localhost:7000/product-serv/api1/demo2`,会提示被限流
- 3 浏览器疯狂请求`http://localhost:7000/product-serv/api2/demo1`,一直正常
- 4 浏览器疯狂请求`http://localhost:7000/product-serv/api1/demo2`,会提示被限流