Spring Cloud Gateway——(二)微服务网关过滤器和网关限流

1.4 微服务网关过滤器

Gateway作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作往往是通过网关提供的过滤器来实现的。之前写的给url添加前缀或者去除前缀都是用过滤器实现的。

  • Gateway自带过滤器有几十个,我常用的自带过滤器有:
过滤器名称说明
AddRequestHeader对匹配上的请求加上Header
AddRequestParameters对匹配上的请求路由添加参数
AddResponseHeader对从网关返回的响应添加Header
StripPrefix对匹配上的请求路径去除前缀

更多过滤器和详细的说明在官网链接

  • 配置全局默认过滤器

这些自带的过滤器可以和之前使用去除url前缀的用法类似,也可以将这些过滤器配置成不只是针对某个路由;而是可以对所有路由生效,也就是配置默认过滤器:

spring:
  application:
    name: gateway
  cloud:
    gateway:
    # 默认过滤器,对所有路由生效
	  default-filters:
		# 响应头过滤器,对输出的响应设置其头部属性名称为X-Response-Default-MyName,值为xm;如果有多个参数多则重写一行设置不同的参数
	    - AddResponseHeader=X-Response-Default-MyName, xm

1.5 自定义过滤器

1.5.1 自定义局部过滤器

自定义一个获取请求头消息的过滤器

package com.xm.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;

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

/**
 * @title: MyParamGatewayFilterFactory
 * @projectName: spring_cloud
 * @description: TODO
 * @author: Tzh
 * @date: 2019/11/25  19:06
 */
@Component
public class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.MyParam> {
    public static final String PARAM_NAME = "param";
    public MyParamGatewayFilterFactory() {
        super(MyParam.class);
    }
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(PARAM_NAME);
    }
    @Override
    public GatewayFilter apply(MyParam config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            if (request.getQueryParams().containsKey(config.param)) {
                request.getQueryParams().get(config.param).
                        forEach(value -> System.out.printf("---MyParamGatewayFilterFactory--- %s = %s ---\n",config.param,value));
            }
            return chain.filter(exchange);
        };
    }
    public static class MyParam{
        public String param;
        public String getParam() {
            return param;
        }
        public void setParam(String param) {
            this.param = param;
        }
    }
}

写好配置类后只需要在yml中配置需要获取请求中key的值就可以了

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
      - id: user-service-route
        uri: lb://user-service
        predicates:
        - Path=/api/user/**
        filters:
          - MyParam=name # 配置后就可以获取请求中key为name的值

1.5.2 自定义全局过滤器

package com.xm.gateway.filter;

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;

/**
 * @title: MyGlobalFilter
 * @projectName: 
 * @description: TODO
 * @author: Tzh
 * @date: 2019/11/25  19:45
 */
@Component
public class MyGlobalFilter implements GlobalFilter , Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("-----------------全局过滤器MyGlobalFilter---------------------\n");
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (StringUtils.isBlank(token)) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }
    //设置过滤器的优先级,值越小优先级越高
    @Override
    public int getOrder() {
        return 1;
    }
}

1.6 网关限流

(1)spring cloud gateway 默认使用redis的RateLimter限流算法来实现。所以我们要使用首先需要引入redis的依赖

<!‐‐redis‐‐>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring‐boot‐starter‐data‐redis‐reactive</artifactId>
	<version>2.1.3.RELEASE</version>
</dependency>

(2)在工程中创建配置类,定义KeyResolver,KeyResolver用于计算某一个类型的限流的KEY也就是说,可以通过KeyResolver来指定限流的Key。

	/**
     * 定义一个KeyResolver
     */
    @Bean
    public KeyResolver ipKeyResolver() {
        return new KeyResolver() {
            @Override
            public Mono<String> resolve(ServerWebExchange exchange) {
                return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
            }
        };
    }

(3)修改application.yml中配置项,指定限制流量的配置以及REDIS的配置

	  # 商品服务
      - id: goods
        uri: lb://goods # 商品服务在Eureka上注册的名字
        predicates:
        - Path=/goods/** # 拦截的路径
        filters:
        - StripPrefix= 1 # url前缀第一个丢弃
        - PrefixPath=/goods/v1 # 添加url前缀 /goods/v1
        - name: RequestRateLimiter #请求数限流 名字不能随便写
          args:
            key-resolver: "#{@ipKeyResolver}"
            redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充平均速率
            redis-rate-limiter.burstCapacity: 1 #令牌桶总容量

解释:

  • burstCapacity:令牌桶总容量。
  • replenishRate:令牌桶每秒填充平均速率。
  • key-resolver:用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。

通过在replenishRateburstCapacity中设置相同的值来实现稳定的速率。

设置burstCapacity高于replenishRate时,可以允许临时突发。在这种情况下,需要在这段突发时间之间允许速率限制器(根据 replenishRate),因为 2 个连续的突发将导致请求被丢弃(HTTP 429 - Too Many Requests)。

key-resolver: “#{@ipKeyResolver}” 用于通过SPEL表达式来指定使用哪一个KeyResolver.
如上配置:
表示 一秒内,允许 一个请求通过,令牌桶的填充速率也是一秒钟添加一个令牌。
最大突发状况 也只允许 一秒内有一次请求,可以根据业务来调整 。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐