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可以配置多个断言,如果配置了多个,那就必须满足所有断言才会将请求路由到对应的服务。
断言种类很多,一个一个先聊一下作用,然后随后挑几个玩一下。
After:匹配具体时间后的请求,才可以做路由。
Before:匹配具体时间前的请求......
Between:配置时间范围内的请求......
Cookie:请求携带cookie,只要匹配cookie配置中指定的正则即可理由......
Header:请求携带请求头,只要匹配header设置的正则即可......
Host:匹配当前请求是否来自于设置的主机(域名)
Method:匹配请求方式......
Path:匹配请求路径......
Query:匹配请求参数......
RemoteAddr:匹配请求来源的IP地址......
Weight:指定路由时的权重,两个参数,分组group,权重weight
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;
}
}