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

相关推荐
Raas10022 分钟前
AI网关能做调用审计吗?MAI Gateway(魔芋企业级AI网关)实战能力深度解读
java·服务器·网络·数据库·gateway·prompt·企业级产品
Java爱好狂.28 分钟前
挑战一周速通Spring+SpringBoot+SpringCloud!
java·后端·spring·springboot·架构师·java面试·java程序员
萧瑟余晖35 分钟前
Spring Security认证机制详解
spring
m0_5873830043 分钟前
无人自助拼豆系统开发实战指南 核心功能架构搭建与落地经验分享
java·经验分享·spring·小程序·架构·需求分析
泡海椒1 小时前
Spring 项目中如何组织 JQuick-Excel 的 XML 与 Bean
xml·spring·excel
鱼宵1 小时前
Spring AI RAG 问答助手:回答带引用,AI 不再睁眼说瞎话
java·人工智能·spring·springai
captain3762 小时前
Spring Web MVC入门(1)
ide·spring·java-ee·tomcat
java_nnnn4 小时前
Spring Web MVC 入门:从一个 URL 到 Java 后端完整处理请求
java·前端·spring boot·spring
SL_staff16 小时前
为什么业务总在等BI报表?JVS-BI的可视化ELT实践解析
spring·数据分析·数据可视化
badhope17 小时前
Spring AI Alibaba停更:Java程序员的信任,还能被辜负几次?
java·spring