自定义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 天前
SpringCloud前后端整体开发流程-以及技术总结文章实时更新中
java·数据库·后端·spring·spring cloud
从心归零1 天前
springboot-jpa的批量更新方法
java·spring boot·spring
LiamTuc1 天前
Hibernate‌
spring
小许学java1 天前
Spring事务和事务传播机制
java·数据库·spring·事务
这是程序猿1 天前
基于java的ssm框架经典电影推荐网站
java·开发语言·spring boot·spring·经典电影推荐网站
jiayong231 天前
海外求职平台与策略指南
java·spring
SadSunset1 天前
(37)全注解式开发AOP
java·spring
子超兄1 天前
Bean生命周期
java·spring
Mr.朱鹏2 天前
超时订单处理方案实战指南【完整版】
java·spring boot·redis·spring·rabbitmq·rocketmq·订单
Lisonseekpan2 天前
RBAC 基于角色的访问控制模型详解与实践指南
java·服务器·网络·后端·spring·log4j