写这篇博客的起因是在昨天的面试中,面试官问我项目中使用的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 FactoriesRoute 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:按照组的权重比例分配流量。修改请求头 :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:在熔断回退中添加异常信息到请求头。重试 :安全增强 :SecureHeaders:添加安全相关的响应头。SaveSession:在转发请求之前保存会话状态。OAuth2 支持 :TokenRelay:转发 OAuth2 令牌到下游服务。重定向 :路径调整 :RewriteLocationResponseHeader:调整响应中 Location 头的值。 用好以上过滤器,就能够获得一个功能及其强大的网关。
在微服务成为流行的当下,一个服务常常会有多个实例,这个时候就需要使用动态路由,此处以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 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 支持,能够满足多种复杂场景需求。通过合理配置和实践应用,可以充分发挥其能力,为系统的高效稳定运行提供坚实保障。
spring :
cloud :
gateway :
routes :
- id : after_route
uri : https://example.org
predicates :
- Cookie=mycookie,mycookievalue spring :
cloud :
gateway :
routes :
- id : after_route
uri : https://example.org
predicates :
- name : Cookie
args :
name : mycookie
regexp : mycookievalue implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery'
implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config' spring :
application :
name : gateway-service
cloud :
nacos :
discovery :
server-addr : 127.0.0.1:8848
config :
server-addr : 127.0.0.1:8848
file-extension : yaml spring :
cloud :
gateway :
routes :
- id : user-service
uri : lb://user-service
predicates :
- Path=/user/**
filters :
- StripPrefix=1
- id : order-service
uri : lb://order-service
predicates :
- Path=/order/**
filters :
- StripPrefix=1 spring :
cloud :
gateway :
discovery :
locator :
enabled : true
lower-case-service-id : true implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer' spring :
cloud :
gateway :
routes :
- id : service-route
uri : lb://example-service
predicates :
- Path=/api/** # 匹配 /api/ 开头的路径 spring :
cloud :
loadbalancer :
configurations :
example-service : # 这里是服务名称
hint : random # 或者round-robin