Spring Cloud Gateway 的核心功能与配置详解
Spring Cloud Gateway 是 Spring Cloud 生态系统中的一个核心组件,专为现代微服务架构设计的 API 网关解决方案。它以 Spring Framework 和 Spring Boot 为基础,旨在为开发者提供一个高性能、易扩展、简单易用的网关工具,用于管理微服务流量,执行安全验证,提供动态路由和过滤功能。
写这篇博客的起因是在昨天的面试中,面试官问我项目中使用的Spring Cloud Gateway能否在tomcat服务器上使用,我却一时半会儿答不出来,感到很惭愧。
正确的答案是不能。因为Spring Cloud Gateway需要由Spring Boot和Spring Webflux提供的Netty运行时,它不适用于传统的Servlet容器,例如Tomcat、Jetty、Undertow等。传统的Servlet应用通常会以WAR包的形式部署到应用服务器,但 Spring Cloud Gateway 依赖的是 Netty,而不是 Servlet 容器,因此它无法以 WAR 包部署。
什么是Netty?
Netty 是一个由 Java 编写的异步事件驱动的网络应用框架,专为快速开发高性能、高可扩展性的网络应用而设计。它是一个开源项目,提供了对网络协议(如 TCP/IP、HTTP、WebSocket 等)的抽象封装,使得开发者可以专注于业务逻辑,而无需深入了解底层网络通信的复杂细节。
Netty之所以能够支持快速开发高性能、高扩展性的网络应用,其原因主要有如下几点:
- 使用非阻塞I/O:Netty基于Java NIO,使用多路复用技术,一个线程可以同时处理多个连接的I/O操作。当某个连接没有可用数据时,线程不会阻塞,而是继续处理其他的任务,这种模型通过减少线程数量和阻塞等待,提高了并发处理能力。而传统的Servlet容器,则使用阻塞I/O,每个客户端连接由一个独立的线程处理,当线程阻塞(等待数据读取或者写入)的时候,它无法执行其他任务,需要为每个连接分配一个线程,随着连接数的增加,线程的数量也增加,线程的上下文切换和资源占用也就成为了性能的瓶颈。
- 使用线程池模型:Netty使用EventLoop的线程池机制,通常只需要少量的I/O线程就能够处理大量的连接,I/O操作和业务逻辑分开执行,不同阶段的任务被分配到了不同的线程池当中,可以最大化CPU和资源的利用率。而传统的Servlet容器使用的是per-request模型,每个请求都占用一个线程,在请求数量增加的时候会导致大量的线程创建和销毁,增加线程的上下文切换。
- 异步数据处理:Netty的数据读取写入是异步的,操作完成以后通过回调通知处理,即使数据传输耗时较长,Netty也不会阻塞线程,而是允许其他任务继续执行。而Servlet的数据读取和写入时同步的,服务器必须等待数据完全就绪以后再进行处理,在此期间线程会被阻塞。
因为Netty的高性能、异步非阻塞架构和强大的扩展性,能够很好地满足现代微服务网关Spring Cloud Gateway的高并发、低延迟和动态路由等需求。
Spring Cloud Gateway工作原理
客户端向Spring Cloud Gateway发出请求,如果Gateway Handler Mapping确定请求与路由匹配,则会将其发送到Gateway Web Handler。该处理程序通过特定于请求的过滤器链运行请求。过滤器可以在发送请求之前和之后运行逻辑。首先会执行所有的“前置(pre)”过滤器逻辑,然后发起代理请求。在代理请求完成后,再执行“后置(post)”过滤器逻辑。
配置Route Predicate Factories与GatewayFilter Factories
Spring Cloud Gateway支持Route Predicate Factories和GatewayFilter Factories。这两种方式都支持快捷方式配置以及使用完全扩展的参数。
快捷方式配置由过滤器名称识别,其后跟上等号以及使用逗号分隔的参数值,示例如下。
使用两个参数定义Cookie
路由谓词工厂:cookie 名称mycookie
和匹配mycookievalue
值:
而使用参数则更像是标准的yaml配置,通常会有一个name
键和args
键,args
键用于配置谓词或者过滤器的键值对的映射:
Route Predicate Factories
按照核心功能及其应用场景,可以大致将Route Predicate Factories分为以上几类。
具体的功能如下:
After
:匹配指定时间之后的请求。Before
:匹配指定时间之前的请求。Between
:匹配指定时间范围内的请求。Cookie
:匹配指定名称和值的 Cookie。Header
:匹配指定 Header 和正则表达式。Host
:匹配 Host 名称(支持 Ant 风格模式)。Method
:匹配 HTTP 请求方法(如 GET、POST)。Path
:匹配请求路径(支持 URI 模板变量)。Query
:匹配请求中的查询参数及其值。RemoteAddr
:基于 CIDR 格式匹配远程地址。XForwardedRemoteAddr
:基于X-Forwarded-For
Header 匹配远程地址。Weight
:按照组的权重比例分配流量。
使用的样例可以参考:Route Predicate Factories。
GatewayFilter Factories
其对应的功能如下:
- 修改请求头:
AddRequestHeader
:添加请求头。AddRequestHeadersIfNotPresent
:如果请求头不存在,则添加。SetRequestHeader
:设置请求头(替换已有的)。RemoveRequestHeader
:移除指定请求头。MapRequestHeader
:从一个请求头映射到另一个请求头。SetRequestHostHeader
:设置请求的 Host 头。
- 修改请求参数:
AddRequestParameter
:添加请求参数。RemoveRequestParameter
:移除请求参数。RewriteRequestParameter
:重写请求参数。
- 修改请求路径:
PrefixPath
:为路径添加前缀。SetPath
:设置请求路径。StripPrefix
:移除路径前缀。RewritePath
:使用正则表达式重写路径。
- 修改请求体:
CacheRequestBody
:缓存请求体。ModifyRequestBody
:修改请求体内容。
- 修改响应头:
AddResponseHeader
:添加响应头。SetResponseHeader
:设置响应头(替换已有的)。RemoveResponseHeader
:移除指定响应头。RewriteResponseHeader
:使用正则表达式重写响应头。DedupeResponseHeader
:去重响应头。
- 修改响应体:
ModifyResponseBody
:修改响应体内容。RemoveJsonAttributesResponseBody
:从 JSON 响应体中移除指定属性。
- 限流:
RequestRateLimiter
:基于 Redis 的请求限流。RequestHeaderSize
:限制请求头大小。RequestSize
:限制请求体大小。
- 熔断:
CircuitBreaker
:使用熔断器保护路由。FallbackHeaders
:在熔断回退中添加异常信息到请求头。
- 重试:
Retry
:失败后重试请求。
- 安全增强:
SecureHeaders
:添加安全相关的响应头。SaveSession
:在转发请求之前保存会话状态。
- OAuth2 支持:
TokenRelay
:转发 OAuth2 令牌到下游服务。
- 重定向:
RedirectTo
:重定向到指定 URL。
- 路径调整:
RewriteLocationResponseHeader
:调整响应中 Location 头的值。
具体的使用案例依然可以参考:GatewayFilter Factories。
用好以上过滤器,就能够获得一个功能及其强大的网关。
Spring Cloud Gateway动态路由
在微服务成为流行的当下,一个服务常常会有多个实例,这个时候就需要使用动态路由,此处以Nacos注册中心为例说明。
Nacos 作为服务注册和配置中心,存储路由配置信息。网关从 Nacos 中获取最新的路由配置。Spring Cloud Gateway通过监听Nacos配置变更事件,在路由配置更新实时刷新网关的路由信息,在应用启动的时候,网关从Nacos加载路由规则。
先引入以下依赖:
而后在application.yml
中配置Nacos的连接信息:
在Nacos配置中心中创建一个配置文件,例如gateway-routes.yaml
,并在其中定义路由规则。确保该配置的Data ID与应用名称和配置文件名称匹配。
而后在启动类上加上@EnableDiscoveryClient
注解,启用Nacos配置和发现功能,最后还需要再在application.yml
中加上网关发现功能:
Spring Cloud Gateway负载均衡
Spring Cloud Gateway的负载均衡主要依赖于Spring Cloud LoadBalancer,引入以下依赖:
在相应的网关服务的application.yml中写入以下配置:
lb://example-service
就是对example-service
使用了负载均衡的意思,使用负载均衡之后,请求会被均衡负载到多个实例当中。默认的负载均衡策略为轮询,目前Spring Cloud Loadbalancer内置了两种负载均衡策略:
- 轮询(Round Robin):按照顺序依次选择每个服务实例,简单易用,负载均匀,适合服务实例性能相近且流量需求较为平均的场景。
- 随机(Random):随机选择一个服务实例,实现比较简单,适用于小规模系统,但是不能够考虑实例的实际负载。
需要切换的时候,只需要在配置文件中更改:
此外,还有其他的负载均衡策略,但是需要自己去实现:
- 加权轮询(Weighted Round Robin):给每一个实例分配权重,权重高的实例会分配更多请求。适用于后端服务实例配置不同、需要优先将更多流量引导到特定服务实例、蓝绿部署的场景。
- 最小连接数(Least Connections):选择当前活跃连接数最少的实例,适用于连接数较长或有显著差异的服务,确保流量分配更加均衡。适用于流媒体服务、后端服务处理时间或连接特性差异明显的场景。
- 基于哈希(Hash-Based):根据请求的特定属性计算哈希值,将请求始终分配到固定的实例,常用的有IP哈希以及用户哈希。使用的场景为状态敏感的系统(如用户购物车、用户认证)、适用于一致性哈希的缓存系统以及需要基于用户地理位置的系统。
- ...
Spring Cloud Gateway 是现代微服务架构中强大的网关解决方案,凭借灵活的路由规则、多样化的负载均衡策略和高性能的 Netty 支持,能够满足多种复杂场景需求。通过合理配置和实践应用,可以充分发挥其能力,为系统的高效稳定运行提供坚实保障。