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

相关推荐
qq_三哥啊2 小时前
【IDEA】设置Debug调试时调试器不进入特定类(Spring框架、Mybatis框架)
spring·intellij-idea·mybatis
别惹CC2 小时前
Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
人工智能·spring boot·spring
寒士obj2 小时前
Spring事物
java·spring
IT毕设实战小研11 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
甄超锋12 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
Java小白程序员15 小时前
Spring Framework:Java 开发的基石与 Spring 生态的起点
java·数据库·spring
甄超锋16 小时前
Java Maven更换国内源
java·开发语言·spring boot·spring·spring cloud·tomcat·maven
还是鼠鼠17 小时前
tlias智能学习辅助系统--Maven 高级-私服介绍与资源上传下载
java·spring boot·后端·spring·maven
还是大剑师兰特18 小时前
Spring面试题及详细答案 125道(1-15) -- 核心概念与基础1
spring·大剑师·spring面试题·spring教程
python_13621 小时前
web请求和响应
java·spring·github