在构建微服务架构时,API网关是一个关键组件,它负责路由、负载均衡、安全验证等多种功能。Spring Cloud Gateway提供了强大的扩展能力,允许开发者通过自定义过滤器来增强其功能。本文将详细介绍如何实现一个自定义过滤器,用于记录响应时间超过指定阈值的请求,并展示如何支持微服务的自定义配置。
首先,我们需要创建一个自定义的Gateway过滤器工厂类。这个类将负责缓存请求体,并在请求处理完成后检查响应时间是否超过了设定的阈值。如果超过了,则记录一条警告日志。
java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@Slf4j
@Component
public class SlowLoggingGatewayFilterFactory extends AbstractGatewayFilterFactory<SlowLoggingGatewayFilterFactory.Config> {
public SlowLoggingGatewayFilterFactory() {
super(SlowLoggingGatewayFilterFactory.Config.class);
log.info("Loaded GatewayFilterFactory [RequestTimeoutLog]");
}
@Override
public GatewayFilter apply(SlowLoggingGatewayFilterFactory.Config config) {
return (exchange, chain) -> {
long startTime = System.currentTimeMillis();
// 缓存请求体,避免因请求体被消费导致读取不到数据
// 使用DataBufferUtils.join将请求体的所有数据缓冲区合并为一个DataBuffer
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
// 将合并后的DataBuffer转换为字节数组,并将其转换为字符串
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);// 释放资源
String cachedBody = new String(bytes, StandardCharsets.UTF_8);
// 将缓存的请求体存储在ServerWebExchange的属性中,键名为"cachedRequestBody"
exchange.getAttributes().put("cachedRequestBody", cachedBody);
return chain.filter(exchange);// 继续处理请求链
})
.doOnSuccessOrError((response, ex) -> {
// 计算耗时并记录
long duration = System.currentTimeMillis() - startTime;
if (config.isEnabled() && duration > config.getTimeout()) {
ServerHttpRequest request = exchange.getRequest();
String cachedBody = exchange.getAttributeOrDefault("cachedRequestBody", "");
Map<String, String> queryParams = request.getQueryParams().toSingleValueMap();
log.warn("Slow Request Detected: Method={}, Path={}, QueryParams={}, Body={}, Duration={}ms",
request.getMethodValue(), request.getPath(), queryParams, cachedBody, duration);
}
});
};
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Config {
// 控制是否超时日志打印
private boolean enabled;
// 超时阈值,单位毫秒,默认1000毫秒
private long timeout = 1000L;
}
}
原理很简单,使用 DataBufferUtils.join
将请求体的所有数据缓冲区合并为一个 DataBuffer
,然后将其转换为字符串并缓存到 exchange
的属性中。调用 chain.filter(exchange)
继续处理请求链,确保请求可以正常传递到下游微服务。在请求处理完成后(无论成功还是失败),计算请求处理的总耗时。如果配置启用了日志记录并且耗时超过了设定的阈值,则记录一条警告日志。
接下来,需要在 application.yml
文件中配置路由并应用自定义过滤器。
yaml
spring:
cloud:
gateway:
routes:
- id: api_route_1
uri: http://service1.example.com
predicates:
- Path=/api/v1/**
filters:
- name: SlowLogging
args:
enabled: true
timeout: 1500
- id: api_route_2
uri: http://service2.example.com
predicates:
- Path=/api/v2/**
filters:
- name: SlowLogging
args:
enabled: false
timeout: 1000
- id: default_route
uri: http://default-service.example.com
predicates:
- Path=/**
filters:
- name: SlowLogging
args:
enabled: true
timeout: 2000
-
id: 每个路由的唯一标识符。
-
uri: 目标服务的URI。
-
predicates : 定义路由匹配条件,这里是匹配路径以
/api/
开头的请求。 -
:
定义应用于该路由的过滤器列表。- name : 过滤器名称,应与自定义过滤器类名中的
GatewayFilterFactory
前缀部分一致(去掉后缀)。例如,SlowLoggingGatewayFilterFactory
对应的过滤器名称为SlowLogging
。 - args: 过滤器的具体参数,这些参数将传递给过滤器的配置类。
- name : 过滤器名称,应与自定义过滤器类名中的
启动Spring Boot应用 和 Gateway网关,并发送请求到不同的路由,观察日志输出。例如,如果你发送一个请求到 http://localhost/api/v1/test
并且响应时间超过1500毫秒,你会看到类似以下的日志记录:
log
WARN Slow Request Detected: Method=GET, Path=/api/v1/test, QueryParams={}, Body=..., Duration=1600ms
通过实现自定义的Spring Cloud Gateway过滤器,我们可以灵活地监控和记录响应时间超过指定阈值的请求。这不仅有助于我们更好地了解系统的性能瓶颈,还可以帮助我们在生产环境中快速定位问题。