SpringCloud - Spring Cloud 之 Gateway网关,Route路由,Predicate 断言,Filter 过滤器(十三)

Posted MinggeQingchun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud - Spring Cloud 之 Gateway网关,Route路由,Predicate 断言,Filter 过滤器(十三)相关的知识,希望对你有一定的参考价值。

阅读本文前可先参考

​​​​​​SpringCloud - Spring Cloud根/父项目,开发准备(二)_MinggeQingchun的博客-CSDN博客

SpringCloud - Spring Cloud 之 Gateway网关(十三)_MinggeQingchun的博客-CSDN博客

Web 有三大组件(监听器 过滤器 servlet),Spring Cloud GateWay 最主要的功能就是路由转发,而在定义转发规则时主要涉及了以下三个核心概念

1、Route(路由)

2、Predicate(断言)

3、Filter(过滤)

一、Routes路由配置 

Gateway有两种配置路由方式

1、Java代码配置类路由

参考官网给出demo Spring Cloud Gateway

@SpringBootApplication
public class DemogatewayApplication 
	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) 
		return builder.routes()
			.route("path_route", r -> r.path("/get")
				.uri("http://httpbin.org"))
			.route("host_route", r -> r.host("*.myhost.org")
				.uri("http://httpbin.org"))
			.route("rewrite_route", r -> r.host("*.rewrite.org")
				.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/$segment"))
				.uri("http://httpbin.org"))
			.route("hystrix_route", r -> r.host("*.hystrix.org")
				.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
				.uri("http://httpbin.org"))
			.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
				.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
				.uri("http://httpbin.org"))
			.route("limit_route", r -> r
				.host("*.limited.org").and().path("/anything/**")
				.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
				.uri("http://httpbin.org"))
			.build();
	

 在 springcloud-7-service-eureka-gateway 模块总 自定义一个配置类 GatewayRouteConfig

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 路由配置类
 */
@Configuration
public class GatewayRouteConfig 

    /**
     * 代码的路由 和 application.yml文件 不冲突,都可以用
     * 如果 uri后面给了一个访问地址 和匹配地址相同,就不会再拼接
     */
    @Bean
    public RouteLocator customRoiteLocator(RouteLocatorBuilder builder)
        //以下url从B站中引用
        return builder.routes()
                .route("movie-id",r->r.path("/movie").uri("https://www.bilibili.com/movie/?spm_id_from=333.1007.0.0"))
                .route("douga-id",r->r.path("/v/douga").uri("https://www.bilibili.com/v/douga/?spm_id_from=333.1007.0.0"))
                .build();
    

启动springboot 类 

输入访问 http://localhost:81/movie

即会跳转到相应 url 进行访问资源

2、application.properties 或 application.yml 配置文件

即 SpringCloud - Spring Cloud 之 Gateway网关(十三)_MinggeQingchun的博客-CSDN博客

文中的 三、Gateway应用方式 

application.properties

server.port=81

#eureka注册中心首页的Application这一栏
spring.application.name=springcloud-7-service-eureka-gateway

#每间隔5s,向Eureka服务注册中心发送一次心跳,证明服务是否依然“存活”
eureka.instance.lease-renewal-interval-in-seconds=2
#告诉服务端,如果10s之内没有发送心跳,就代表故障,将本服务踢出
eureka.instance.lease-expiration-duration-in-seconds=10
#告诉服务端,服务实例以IP作为链接,不是取机器名
eureka.instance.prefer-ip-address=false

#注册服务实例ID,,服务ID必须唯一 springcloud-7-service-eureka-gateway
eureka.instance.instance-id=$spring.application.name:$server.port
#注册中心的链接地址  http://eureka8761:8761/eureka,http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka

#网关路由配置
#开启网关,默认开启
spring.cloud.gateway.enabled=true
#节点 routes 是一个List 对象,其中 routes 集合中中又包含多个对象,每个对象有三个属性(一个 索引[0]代表一个对象)
#路由 id,没有固定规则,但唯一
spring.cloud.gateway.routes[0].id=login-service-route
#匹配后提供服务的路由地址;uri统一资源定位符   url 统一资源标识符
spring.cloud.gateway.routes[0].uri=http://localhost:9001
#以下是断言条件,必选全部符合条件;断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
#断言,路径匹配,只要Path匹配上了/doLogin 就往 uri 转发 并且将路径带上 注意:Path 中 P 为大写
#也可以全局匹配,如 /service/**
spring.cloud.gateway.routes[0].predicates[0]=Path=/doLogin
#只能是 GET 请求时,才能访问
spring.cloud.gateway.routes[0].predicates[0]=Method=GET,POST

#配置第二个路由规则
spring.cloud.gateway.routes[1].id=admin-service-route
spring.cloud.gateway.routes[1].uri=http://localhost:9001
spring.cloud.gateway.routes[1].predicates[0]=Path=/doAdmin
spring.cloud.gateway.routes[1].predicates[0]=Method=GET,POST

#表明gateway开启服务注册和发现的功能,并且spring cloud gateway自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务
spring.cloud.gateway.discovery.locator.enabled=true
#是将请求路径上的服务名配置为小写(服务注册的时候,向注册中心注册时将服务名转成大写了),如以/service/*的请求路径被路由转发到服务名为service的服务上
spring.cloud.gateway.discovery.locator.lower-case-service-id=true

application.yml 

server:
  port: 81

#eureka注册中心首页的Application这一栏
spring:
  application:
    name: springcloud-7-service-eureka-gateway
  #网关路由配置
  cloud:
    gateway:
      #开启网关,默认开启
      enabled: true
      #节点 routes 是一个List 对象,其中 routes 集合中中又包含多个对象,每个对象有三个属性(一个 索引[0]代表一个对象)
      routes:
        #路由 id,没有固定规则,但唯一
        - id: login-service-route
          #匹配后提供服务的路由地址;uri统一资源定位符   url 统一资源标识符
          uri: http://localhost:9001
          #以下是断言条件,必选全部符合条件;断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
          predicates:
            #断言,路径匹配,只要Path匹配上了/doLogin 就往 uri 转发 并且将路径带上 注意:Path 中 P 为大写
            #也可以全局匹配,如 /service/**
            - Path=/doLogin
            #只能是 GET,POST 请求时,才能访问
            - Method=GET,POST
        #配置第二个路由规则
        - id: admin-service-route
          uri: http://localhost:9001
          predicates:
            - Path=/doAdmin
            - Method=GET,POST
    #表明gateway开启服务注册和发现的功能,并且spring cloud gateway自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务
    discovery:
      locator:
        enabled: true
        #是将请求路径上的服务名配置为小写(服务注册的时候,向注册中心注册时将服务名转成大写了),如以/service/*的请求路径被路由转发到服务名为service的服务上
        lower-case-service-id: true

eureka:
  instance:
    #每间隔5s,向Eureka服务注册中心发送一次心跳,证明服务是否依然“存活”
    lease-renewal-interval-in-seconds: 2
    #告诉服务端,如果10s之内没有发送心跳,就代表故障,将本服务踢出
    lease-expiration-duration-in-seconds: 10
    #告诉服务端,服务实例以IP作为链接,不是取机器名
    prefer-ip-address: false
    #注册服务实例ID,,服务ID必须唯一 springcloud-7-service-eureka-gateway
    instance-id: $spring.application.name:$server.port
  #注册中心的链接地址  http://eureka8761:8761/eureka,http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka


3、Gateway 微服务名动态路由,负载均衡 

我们设置的 routes的 uri 都是写死的,这样不符合微服务的要求,微服务是只要知道服务的名字,根据名字去找,且直接写死URI就没有负载均衡的效果

默认情况下 Gateway 会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路

由进行转发,从而实现动态路由的功能

uri 的协议(如 http 协议)为 lb(load Balance),表示启用 Gateway 的负载均衡功能。

lb://serviceName 是 spring cloud gateway 在微服务中自动为我们创建的负载均衡 uri

#匹配后提供服务的路由地址;uri统一资源定位符   url 统一资源标识符
#uri 的协议为 lb(load Balance),表示启用 Gateway 的负载均衡功能
#lb://serviceName 是 spring cloud gateway 在微服务中自动为我们创建的负载均衡 uri;serviceName要和启动的微服务名保持一致
spring.cloud.gateway.routes[0].uri=lb://springcloud-7-service-eureka-gateway-login

#eureka注册中心首页的Application这一栏
spring:
  application:
    name: springcloud-7-service-eureka-gateway
  #网关路由配置
  cloud:
    gateway:
      #开启网关,默认开启
      enabled: true
      #节点 routes 是一个List 对象,其中 routes 集合中中又包含多个对象,每个对象有三个属性(一个 索引[0]代表一个对象)
      routes:
        #路由 id,没有固定规则,但唯一
        - id: login-service-route
          #匹配后提供服务的路由地址;uri统一资源定位符   url 统一资源标识符
          #uri: http://localhost:9001
          #uri 的协议为 lb(load Balance),表示启用 Gateway 的负载均衡功能
          #lb://serviceName 是 spring cloud gateway 在微服务中自动为我们创建的负载均衡 uri;serviceName要和启动的微服务名保持一致
          uri: lb://springcloud-7-service-eureka-gateway-login
          #以下是断言条件,必选全部符合条件;断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
          predicates:
            #断言,路径匹配,只要Path匹配上了/doLogin 就往 uri 转发 并且将路径带上 注意:Path 中 P 为大写
            #也可以全局匹配,如 /service/**
            - Path=/doLogin
            #只能是 GET,POST 请求时,才能访问
            - Method=GET,POST

浏览器输入访问http://localhost:81/springcloud-7-service-eureka-gateway-login/doLogin

二、Predicate 断言

Gateway 启动时会去加载一些路由断言工厂(一个 boolean 表达式 )

Spring Cloud Gateway

我们可以通过上述官网查看断言表达式 

断言就是路由添加一些条件(通俗的说,断言就是一些布尔表达式,满足条件的返回 true,不满足的返回 false)

Spring Cloud Gateway 将路由作为 Spring WebFlux HandlerMapping 基础架构的一部分进行匹配。

Spring Cloud Gateway 包括许多内置的路由断言工厂。所有这些断言都与 HTTP请求的不同属性匹配。可以将多个路由断言可以组合使用

Spring Cloud Gateway 创建对象时,使用 RoutePredicateFactory 创建 Predicate 对象,Predicate 对象可以赋值给 Route。

规则实例说明
Path- Path=/gate/,/rule/## 当请求的路径为gate、rule开头的时,转发到http://localhost:9023服务器上
Before- Before=2020-01-20T17:42:47.789-07:00[America/Denver]在某个时间之前的请求才会被转发到 http://localhost:9023服务器上
After- After=2020-01-20T17:42:47.789-07:00[America/Denver]在某个时间之后的请求才会被转发
Between- Between=2020-01-20T17:42:47.789-07:00[America/Denver],2020-01-21T17:42:47.789-07:00[America/Denver]在某个时间段之间的才会被转发
Cookie- Cookie=chocolate, ch.p名为chocolate的表单或者满足正则ch.p的表单才会被匹配到进行请求转发
Header- Header=X-Request-Id, \\d+携带参数X-Request-Id或者满足\\d+的请求头才会匹配
Host- Host=www.hd123.com当主机名为www.hd123.com的时候直接转发到http://localhost:9023服务器上
Method- Method=GET只有GET方法才会匹配转发请求,还可以限定POST、PUT等请求方式

application.yml配置文件

server:
  port: 81

spring:
  application:
    name: gateway-81
  cloud:
    gateway:
      enabled: true #开启网关,默认是开启的
      routes: #设置路由,注意是数组,可以设置多个,按照 id 做隔离
        - id: user-service #路由 id,没有要求,保持唯一即可
          uri: lb://provider #使用 lb 协议 微服务名称做负均衡
          predicates: #断言匹配
            - Path=/info/** #和服务中的路径匹配,是正则匹配的模式
            - After=2020-01-20T17:42:47.789-07:00[Asia/Shanghai] #此断言匹配发生在指定 日期时间之后的请求,ZonedDateTime dateTime=ZonedDateTime.now()获得
            - Before=2020-06-18T21:26:26.711+08:00[Asia/Shanghai] #此断言匹配发生在指定 日期时间之前的请求
            - Between=2020-06-18T21:26:26.711+08:00[Asia/Shanghai],2020-06-18T21:32:26.711+08:00[Asia/Shanghai] #此断言匹配发生在指定日期时间之间的请求
            - Cookie=name,xiaobai #Cookie 路由断言工厂接受两个参数,Cookie 名称和 regexp(一 个 Java 正则表达式)。此断言匹配具有给定名称且其值与正则表达式匹配的 cookie
            - Header=token,123456 #头路由断言工厂接受两个参数,头名称和 regexp(一个 Java 正 则表达式)。此断言与具有给定名称的头匹配,该头的值与正则表达式匹配。
            - Host=**.bai*.com:* #主机路由断言工厂接受一个参数:主机名模式列表。该模式是一 个 ant 样式的模式。作为分隔符。此断言匹配与模式匹配的主机头 
            - Method=GET,POST #方法路由断言工厂接受一个方法参数,该参数是一个或多个参数: 要匹配的 HTTP 方法 
            - Query=username,cxs #查询路由断言工厂接受两个参数:一个必需的 param 和一个 可选的 regexp(一个 Java 正则表达式)。 
            - RemoteAddr=192.168.1.1/24 #RemoteAddr 路由断言工厂接受一个源列表(最小大小 1), 这些源是 cidr 符号(IPv4 或 IPv6)字符串,比如 192.168.1.1/24(其中 192.168.1.1 是 IP 地址,24 是子网掩码)。

三、Filter 过滤器

Gateway 里面的过滤器和 Servlet 里面的过滤器,功能差不多,路由过滤器可以用于修改进入

Http 请求和返回 Http 响应

Gateway 中过滤器

1、按生命周期

pre 在业务逻辑之前

post 在业务逻辑之后

2、按种类

GatewayFilter 需要配置某个路由,才能过滤。如果需要使用全局路由,需要配置 DefaultFilters

GlobalFilter 全局过滤器,不需要配置路由,系统初始化作用到所有路由上

全局过滤器 统计请求次数;限流;token 的校验;ip 黑名单拦截 ;跨域本质(filter) ;144 开头的电话;限制一些 ip 的访问

1、自定义 GlobalFilter 全局过滤器

GlobalFilter 是一种作用于所有的路由上的全局过滤器,通过它,我们可以实现一些统一化的业务功能,例如权限认证、IP 访问限制等。当某个请求被路由匹配时,那么所有的 GlobalFilter 会和该路由自身配置的 GatewayFilter 组合成一个过滤器链

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;

/**
 * 自定义全局过滤器
 */
@Component
public class GlobalFilterConfig implements GlobalFilter, Ordered 

    /**
     * 过滤方法
     * 过滤器链模式;责任链模式
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) 
        //针对请求的过滤,拿到请求的header、url、参数等

        // HttpServletRequest  是web里面的
        // ServerHttpRequest   是webFlux里面(响应式)
        ServerHttpRequest request = exchange.getRequest();

        String path = request.getURI().getPath();
        System.out.println("path====" + path);

        HttpHeaders headers = request.getHeaders();
        System.out.println("headers====" + headers);

        String methodName = request.getMethod().name();
        System.out.println("methodName====" + methodName);

        //IPV4、IPV6地址
        String hostName = request.getRemoteAddress().getHostName();
        System.out.println("hostName====" + hostName);

        String ip = request.getHeaders().getHost().getHostString();
        System.out.println("ip====" + ip);


        // 响应相关的数据
        ServerHttpResponse response = exchange.getResponse();
        //响应头设置编码
        response.getHeaders().set("content-type","application/json;charset=utf-8");

        HashMap<String ,Object> map = new HashMap<>(4);
        map.put("code", HttpStatus.UNAUTHORIZED.value());
        map.put("msg","你未授权");

        ObjectMapper objectMapper = new ObjectMapper();
        //将一个map转成一个字节数组
        byte[] bytes = new byte[0];
        try 
            bytes = objectMapper.writeValueAsBytes(map);
         catch (JsonProcessingException e) 
            e.printStackTrace();
        
        //通过buffer工厂将字节数组包装成一个数据包
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(wrap));

        // 放行 到下一个过滤器
//        return chain.filter(exchange);
    

    /**
     * 指定顺序的方法,越小越先执行
     */
    @Override
    public int getOrder() 
        return 0;
    

2、IP、Token认证拦截

1、token认证拦截

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * token校验
 */
@Component
public class TokenCheckFilter implements GlobalFilter, Ordered 
    /**
     * 指定好放行的路径
     */
    public static final List<String> ALLOW_URL = Arrays.asList("/springcloud-7-service-eureka-gateway-login/doLogin", "/myUrl","/doLogin");

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 和前端约定好 一般放在请求头里面 一般 key   Authorization   value bearer token
     * 1、拿到请求url
     * 2、判断放行
     * 3、拿到请求头
     * 4、拿到token
     * 5、校验
     * 6、放行/拦截
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) 
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        if (ALLOW_URL.contains(path)) 
            return chain.filter(exchange);
        
        // 检查
        HttpHeaders headers = request.getHeaders();
        List<String> authorization = headers.get("Authorization");
        if (!CollectionUtils.isEmpty(authorization)) 
            String token = authorization.get(0);
            if (StringUtils.hasText(token)) 
                // 约定好的有前缀的 bearer token
                String realToken = token.replaceFirst("bearer ", "");
                if (StringUtils.hasText(realToken) && redisTemplate.hasKey(realToken)) 
                    return chain.filter(exchange);
                
            
        
        // 拦截
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().set("content-type","application/json;charset=utf-8");

        HashMap<String, Object> map = new HashMap<>(4);
        // 返回401
        map.put("code", HttpStatus.UNAUTHORIZED.value());
        map.put("msg","未授权");
        ObjectMapper objectMapper = new ObjectMapper();
        byte[] bytes = new byte[0];
        try 
            bytes = objectMapper.writeValueAsBytes(map);
         catch (JsonProcessingException e) 
            e.printStackTrace();
        
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(wrap));
    

    /**
     * 这个顺序 最好在0附近  -2 -1 0 1
     * @return
     */
    @Override
    public int getOrder() 
        return 1;
    

2、IP认证拦截

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * 网关里面 过滤器
 * ip拦截
 * 请求都有一个源头
 * 电话 如:144 开头
 * 请求------->gateway------->service
 * 黑名单 black_list
 * 白名单 white_list
 * 根据数量
 * 像具体的业务服务 一般黑名单
 * 一般像数据库 用白名单
 */
@Component
public class IPCheckFilter implements GlobalFilter, Ordered 

    /**
     * 网关的并发比较高,不要在网关里面直接操作mysql
     * 后台系统可以查询数据库,用户量并发量不大
     * 如果并发量大 可以查redis 或者 在内存中写好
     */
    public static final List<String> BLACK_LIST = Arrays.asList("127.0.0.1", "144.128.139.137");

    /**
     * 1、获取到ip
     * 2、校验ip是否符合规范
     * 3、放行 OR 拦截
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) 
        ServerHttpRequest request = exchange.getRequest();
        String ip = request.getHeaders().getHost().getHostString();

        // 查询数据库 看这个ip是否存在黑名单里面
        if (!BLACK_LIST.contains(ip))
            return chain.filter(exchange);
        

        //拦截
        //注:此处如果拦截IP,就要放开 GlobalFilter 的拦截
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().set("content-type","application/json;charset=utf-8");
        HashMap<String, Object> map = new HashMap<>(4);
        map.put("code", 438);
        map.put("msg","你是黑名单");

        ObjectMapper objectMapper = new ObjectMapper();
        byte[] bytes = new byte[0];
        try 
            bytes = objectMapper.writeValueAsBytes(map);
         catch (JsonProcessingException e) 
            e.printStackTrace();
        
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(wrap));
    

    @Override
    public int getOrder() 
        return 0;
    

3、限流

限流就是限制一段时间内,用户访问资源的次数,减轻服务器压力,限流大致分为两种:

1、IP 限流(5s 内同一个 ip 访问超过 3 次,则限制不让访问,过一段时间才可继续访问)

2.、请求量限流(只要在一段时间内(窗口期),请求次数达到阀值,就直接拒绝后面来的访问了,

过一段时间才可以继续访问)(粒度可以细化到一个 api(url),一个服务)

限流模型:漏斗算法 ,令牌桶算法,窗口滑动算法 计数器算法

令牌桶算法

(1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;

(2)根据限流大小,设置按照一定的速率往桶里添加令牌

(3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;

(4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;

(5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令

牌,以此保证足够的限流

Spring Cloud Gateway 已经内置了一个 RequestRateLimiterGatewayFilterFactory可以直接使用。

目前 RequestRateLimiterGatewayFilterFactory 的实现依赖于 Redis,所以我们还要引入 spring-boot-starter-data-redis-reactive 依赖

<!--限流要引入 Redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

配置类

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;

/**
 * 自定义请求限流
 */
@Configuration
public class RequestRateLimiterConfig 

    // 针对某一个接口,ip来限流,如/doLogin,每一个ip,10s只能访问3次
    @Bean
    @Primary // 主候选的
    public KeyResolver ipKeyResolver() 
        return exchange -> Mono.just(exchange.getRequest().getHeaders().getHost().getHostString());
    

    // 针对这个路径来限制  /doLogin
    // api 就是 接口  外面一般把gateway    api网关  新一代网关
    @Bean
    public KeyResolver apiKeyResolver() 
        return exchange -> Mono.just(exchange.getRequest().getPath().value());
    

需要添加一个  @Primary // 主候选的 注解,不然报如下错误

启动快速访问 

4、CORS跨域配置

@Configuration
public class CorsConfig 
    @Bean
    public CorsWebFilter corsFilter() 
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    
或者 yml 文件配置
spring: 
    cloud: 
        gateway: 
            globalcors: 
                corsConfigurations: '[/**]': // 针对哪些路径 
                allowCredentials: true // 这个是可以携带 cookie
                allowedHeaders: '*' 
                allowedMethods: '*' allowedOrigins: '*'

以上是关于SpringCloud - Spring Cloud 之 Gateway网关,Route路由,Predicate 断言,Filter 过滤器(十三)的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud总结29.Zuul的FallBack回退机制

基于consul的服务注册(含踩坑过程)

Oauth2.0 整合springCloud的Zuul 解决关键BUG 报错信息:Principal must not be null

Spring Cloud Alibaba - 02 SpringCloud SpringCloud Alibaba SpringBoot的生产版本选择

Spring Cloud

spring cloud 服务注册、发现了解