Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
为什么需要网关
在微服务架构中,网关(Gateway)作为统一入口,承担着多个关键角色。
核心功能特性
- 请求路由:网关根据请求的路径、方法和其他参数,将请求路由到相应的微服务实例。这种集中管理的方式简化了客户端与多个微服务之间的交互。
- 权限控制:网关可以实现用户身份验证和授权,确保只有经过授权的用户才能访问特定的服务。这提供了一个安全层,保护后端服务不受未授权访问。
- 限流:为了防止系统过载,网关可以设置请求限制,以控制流量。例如,可以限制每秒的请求数量,确保服务的稳定性和可靠性。
架构图:
权限控制:
- 网关负责校验用户请求的资格,确保只有经过身份验证和授权的用户才能访问特定的微服务。如果未授权,网关将拦截请求并返回相应的错误信息。
路由和负载均衡:
- 所有请求都必须经过网关,网关根据预设的规则将请求转发到合适的微服务。这一过程称为路由。
- 当多个服务实例可用时,网关还需要执行负载均衡,以分散流量并提高系统的可用性和响应速度。
限流:
- 在高流量情况下,网关通过限制请求速率来保护后端服务,确保它们不会因过载而崩溃。这通常基于下游服务的处理能力进行调整,以平衡请求流量。
Spring Cloud中的网关实现
Zuul:
- 类型:基于Servlet的阻塞式编程。
- 特点:提供路由、负载均衡和过滤功能,但由于其阻塞特性,在高并发场景下可能会导致性能瓶颈。
- 适用场景:适合简单的微服务应用,但在对性能要求较高的情况下可能不够理想。
Spring Cloud Gateway:
- 类型:基于Spring 5中提供的WebFlux,响应式编程。
- 特点:具备更好的性能,能够处理更多的并发请求,支持非阻塞IO。提供了更灵活的路由和过滤功能,并且集成了Spring生态中的其他组件。
- 适用场景:适合需要高性能、高并发的微服务架构,是现代微服务应用的推荐选择。
gateway快速入门
第一步:添加依赖
<!-- 不可以引入spring-boot-starter-web会有冲突 -->
<!--网关-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>4.1.0</version>
</dependency>
第二步:配置好注册中心[按照Eureka、zookeeper、nacos客户端配置即可]
第三步:编写properties
# 服务端口
server.port=8082
# 服务名称
spring.application.name=service2
# Nacos 服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# 启用 Sentinel,并连接 Sentinel Dashboard
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080
spring.cloud.sentinel.transport.port=8720 # 本地 Sentinel 客户端的监控端口
# 启用 Sentinel
spring.cloud.sentinel.enabled=true
# 网关配置
# 定义路由的唯一标识符
spring.cloud.gateway.routes[0].id=route_to_service2
# 设置目标服务的URI
spring.cloud.gateway.routes[0].uri=http://localhost:8082
# 配置路由的断言,这里指定路径匹配规则
spring.cloud.gateway.routes[0].predicates[0]=Path=/service2/**
启动网关服务
然后可以通过访问如下 URL 来测试路由功能:
http://localhost:8082/service2/test
网关路由的流程图
整个访问的流程如下:
断言工厂
在 Spring Cloud Gateway 中,断言工厂(Predicate Factory)用于定义请求路由的条件。它们通过处理配置文件中的字符串,来决定哪些请求应该被转发到具体的服务。
常用断言工厂
名称 | 说明 | 示例 |
---|---|---|
After | 匹配在某个时间点后的请求 | After=2037-01-20T17:42:47.789-07:00[America/Denver] |
Before | 匹配在某个时间点之前的请求 | Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
Between | 匹配在两个时间点之间的请求 | Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
Cookie | 请求必须包含某些 cookie | Cookie=chocolate, ch.p |
Header | 请求必须包含某些 header | Header=X-Request-Id, \d+ |
Host | 请求必须访问特定的 host(域名) | Host=**.somehost.org,**.anotherhost.org |
Method | 请求方式必须是指定方式 | Method=GET,POST |
Path | 请求路径必须符合指定规则 | Path=/red/{segment},/blue/** |
Query | 请求参数必须包含指定参数 | Query=name, Jack 或 Query=name |
RemoteAddr | 请求者的 IP 必须在指定范围 | RemoteAddr=192.168.1.1/24 |
Weight | 权重处理 |
使用示例
以 Path 断言工厂为例,假设我们希望将所有访问 /user/** 的请求转发到某个服务,可以在 application.properties 文件中这样配置:
spring.cloud.gateway.routes[0].id=user_route
spring.cloud.gateway.routes[0].uri=http://localhost:8080
spring.cloud.gateway.routes[0].predicates[0]=Path=/user/**
虽然有多种断言工厂可供使用,但熟悉 Path 断言工厂通常足以应对大多数路由需求。
过滤器工厂
GatewayFilter 是 Spring Cloud Gateway 提供的一种机制,用于处理进入网关的请求以及微服务返回的响应。
路由过滤器的种类
Spring 提供了多种路由过滤器工厂,常用的包括:
名称 | 说明 |
---|---|
AddRequestHeader | 给当前请求添加一个请求头 |
RemoveRequestHeader | 移除请求中的一个请求头 |
AddResponseHeader | 给响应结果中添加一个响应头 |
RemoveResponseHeader | 从响应结果中移除一个响应头 |
RequestRateLimiter | 限制请求流量 |
请求头过滤器示例
以 AddRequestHeader 为例,假设我们的需求是给所有进入 userservice 的请求添加一个请求头:Truth=Itcast is freaking awesome!。可以在 application.yml 文件中这样配置:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
filters: # 过滤器
- AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
通过这种方式,该过滤器仅对访问 userservice 的请求生效。
默认过滤器
如果希望某个过滤器对所有路由都生效,可以将过滤器配置在 default-filters 下:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
default-filters: # 默认过滤项
- AddRequestHeader=Truth, Itcast is freaking awesome!
总结
过滤器的作用是什么?
- 对路由的请求或响应进行加工处理,例如添加请求头。
- 配置在特定路由下的过滤器仅对该路由的请求生效。
default-filters 的作用是什么?
- default-filters 中的过滤器会对所有路由生效。
全局过滤器
全局过滤器是 Spring Cloud Gateway 中的一种机制,用于对所有请求和响应进行统一的处理。与路由特定的过滤器不同,全局过滤器可以应用于网关中的所有路由,提供了一种集中管理请求和响应的方法。
全局过滤器作用
全局过滤器用于处理所有进入网关的请求和微服务响应,功能与GatewayFilter类似。不同之处在于GatewayFilter通过配置定义,其处理逻辑是固定的;而GlobalFilter的逻辑需要开发者自行编写代码实现。
实现方式是通过实现GlobalFilter接口:
public interface GlobalFilter {
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
在filter方法中可以编写自定义逻辑,实现如下功能:
- 登录状态判断
- 权限校验
- 请求限流等
自定义全局过滤器
需求
定义一个全局过滤器,拦截请求,判断请求参数是否符合以下条件:
- 参数中是否有authorization
- authorization参数值是否为admin
- 如果满足这两个条件,则放行;否则拦截请求。
实现
在网关中定义一个过滤器:
package cn.itcast.gateway.filters;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取请求参数
MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
// 2.获取authorization参数
String auth = params.getFirst("authorization");
// 3.校验
if ("admin".equals(auth)) {
// 放行
return chain.filter(exchange);
}
// 4.拦截
// 4.1.禁止访问,设置状态码
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
// 4.2.结束处理
return exchange.getResponse().setComplete();
}
}
过滤器执行顺序
请求进入网关后,会经过三类过滤器:
- 当前路由的过滤器
- DefaultFilter
- GlobalFilter
请求路由后,会将这些过滤器合并成一个过滤器链,并按照顺序依次执行。
排序规则
- 每个过滤器必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
- GlobalFilter通过实现Ordered接口或使用@Order注解来指定order值。
- 路由过滤器和defaultFilter的order由Spring自动指定,默认按声明顺序递增。
- 当多个过滤器的order值相同时,执行顺序为:defaultFilter > 路由过滤器 > GlobalFilter。
更多详细内容,可查看源码:
- org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法加载defaultFilters和路由的filters并进行合并。
- org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链。
跨域问题
什么是跨域问题
跨域指的是在不同的域名、端口或协议之间进行请求。具体包括以下几种情况:
- 域名不同:例如 www.taobao.com 与 www.taobao.org,以及 www.jd.com 和 miaosha.jd.com。
- 端口不同:例如 localhost:8080 和 localhost:8081。
- 协议不同:例如 http:// 与 https://。
跨域问题是指浏览器出于安全考虑,禁止发起来自不同域的 AJAX 请求,因此请求会被浏览器拦截。
解决方案:CORS(跨域资源共享),可以参考阮一峰的网络日志了解更多。
解决跨域问题
在 Gateway 服务的 application.yml 文件中,添加以下配置,以实现全局的跨域处理:
spring:
cloud:
gateway:
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决 OPTIONS 请求被拦截问题
corsConfigurations:
'[/**]': # 所有路径的跨域配置
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域 AJAX 请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带 cookie
maxAge: 360000 # 这次跨域检测的有效期
总结
通过配置 CORS,可以有效解决跨域请求的问题,使得不同源之间的 AJAX 请求能够顺利进行。