自定义Spring Cloud Gateway过滤器:记录慢请求

在构建微服务架构时,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: 过滤器的具体参数,这些参数将传递给过滤器的配置类。

启动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过滤器,我们可以灵活地监控和记录响应时间超过指定阈值的请求。这不仅有助于我们更好地了解系统的性能瓶颈,还可以帮助我们在生产环境中快速定位问题。

相关推荐
等什么君!1 小时前
spring 学习(spring-Dl补充(注入不同类型的数据))
java·学习·spring
RainbowJie11 小时前
线程池-抢票系统性能优化
java·spring·性能优化
zzyh1234561 小时前
springcloud gateway 负载均衡
spring cloud·gateway·负载均衡
添砖Java中2 小时前
深度解析策略模式:从理论到企业级实战应用
spring boot·spring·spring cloud·设计模式·maven·策略模式
励碼4 小时前
SpringCloud - Gateway 网关
spring boot·spring·spring cloud·gateway
m0_748240544 小时前
502 Bad Gateway 错误详解:从表现推测原因,逐步排查直至解决
gateway
hlsd#6 小时前
微服务中如何使用openfeign上传文件
java·spring boot·spring·微服务
lllsure15 小时前
【快速入门】SpringMVC
java·后端·spring·mvc
wangbing112516 小时前
开发指南098-logback-spring.xml说明
xml·spring·logback