4-1 Uso de la puerta de enlace de la puerta de enlace de servicio _ límite actual del filtro de aserción

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

    <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/1Verificació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/1Verificació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=61devoluciones 404
  • Solicitud http://localhost:7000/product-serv/product/1?age=60para 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"; }


#### 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`,会提示被限流

Supongo que te gusta

Origin blog.csdn.net/weixin_45544465/article/details/105939164
Recomendado
Clasificación