Spring Cloud Gateway Filter过滤器

目录

过滤器作用

网关过滤器工厂

AddRequestHeaderGatewayFilterFactory过滤器工厂

过滤器工厂核心API

自定义过滤器工厂

AbstractGatewayFilterFactory 实现自定义过滤器工厂

全局过滤器

自定义全局过滤器

Gateway 限流

总结


上一篇描述了服务网关Gateway 的工作流程,断言、和生命周期;Predicate 的作用是,请求到来之后,如果匹配到相应的断言,则会交给相应的路由进行处理,在路由处理前后,请求还会交给Filter 处理;

过滤器作用

在服务网关执行流程中可以了解到,过滤器起到了非常重要的作用,pre 类型的filter 可以实现日志输出,权限校验,限流等功能,post 类型的filter  可以实现响应头、响应内容、流量监控等功能;

同时,服务网关的过滤器可以分为两种,一种是作用在单个路由的网关过滤器(Gateway Filter),他的作用和断言类似;一种是作用于所有路由的,他是全局过滤器(Global Gateway Filter);

网关过滤器工厂

GateWayFilter 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用于当前路由上,或者通过spring.cloud.default-filters 配置在全局中,作用在所有的路由上;

在服务网关中包含了多个内置的网关过滤器工厂(Gateway Filter),配置和断言类似,在配置文件中配置即可生效;服务网关内置的Gateway Filter Factory 如下:

在这里插入图片描述

AddRequestHeaderGatewayFilterFactory过滤器工厂

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-Foo, Bar

为原始请求添加名为X-Request-Foo 值为Bar 请求头

其他过滤器工厂具体的配置方式可以参考官网进行配置,此处不一一举例 ;Gateway官方文档

过滤器工厂核心API

exchange.getRequest().mutate().xxx //修改request
exchange.mutage().xxx // 修改exchange
chain.filter() // 将filter 传递给下一个filter
exchange.getResponse() //获取响应

自定义过滤器工厂

上边列举了Gateway 内置过滤器工厂,但是在业务中,如果上述不符合我们的需求,我们需要自定义过滤器工厂;比如我们根据配置开关决定是否打印log日志需求

通过跟踪内置的过滤器工厂源码发现,实现过滤器工厂的方式有两种,一种是继承AbstractGatewayFilterFactory,实现类参考RedirectToGatewayFilterFactory;另一种是继承AbstractNameValueGatewayFilterFactory实现类参考AddRequestHeaderGatewayFilterFactory。两种方式的区别就是前者接收一个参数,后者接收两个参数;注意:自定义过滤器工厂类后缀要以xxxGatewayFilterFactory 结尾,配置文件配置属性名称为xxx;

下面基于第一种方式实现根据配置判断是否打印log日志;

AbstractGatewayFilterFactory 实现自定义过滤器工厂

package com.corn.gateway;

import com.google.common.collect.Lists;
import lombok.Data;
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 reactor.core.publisher.Mono;

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

/**
 * @Author corn
 * @Date 2021/3/30 20:57
 */
@Slf4j
@Component
public class PrintLogGatewayFilterFactory extends AbstractGatewayFilterFactory<PrintLogGatewayFilterFactory.Config> {

    public PrintLogGatewayFilterFactory(){
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {

        return ((exchange, chain) -> {
            return chain.filter(exchange).then(
                    Mono.fromRunnable(()->{
                        if (config.isPrint()){
                            // 打印路径地址
                            log.info(exchange.getRequest().getURI().getRawPath());
                        }
                    })
            );
        });
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("print");
    }

    @Data
    public static class Config{
        boolean print;
    }

}

配置

spring:
  cloud:
    gateway:
      routes:
      - id: printlog_route
        uri: lb:\\nacos-alibaba
        filters:
        # 开关控制是否打印请求url
        - PrintLog=false 

全局过滤器

上边讲述了作用于具体路由下的过滤器Gateway Fliter ,而GloubaFilter 则不需要在配置文件中配置,便可以作用在所有的路由上,GloubaFilter 将请求的业务及路由Url转换成真实的业务服务请求地址和核心过滤器,不需要配置,系统在初始化的时候,作用在每个路由上;注意:order 值越小,优先级越高,越靠前执行

GloubaFilter 过滤器包含了:

1 Combined Global Filter and GatewayFilter Ordering   
2 Forward Routing Filter    路径转发过滤器
3 LoadBalancerClient Filter  负载均衡客户端过滤器
4 Netty Routing Filter  http客户端过滤器
5 Netty Write Response Filter  http端客户端过滤器
6 RouteToRequestUrl Filter   转发路由过滤器
7 Websocket Routing Filter  webSocket 转发过滤器

自定义全局过滤器

实现自定义全局过滤器校验参数和打印请求执行时间

package com.corn.gateway;

import lombok.extern.slf4j.Slf4j;
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.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;

/**
 * @Author corn
 * @Date 2021/3/28 15:27
 * 自定义网关
 * 实现判断请求参数中是否含有version 参数,如果不含有这返回404
 */
@Slf4j
@Component
public class MyGatewayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        // 获取请求参数
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        String version = queryParams.getFirst("version");
        if (StringUtils.isEmpty(version)){
            log.info("请求参数中缺少version 参数,返回404" );
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return  response.setComplete();
        }

        exchange.getAttributes().put("startTime",System.currentTimeMillis());

        return chain.filter(exchange).then(
                Mono.fromRunnable(()->{
                   Long startTime = exchange.getAttribute("startTime");
                   if (null != startTime){
                       log.info("请求地址:{},响应时间:{}ms",exchange.getRequest().getURI().getRawPath(),(System.currentTimeMillis()-startTime));
                   }
                })
        );
    }

    /**
     *@描述 值越小,优先级越高
     *@参数
     *@返回值
     *@创建人  corn
     *@创建时间  2021/3/28
     */
    @Override
    public int getOrder() {
        return 10;
    }
}

Gateway 限流

未引入Gateway之前,针对我们的微服务接口,如果我们需要对流量进行管控,可以通过sentinel 来实现。在Gateway 中,官方给我们提供了RequestRateLimiterGatewayFilterFactory 这个类,这个类使用了redis 和lua 脚本实现令牌桶算法进行限流;

配置方式如下:

引入依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

编写规则

package com.corn.gateway;


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

/**
 * 令牌算法限流,根据请求url进行限流
 * @Author corn
 * @Date 2021/3/28 18:23
 */
@Configuration
public class Raonfiguration {
    /**
     * 按照Path限流
     *
     * @return key
     */
    @Bean
    public KeyResolver pathKeyResolver() {
        return exchange -> Mono.just(
                exchange.getRequest()
                        .getPath()
                        .toString()
        );
    }

//    /**
//     *@描述 针对来源ip 进行限流
//     *@参数
//     *@返回值
//     *@创建人  corn
//     *@创建时间  2021/3/30
//     */
//    @Bean
//    public KeyResolver ipKeyResolver() {
//        return exchange -> Mono.just(
//                exchange.getRequest()
//                        .getHeaders()
//                        .getFirst("X-Forwarded-For")
//        );
//    }
//
//    /**
//     *@描述 根据用户进行限流
//     *@参数 
//     *@返回值 
//     *@创建人  corn
//     *@创建时间  2021/3/30
//     */
//    @Bean
//    public KeyResolver userKeyResolver() {
//        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
//    }

}

 添加配置

spring:
  cloud:
    gateway:
      routes:
      - id: printlog_route
        uri: lb:\\nacos-alibaba
        filters:
        # 开关控制是否打印请求url
        - name: RequestRateLimiter
          args:
                # 令牌桶每秒填充平均速率
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的上限
                redis-rate-limiter.burstCapacity: 2
                # 使用SpEL表达式从Spring容器中获取Bean对象
                key-resolver: "#{@pathKeyResolver}"


总结

结合上一篇博文,讲述了服务网关Gateway 的原理和作用,然后梳理了两个核心组件断言和过滤器,并基于两个核心组件实现简单配置的demo ;

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值