Spring Cloud Gateway(分发请求)
Spring Cloud Gateway 的过滤器和 Spring MVC 的拦截器的区别
过滤器用于整个微服务系统的网关层控制,拦截器则用于单个微服务内部的控制层请求处理。
1. 作用范围
-
Spring Cloud Gateway 过滤器:过滤器的作用范围是在网关层,主要在请求进入后端服务之前和响应返回客户端之前执行。它处理的是网关中的所有流量,可以对来自客户端的请求进行预处理,并决定是否转发给后端服务,适用于微服务架构中的网关场景。
-
Spring MVC 拦截器:拦截器的作用范围是在单个微服务或应用内部的控制层(Controller)请求,作用于处理 HTTP 请求的 Spring MVC 控制器(Controller)之前和之后,主要用于单个服务的请求处理,不适用于跨服务请求的场景。
2. 执行时机
-
过滤器:过滤器在请求到达后端服务前执行,可以修改请求的路径、请求头、响应头等内容。一般分为 Pre Filter(请求前过滤器)和 Post Filter(响应后过滤器),可以决定是否拦截请求或修改响应内容。过滤器通常对整个网关服务的请求流进行控制,适用于负载均衡、路由分发和限流等。
-
拦截器:拦截器在单个服务的控制器方法调用前和调用后执行,常用于身份验证、日志记录、会话管理等功能,控制请求在具体 Controller 层的行为。拦截器无法修改网关层的流量请求,也无法对整个微服务系统中的请求流量进行统一管理。
3. 使用场景
-
过滤器的使用场景:
- 路由:将请求分发到不同的后端微服务。
- 身份认证和授权:在请求到达后端服务之前对请求进行身份验证或添加认证信息。
- 限流与熔断:对高并发场景的请求流量进行限流、熔断保护。
- 日志记录:记录跨服务的请求和响应日志,便于追踪和监控。
- 添加、修改请求或响应头:在请求进入网关时或响应返回时修改头信息。
-
拦截器的使用场景:
- 权限检查:在控制器执行前,检查用户的权限是否符合业务要求。
- 参数验证:在请求到达控制器前,验证请求参数的合法性。
- 日志记录:记录进入控制器的请求信息和返回结果,用于调试和监控。
- 异常处理:在请求完成后统一处理和记录异常情况。
4. 配置方式与依赖关系
-
过滤器:过滤器是 Spring Cloud Gateway 的核心功能,配置在网关服务的
application.yml
中,并且可以配置全局过滤器或局部过滤器,作用于网关中的所有请求或特定的路由路径。它依赖 Spring Cloud Gateway 组件,在网关的微服务架构中使用。 -
拦截器:拦截器依赖 Spring MVC,在单个 Spring Boot 应用程序中配置和使用。拦截器通常在配置类中进行注册,并通过实现
HandlerInterceptor
接口来自定义拦截逻辑。它不会作用于跨服务的请求,只处理单个服务的请求。
5.总结
特性 | Spring Cloud Gateway 过滤器 | Spring MVC 拦截器 |
---|---|---|
作用范围 | 网关层(跨微服务) | 单个服务 |
执行时机 | 请求到达后端服务前,响应返回客户端前 | Controller 调用前后 |
使用场景 | 路由、限流、认证、修改头信息等 | 权限检查、日志记录、参数验证等 |
配置方式 | application.yml 或自定义过滤器类 | 实现 HandlerInterceptor 接口 |
依赖组件 | Spring Cloud Gateway | Spring MVC |
步骤 1:创建网关项目并添加依赖
我们先创建一个 Spring Boot 项目并添加 Spring Cloud Gateway 的依赖。网关的基本配置会依赖这些依赖项。
- 创建项目:在 IntelliJ IDEA 或者通过 Spring Initializr 创建一个 Spring Boot 项目。
- 添加依赖:在
pom.xml
中引入 Spring Cloud Gateway 依赖。
在 pom.xml
中,加入以下内容:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Spring Cloud Gateway 依赖提供了核心的网关功能,而 Actuator 依赖可以帮助我们监控和管理网关。
步骤 2:编写启动类
接下来,我们需要一个启动类来运行我们的网关服务。这是一个简单的 Spring Boot 启动类,将帮助我们启动项目。
在 src/main/java
目录下创建一个 GatewayApplication.java
,代码如下:
package com.example.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
启动类 GatewayApplication
的作用是启动 Spring Boot 应用,使网关服务可以监听客户端的请求。
步骤 3:配置基本的路由规则
在网关中,路由规则定义了请求路径和目标服务之间的映射关系。通过这些配置,网关可以根据请求路径将请求转发到不同的微服务。
3.1 创建 application.yml
文件
在 src/main/resources
目录下创建一个 application.yml
文件,用于定义网关配置。
3.2 定义路由规则
假设我们有三个服务模块:easychat-auth
、easychat-chat
和 easychat-filesystem
。我们希望根据请求路径的不同,将请求转发到相应的服务。
在 application.yml
中添加以下内容:
server:
port: 8080 # 设置网关服务的端口
spring:
cloud:
gateway:
routes:
- id: easychat-auth-route # 定义路由的ID
uri: lb://easychat-auth # 目标服务地址,lb:// 表示使用负载均衡
predicates:
- Path=/auth/** # 当路径匹配 /auth/** 时,将请求转发到 easychat-auth 服务
- id: easychat-chat-route
uri: lb://easychat-chat
predicates:
- Path=/chat/**
- id: easychat-filesystem-route
uri: lb://easychat-filesystem
predicates:
- Path=/filesystem/**
配置解释
id
:为每条路由规则定义一个唯一的 ID,便于管理。uri
:目标服务的 URI,这里使用lb://
表示服务地址是通过负载均衡机制来解析的。predicates
:用于定义路由条件。在这里,我们使用Path
谓词,通过请求路径来匹配对应的服务模块。
通过这种方式,当网关接收到一个请求时,会检查请求路径:
- 如果路径匹配
/auth/**
,则将请求转发到easychat-auth
服务。 - 如果路径匹配
/chat/**
,则将请求转发到easychat-chat
服务。 - 如果路径匹配
/filesystem/**
,则将请求转发到easychat-filesystem
服务。
步骤 4:配置负载均衡
为了让 lb://
生效,我们通常需要服务注册中心(比如 Eureka 或 Consul)。在没有服务注册中心的情况下,也可以在 application.yml
中配置静态的负载均衡服务地址。
例如,我们可以指定 easychat-auth
服务的地址为多个实例来模拟负载均衡:
spring:
cloud:
gateway:
routes:
- id: easychat-auth-route
uri: lb://easychat-auth
predicates:
- Path=/auth/**
loadbalancer:
clients:
easychat-auth:
instances:
- http://localhost:8081
- http://localhost:8082
以上配置中:
easychat-auth
的请求会在localhost:8081
和localhost:8082
之间进行负载均衡。
步骤 5:配置全局过滤器
过滤器可以用于所有路由(称为全局过滤器),也可以用于特定的路由(称为局部过滤器)。我们从配置全局过滤器开始,举例说明如何记录请求的基本信息,比如请求时间和路径。
在 application.yml
中,可以配置一个全局过滤器,用于在所有请求经过网关时记录日志。我们可以使用 LoggingFilter
来实现这一功能。
在 application.yml
文件中添加以下内容:
spring:
cloud:
gateway:
default-filters:
- name: DedupeResponseHeader # 移除重复的响应头
args:
strategy: RETAIN_FIRST # 保留第一个重复的响应头
- name: AddRequestHeader # 向请求头中添加信息
args:
X-Request-Gateway: EasyChatGateway
- name: RequestRateLimiter # 基于 Redis 实现的限流器
args:
redis-rate-limiter:
replenishRate: 10 # 每秒允许通过的请求数
burstCapacity: 20 # 最大突发流量
在这里:
DedupeResponseHeader
:移除响应中重复的头部信息,防止信息冗余。AddRequestHeader
:给请求头添加一个标识X-Request-Gateway
,可以用于标识该请求通过网关。RequestRateLimiter
:使用 Redis 实现限流,这里限制每秒允许 10 个请求,突发流量上限为 20。
步骤 6:配置局部过滤器(仅对某些路由生效)
局部过滤器只对特定的路由生效,下面我们给 easychat-auth
路由配置一些常用的过滤器,比如 请求头修改 和 响应头添加。
在 application.yml
中修改 easychat-auth
路由的配置,增加过滤器设置:
spring:
cloud:
gateway:
routes:
- id: easychat-auth-route
uri: lb://easychat-auth
predicates:
- Path=/auth/**
filters:
- AddRequestHeader=X-Auth-Token, "Auth123" # 添加请求头
- AddResponseHeader=X-Response-Gateway, "EasyChat-Gateway" # 添加响应头
- RewritePath=/auth/(?<segment>.*), /$\\{segment} # 重写请求路径
解释:
AddRequestHeader
:在请求头中添加X-Auth-Token
,值为"Auth123"
,可以用于认证或日志记录。AddResponseHeader
:在响应头中添加X-Response-Gateway
,值为"EasyChat-Gateway"
,标识该响应经过了网关。RewritePath
:将/auth/**
的路径重写为/
后的部分。例如,请求/auth/login
将重写为/login
,方便后端服务不需要额外处理路径前缀。
步骤 7:自定义过滤器
如果需要更复杂的逻辑,可以编写自定义过滤器。例如,可以创建一个过滤器来记录请求的时间或检查某些请求参数。以下是创建自定义过滤器的步骤:
7.1 创建过滤器类
在 src/main/java/com/example/gateway/filter
路径下创建一个名为 CustomLoggingFilter.java
的文件:
package com.example.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class CustomLoggingFilter extends AbstractGatewayFilterFactory<CustomLoggingFilter.Config> {
private static final Logger logger = LoggerFactory.getLogger(CustomLoggingFilter.class);
public CustomLoggingFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
long startTime = System.currentTimeMillis();
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
long endTime = System.currentTimeMillis();
logger.info("Request to {} took {} ms", exchange.getRequest().getURI(), (endTime - startTime));
}));
};
}
public static class Config {
// 配置类可以存放一些自定义配置
}
}
解释:
- 该过滤器在请求处理前记录开始时间,然后在响应时计算请求的耗时,并输出到日志。
apply()
方法返回的GatewayFilter
处理请求并记录时间。
7.2 将自定义过滤器应用到某个路由
在 application.yml
中,将 CustomLoggingFilter
添加到 easychat-chat
路由:
spring:
cloud:
gateway:
routes:
- id: easychat-chat-route
uri: lb://easychat-chat
predicates:
- Path=/chat/**
filters:
- name: CustomLoggingFilter
这样,每次访问 /chat/**
的请求都会触发自定义日志记录,记录请求的处理耗时。