Gateway的使用

Spring Cloud Gateway 支持两种不同的用法:

  • 编码式
  • properties、yml 配置

编码式

首先创建 Spring Boot 项目,添加 Spring Cloud Gateway 模块:


image.png
 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
 </dependency>

项目创建成功后,直接配置一个 RouteLocator 这样一个 Bean,就可以实现请求转发。

   @Bean
    RouteLocator routeLocator(RouteLocatorBuilder builder){
        return builder.routes().
                route("gongjie",j -> j.path("/get").uri("http://httpbin.org"))
                .build();
    }

这里只需要提供 RouteLocator 这个 Bean,就可以实现请求转发。配置完成后,重启项目,访问:http://localhost:8080/get

配置

properties配置

spring.cloud.gateway.routes[0].id=gongjie
spring.cloud.gateway.routes[0].uri=http://httpbin.org
spring.cloud.gateway.routes[0].predicates[0]=Path=/get

yml配置

spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - Path=/get

服务化

首先给 Gateway 添加依赖,将之注册到 Eureka 上。

  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

修改配置

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  #开启自动代理
  application:
    name: gateway
eureka:
  client:
    service-url:
      defaultZone: http://localhost:1999/eureka

image.png

调用:http://localhost:8080/APPUSER/api/user/getUserInfo/1
APPUSER是另外一个服务的服务名称。/api/user/getUserInfo是接口地址。

Predicate

spring cloud gateway 通过谓词(Predicate)来匹配来自用户的请求

  • After 通过时间匹配:
spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - After=2021-09-01T01:01:01+08:00[Asia/Shanghai]

表示,请求时间在 2021-09-01T01:01:01+08:00[Asia/Shanghai] 时间之后,才会被路由。
除了 After 之外,还有两个关键字:
Before,表示在某个时间点之前进行请求转发
Between,表示在两个时间点之间,两个时间点用 , 隔开

  • Method 通过请求方式匹配:
spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - Method=GET

这个配置表示只给 GET 请求进行路由

  • Path 通过请求路径匹配:(重点)
spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - Path=/get

表示路径满足 get这个规则,都会被进行转发到http://httpbin.org/get
比如:http://localhost:8080/get

  • Query 通过参数进行匹配:
spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - Query=name
image.png

表示请求中一定要有 name 参数才会进行转发,否则不会进行转发。
也可以指定参数和参数的值。
例如参数的 key 为 name,value 必须要以 java 开始

spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - Query=name,java.*
image.png
  • Header 通过请求头匹配
spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - Header=requestId
image.png

表示请求头中一定要有 requestId 参数才会进行转发,否则不会进行转发。也可以指定参数和参数的值。

例如参数的 key 为 requestId,value 必须要为数字


image.png
  • RemoteAddr 通过ip地址匹配
spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - RemoteAddr=47.105.198.54/24
  • host 通过host匹配
spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - Host=**.gongjie.top
  • Cookie 通过Cookie匹配
spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - Cookie=CookieName, .*jie.*

这里判断cookie的 CookieName是否包含"jie" 是的话就路由到对应的服务上去

  • 组合使用
    当它们同时存在于同一个路由时,请求必须同时满足所有的谓词条件才被这个路由匹配。
    注意:一个请求满足多个路由的谓词条件时,请求只会被首个成功匹配的路由转发
spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: http://httpbin.org
          predicates:
            - Query=name,java.*
            - Method=GET
            - After=2021-01-01T01:01:01+08:00[Asia/Shanghai]

Filter

Spring Cloud Gateway 中的过滤器分为两大类:

  • GatewayFilter:应用到单个路由或者一个分组的路由上。
  • GlobalFilter:应用到所有的路由上。


    image.png

AddRequestParameter 过滤器使用:
添加请求参数的过滤器

spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: lb://APPUSER
          filters:
            - AddRequestParameter=name,gong
          predicates:
            - Cookie=CookieName, .*jie.*
image.png

自动带上了参数name并且值为gong。


image.png

如果你有多个服务需要进行转发,配置多个routes规则就行

spring:
  cloud:
    gateway:
      routes:
        - id: gongjie
          uri: lb://APPUSER
          filters:
            - AddRequestParameter=name,gong
          predicates:
            - Cookie=CookieName, .*jie.*
        - id: yuanj
          uri: lb://APPPAY
          filters:
            - AddRequestParameter=name,Pay
          predicates:
            - Method=GET

一个转发到APPUSER,一个转发到APPPAY。


image.png

image.png

image.png

自定义GatewayFilter

/**
 * 统计某个或者某种路由的处理时长
 */
public class customGatewayFilter implements GatewayFilter, Ordered {
    private static final Logger log = LoggerFactory
            .getLogger( customGatewayFilter.class );
    private static final String COUNT_START_TIME = "countStartTime";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //得到当前时间
        Instant now = Instant.now();
        //Instant.now()使用的是UTC时间,会与北京时间相差八小时
        now.plusMillis(TimeUnit.HOURS.toMillis(8));
        //now.toEpochMilli()  毫秒数
        exchange.getAttributes().put(COUNT_START_TIME, now.toEpochMilli());
        return chain.filter(exchange).then(
                Mono.fromRunnable(()->{
                    long startTime = exchange.getAttribute(COUNT_START_TIME);
                    long endTime = (Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8)).toEpochMilli() - startTime);
                    log.info(exchange.getRequest().getURI().getRawPath() + ": " + endTime + "ms");
                })
        );
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

上述代码中,getOrder()方法是来给过滤器设定优先级别的,值越大则优先级越低。需要将自定义的GatewayFilter 注册到router中,代码如下:

 @Bean
    RouteLocator routeLocator(RouteLocatorBuilder builder){
        return builder.routes()
                .route("customGongj",
                        j -> j.path("/get").filters(f -> f.filter(new CustomGatewayFilter())
                                .addRequestHeader("auth","gongjie")).uri("http://httpbin.org")
                )
                .build();
    }

使用psotman测试


image.png

控制台打印

2020-09-13 15:00:28.691  INFO 10364 --- [ctor-http-nio-4] com.gongj.gateway.CustomGatewayFilter    : /get: 28801395ms

自定义过滤器工厂

自定义GatewayFilter又有两种实现方式,一种是上面的直接实现GatewayFilter接口,另一种是自定义过滤器工厂(继承AbstractGatewayFilterFactory类) , 选择自定义过滤器工厂的方式,可以在配置文件中配置过滤器了。

@Component
public class CustomerGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomerGatewayFilterFactory.Config> {
    private static final Logger log = LoggerFactory.getLogger( CustomerGatewayFilterFactory.class );
    private static final String COUNT_START_TIME = "countStartTime";
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (!config.isEnabled()) {
                return chain.filter(exchange);
            }
            exchange.getAttributes().put(COUNT_START_TIME, System.currentTimeMillis());
            return chain.filter(exchange).then(
                    Mono.fromRunnable(() -> {
                        Long startTime = exchange.getAttribute(COUNT_START_TIME);
                        if (startTime != null) {
                            StringBuilder sb = new StringBuilder(exchange.getRequest().getURI().getRawPath())
                                    .append(": ")
                                    .append(System.currentTimeMillis() - startTime)
                                    .append("ms");
                            sb.append(" params:").append(exchange.getRequest().getQueryParams());
                            log.info(sb.toString());
                        }
                    })
            );
        };
    }

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

    public CustomerGatewayFilterFactory() {
        super(Config.class);
        log.info("Loaded GatewayFilterFactory [CustomerGatewayFilterFactory]");
    }
    public static class Config {
        /**
         * 控制是否开启统计
         */
        private boolean enabled;

        public Config() {}

        public boolean isEnabled() {
            return enabled;
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    }
}

application.yml 中 网关路由配置如下:

spring:
  cloud:
    gateway:
      routes:
        - id: customer_route
          uri: http://httpbin.org
          filters:
            - Customer=true
            - AddRequestHeader=auth,gongjie123
          predicates:
            - Method=GET

postman测试


image.png

控制台打印:

2020-09-13 15:19:16.233  INFO 12836 --- [ctor-http-nio-3] c.g.g.CustomerGatewayFilterFactory       : /get: 437ms params:{}

Global filter

Spring Cloud Gateway框架内置的GlobalFilter如下:


image.png

内置的 GlobalFilter 能够满足大多数的需求了,但是如果遇到特殊情况,内置满足不了我们的需求,还可以自定义GlobalFilter。

自定义GlobalFilter

下面的我们自定义一个GlobalFilter,去校验所有请求的请求参数中是否包含“token”,如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。

/**
 * Token 校验全局过滤器
 */
@Component
public class AuthorizeFilter  implements GlobalFilter, Ordered {
        private static final Logger log = LoggerFactory.getLogger( AuthorizeFilter.class );

        private static final String AUTHORIZE_TOKEN = "token";

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            String token = exchange.getRequest().getQueryParams().getFirst( AUTHORIZE_TOKEN );
            if ( StringUtils.isBlank( token )) {
                log.info( "token is empty ..." );
                exchange.getResponse().setStatusCode( HttpStatus.UNAUTHORIZED );
                return exchange.getResponse().setComplete();
            }
            return chain.filter(exchange);
        }

        @Override
        public int getOrder() {
            return 0;
        }
}

执行不带token的请求


image.png

控制台打印

2020-09-13 15:26:44.540  INFO 4608 --- [ctor-http-nio-3] com.gongj.gateway.AuthorizeFilter        : token is empty ...

执行带token的请求


image.png

控制台打印

2020-09-13 15:28:59.125  INFO 4608 --- [ctor-http-nio-4] c.g.g.CustomerGatewayFilterFactory       : /get: 533ms params:{token=[gggg]}

代码参考:https://blog.51cto.com/13698036/2395413
大佬江南一点雨个人博客:http://www.javaboy.org/
必读的官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-cloud-gateway.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容