Gateway

1.1 网关介绍

首先要清楚网关的定位,他是后端服务的入口,有请求想要访问你的订单,或者库存,或者是其他服务时,必须要经过网关将请求转发过去。

至于入口安全的策略做到位,后面的基本都安全。

So,可以在网关位置做好统一的鉴权,限流,安全机制的操作。

一般网关的实现方式很多,一般我认为网关有两类:

1、面向用户的

  • 面向用户的网关中间件,就是Nginx。

  • Nginx的并发能力非常强,用户请求可以先达到Nginx,再由Nginx转发到其他的服务中。

  • Nginx也不是SpringCloud生态,跟注册中心整合成本很高,其次Nginx也没有办法去基于Java编写具体的逻辑业务。

2、后端入口的

  • 后端入口,一般就是咱们现在要学的Gateway,当然还有Netflix开源的Zuul

  • Gateway相对Nginx并发能力是比较差的。

  • Gateway本身就是SpringCloud生态中的一个组件,他可以直接跟注册中心整合,基于服务名直接获取到服务的元数据。

  • Gateway还提供了各种Filter,可以在请求进来,以及响应之前做各种操作。


这里需要说一下之前SpringCloud集成的Zuul。

Zuul采用的是Tomcat容器,使用的是非常传统的Servlet IO的处理模型。

Servlet本身是一个非常简单的网络IO模型,当请求进入到Web服务时,Web服务会给他分配一个线程(Web容器线程池里拿的),在并发不高的时候,没任何问题。

如果并发比较高的话会导致线程变多。

  • 线程占用内存资源,如果并发特别大,线程池又没控制,会导致内存资源占用较多。

  • 线程太多,CPU的资源都浪费在了大量的线程之间切换中。

所以在并发比较高的情况下,不希望网关采用传统的Servlet IO模型,不要给每一个请求都分配一个线程。

问题得知了,Gateway自然没有采用这个方案。


So,Gateway他底层使用的是Spring Framework里提供的一个WebFlux组件。WebFlux模型替换了旧的Servlet IO模型。用少量的线程处理request和response,而这种线程可以称为Loop线程。

而具体的业务逻辑代码,Gateway内部是基于WebFlux里面使用的Reactive Streams这种异步编程的方式去处理。


而处理请求的那个Loop线程,就可以对应上Reactor模型中的Reactor线程。而基于这种模型实现的高性能的通讯框架,最常见的,就是Netty。而Reactor线程对应Netty就可以理解为是EventLoop线程。


先不纠结他的底层,就可以理解为,WebFlux处理请求,就可以上Netty。

基于上述性能基本上是最强的Netty来通讯,再结合Reactive Streams这种异步编程。

Gateway的性能远高于之前的Zuul,并发能力更强。

Ps:虽然Zuul的新版本也使用的Netty作为底层的Reactor模型实现,但是,SpringCloud不跟他玩了。。。自己搞了一个Gateway。

1.2 Gateway介绍

Gateway作为网关,依然是做请求的转发和过滤

复制代码
Spring Cloud Gateway的特点:
基于Spring Framework 5、Project Reactor和Spring Boot 2.0构建
能够匹配任何请求属性上的路由。
在请求转发时,可以根据请求所携带的任何报文作为请求转发的要求…………
谓词和过滤器是特定于路由的。
谓词就是配置转发的需要编写的内容, 过滤器就是可以自己编写的一些逻辑。
Hystrix断路器集成。
可以集成Sentinel
Spring Cloud DiscoveryClient集成
可以集成Nacos
易于编写的谓词和过滤器
写着方便,可以在配置文件里写,也可以写在Java代码里。
请求速率限制
限流功能,Gateway提供了这种Filter,除了这种还有很多其他…………
路径重写
可以在转发请求前,以及响应数据前,对各种报文做修改

其次,咱们还需要了解一些Gateway的相关术语。

复制代码
专业术语:
Route(路由):
网关内部的一个核心机制,他由ID、目标URI、谓词集合和过滤器集合。
当谓词匹配上后,会经过Filter,转发请求到目标URI。
​
Predicate(断言、谓词): 
他可以匹配HTTP请求中锁携带的所有报文信息。基于某几个作为匹配的规则,比如请求头,请求参数,甚至cookie等……
​
Filter(过滤器):
可以在发送下游请求之前或之后,修改请求或响应。

下图是Gateway的底层请求流转过程。

1、三个角色

  • 客户端

  • Gateway

  • 目标服务

2、整个流程

  • 客户端请求发送到Gateway

  • Gateway基于HandlerMapping确认请求能否匹配谓词。如果不匹配,404......

  • 如果匹配请求交给Web Handler处理,经过一个过滤器链的前置处理。

  • 将请求转发到具体的目标服务,目标服务处理好逻辑,再响应给Gateway。

  • 再经过Gateway的过滤器链的后置处理,依次响应,最终响应给客户端。

1.3 Gateway初体验

1、启动好目标服务,确保目标服务的接口可以单独访问。

2、创建gateway项目。

3、导入依赖。

复制代码
<dependencies>
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-gateway</artifactId>
 </dependency>
 <dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-loadbalancer</artifactId>
 </dependency>
</dependencies>

4、创建启动类

复制代码
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayStarterApp {
​
 public static void main(String[] args) {
     SpringApplication.run(GatewayStarterApp.class, args);
 }
}

5、编写配置文件

复制代码
spring:
application:
 name: gateway
cloud:
 gateway:
   discovery:
     locator:
       # 开启默认的路由规则,会根据注册中心中的服务名作为路径实现理由规则
       enabled: true
server:
port: 80

6、测试效果

http://localhost/order/order/info

http://localhost /order /order/info

  • http://localhost 代表访问gateway服务

  • /order: 代表路由的服务是订单服务

  • /order/info: 代表访问/order/info接口

1.4 自定义路由配置

其实就是基于断言去实现请求进来后的路由规则。

1.4.1 yml方式

查看好路由的规则

复制代码
spring:
application:
 name: gateway
cloud:
 gateway:
   discovery:
     locator:
       # 开启默认的路由规则,会根据注册中心中的服务名作为路径实现理由规则
       enabled: false
   routes:
        - id: path_route
          uri: http://localhost:9090/
          predicates:
            - Path=/order/**
          # http://localhost/order/order/info  路由到    http://localhost:9090/order/order/info
          # http://localhost/order/info   路由到    http://localhost:9090/order/info

1.4.2 配置类方式

复制代码
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 GatewayRoutesConfig {
​
    @Bean
    public RouteLocator pathRoute(RouteLocatorBuilder routeLocatorBuilder) {
        //1、基于routeLocatorBuilder获取构建route的Builder
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        //2、设置route
        return routes.route("path_route",route -> route.path("/order/**").uri("http://localhost:9090/")).build();
        /*
        #        - id: path_route
        #          uri: http://localhost:9090/
        #          predicates:
        #            - Path=/order/**
        */
    }
​

1.4.3 负载均衡

之前的Gateway自带的基于服务名的路由和负载方式都是默认提供的。

自定义配置,想去找注册中心,并且实现负载均衡,只需要修改uri即可。

配置类的修改:

复制代码
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 GatewayRoutesConfig {
​
 @Bean
 public RouteLocator pathRoute(RouteLocatorBuilder routeLocatorBuilder) {
     //1、基于routeLocatorBuilder获取构建route的Builder
     RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
     //2、设置route  将uri修改为lb://服务名即可
     return routes.route("path_route",route ->      route.path("/order/**").uri("lb://order")).build();
 }
​
}
​

yml方式

复制代码
spring:
cloud:
 gateway:
   routes:
        - id: path_route
          #  这个位置  ,lb代表负载均衡
          uri: lb://order
          predicates:
            - Path=/order/**

1.5 断言

1.5.1 断言介绍

Gateway中内置了很多中断言,并且每个route可以配置多个断言,如果配置了多个,那就必须满足所有断言才会将请求路由到对应的服务。


断言种类很多,一个一个先聊一下作用,然后随后挑几个玩一下。

  1. After:匹配具体时间后的请求,才可以做路由。

  2. Before:匹配具体时间前的请求......

  3. Between:配置时间范围内的请求......

  4. Cookie:请求携带cookie,只要匹配cookie配置中指定的正则即可理由......

  5. Header:请求携带请求头,只要匹配header设置的正则即可......

  6. Host:匹配当前请求是否来自于设置的主机(域名)

  7. Method:匹配请求方式......

  8. Path:匹配请求路径......

  9. Query:匹配请求参数......

  10. RemoteAddr:匹配请求来源的IP地址......

  11. Weight:指定路由时的权重,两个参数,分组group,权重weight

  12. XForWardedRemoteAddr:匹配请求头的x-for-warded或者remoteAddr,查看请求来源......

1.5.2 断言测试

After:在xxx时间之后才可以正常的路由

复制代码
spring:
cloud:
 gateway:
   routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
#            - After=2025-07-21T19:30:25.789+08:00[Asia/Shanghai]
#            - After=2026-05-16T19:30:25.789+08:00[Asia/Shanghai]

Header:请求头里必须携带xxx=yyy

复制代码
spring:
cloud:
 gateway:
   routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
            - Header=X-Request-Id, \d+

Method:请求方式的匹配

复制代码
spring:
cloud:
 gateway:
   routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
            - Method=GET

............

1.6 Filter

Filter是在请求到Gateway服务后,基于断言确认请求可以转发,之后在转发请求的前后,会经过Filter链。

分成两块去玩:

  • 玩Gateway提供的一些Filter

  • 自定义Filter

1.6.1 Gateway自带的Filter

Gateway自带的Filter有点多,一个一个玩的意义不大,就玩4个最常用的就得了,别的不碰了。

1.6.1.1 AddRequestHeader

在请求转发到目标服务之前,追加一个请求头信息

复制代码
spring:
cloud:
 gateway:
   routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
          filters:
            - AddRequestHeader=X-Request-red, blue
1.6.1.2 AddRequestParameter

在请求转发到目标服务之前,追加一个请求参数

复制代码
spring:
cloud:
 gateway:
   routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
          filters:
            - AddRequestHeader=X-Request-red, blue
            - AddRequestParameter=red, blue
1.6.1.3 AddResponseHeader

在响应数据给客户端之前,追加一个响应头的信息

复制代码
spring:
cloud:
 gateway:
   routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
          filters:
            - AddRequestHeader=X-Request-red, blue
            - AddRequestParameter=red, blue
            - AddResponseHeader=X-Response-Red, Blue
1.6.1.4 StripPrefix

在请求路径打到Gateway后,如果断言匹配后。

可以忽略掉客户端请求地址后的几个路径。

复制代码
spring:
cloud:
 gateway:
   routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
          filters:
            - AddRequestHeader=X-Request-red, blue
            - AddRequestParameter=red, blue
            - AddResponseHeader=X-Response-Red, Blue
            - StripPrefix=1
          # http://localhost/order/order/info  路由到    http://localhost:9090/order/info
          # http://localhost/order/info   路由到    http://localhost:9090/info
1.6.1.5 DefaultFilter

前面玩Filter都是基于某一个route单独配置,如果有一些Filter,需要全局都指定的话,可以基于DefaultFilter配置,所有的route都会走这个defaultFilter的规则。

复制代码
spring:
cloud:
 gateway:
   default-filters:
        - AddRequestHeader=X-Request-red, blue
        - AddRequestParameter=red, blue
        - AddResponseHeader=X-Response-Red, Blue
        - StripPrefix=1

1.6.2 自定义过滤器

前面玩的都是Gateway自带的过滤器,如果想实现自定义的过滤器,非常简单,只需要创建好类,实现对应的接口,重新方法,在方法里追加逻辑即可。

需要实现两个接口

  • GlobalFilter:重写filter方法,在内部基于exchange可以拿到请求报文和响应报文的信息,在filter方法内部可以做限流啊,鉴权啊等等操作。

    • 方法返回exchange.getResponse().setComplete(); 拦截请求。

    • 方法返回chain.filter(exchange); 放行操作。

  • Ordered:指定多个过滤器之间的执行顺序,返回的数值越小,优先级越高。

java 复制代码
import com.alibaba.nacos.common.utils.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;

/**
 * 实现Gateway的自定义过滤器,并且实现参数校验。
 */
@Component
public class ParameterInvalidateFilter implements GlobalFilter, Ordered {

    // 要求必须传递一个参数,   key=value, 要求value不允许为空,也不允许为空串,否则400的错误
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 具体的逻辑在这些。
        String value = exchange.getRequest().getQueryParams().getFirst("key");
        // 判断
        if(StringUtils.isEmpty(value)){
            System.out.println("参数异常!!!");
            exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
            // 拦截住,直接不往下走Filter了。
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    // 返回的数值越小,优先级越高。
    public int getOrder() {
        return 0;
    }
}
相关推荐
Reggie_L40 分钟前
spring-cloud概述
java
遗憾随她而去.1 小时前
js面试题 高频(1-11题)
开发语言·前端·javascript
贾修行1 小时前
深入浅出理解 Reactor:响应式编程的利器
java·reactor
Dxy12393102164 小时前
Python观察者模式详解:从理论到实战
开发语言·python·观察者模式
hqxstudying4 小时前
J2EE模式---前端控制器模式
java·前端·设计模式·java-ee·状态模式·代码规范·前端控制器模式
不写八个6 小时前
GoLang教程005:switch分支
开发语言·后端·golang
ZeroToOneDev6 小时前
Java(LinkedList和ArrayList底层分析)
java·开发语言
吃饭只吃七分饱7 小时前
C++第8章:IO库
开发语言·c++
没有bug.的程序员7 小时前
JAVA面试宝典 -《 架构演进:从单体到 Service Mesh》
java·面试·架构
宴之敖者、8 小时前
数组——初识数据结构
c语言·开发语言·数据结构·算法