springboot 项目某个接口响应特别慢排查

问题现象

接口请求日志显示耗时仅为几十毫秒,但通过Postman发起相同请求时,实际耗时却高达7秒多。

bash 复制代码
[2025-12-02 09:06:27.344]-[INFO]-[http-nio-3012-exec-3]-[logId:6600b2f0-8d68-4c2c-a7b5-bce11a42e140]-[com.xxx.interceptor.ControllerInterceptor.info:63] 调用 接口://task/retry Class:com.xxx.xxx.xxx.controller.TaskController,Method:taskRetry,Request:101   
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@63bd0ef2] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.jdbc.JDBC4Connection@3448c914] will not be xxxd by Spring
==>  Preparing: select id, task_code, task_name, task_desc, task_execute_time, task_over_time, task_timer, file_path, status, task_type, create_name, update_name, create_date, update_date from r_task where id = ? and status = ? 
==> Parameters: 101(Integer), 4(Byte)
<==      Total: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@63bd0ef2]
[2025-12-02 09:06:27.354]-[WARN]-[http-nio-3012-exec-3]-[logId:6600b2f0-8d68-4c2c-a7b5-bce11a42e140]-[com.xxx..xxx.service.impl.RetryTaskServiceImpl.taskRetry:50]-任务重试任务查询为空,id:101
[2025-12-02 09:06:27.355]-[INFO]-[http-nio-3012-exec-3]-[logId:6600b2f0-8d68-4c2c-a7b5-bce11a42e140]-[com.xxx.interceptor.ControllerInterceptor.logReturn:81]-Class:com.xxx..xxx.controller.TaskController,Method:taskRetry,Response:{"code":"999","msg":"任务重试失败"}

postman请求示例

问题排查

先用arthas的 trace命令 确定哪块执行比较耗时

bash 复制代码
    `---[6792.636ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:intercept()
        +---[0.0061ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:getTarget() #655
        +---[0.005ms] org.springframework.aop.framework.AdvisedSupport:getInterceptorsAndDynamicInterceptionAdvice() #659
        +---[0.0079ms] org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation:<init>() #673
        +---[6792.5681ms] org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation:proceed() #673
        |   `---[10.2326ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:intercept()
        |       +---[0.0044ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:getTarget() #655
        |       +---[0.0047ms] org.springframework.aop.framework.AdvisedSupport:getInterceptorsAndDynamicInterceptionAdvice() #659
        |       +---[0.0092ms] org.springframework.aop.framework.AopProxyUtils:adaptArgumentsIfNecessary() #668
        |       +---[10.1483ms] org.springframework.cglib.proxy.MethodProxy:invoke() #669
        |       +---[0.0034ms] org.springframework.aop.framework.CglibAopProxy:access$000() #675
        |       `---[0.0031ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:releaseTarget() #680
        +---[0.0034ms] org.springframework.aop.framework.CglibAopProxy:access$000() #675
        `---[0.0025ms] org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor:releaseTarget() #680

经过断点调试,程序最终进入了自定义的ControllerInterceptor中的logBefore方法。该方法的主要逻辑相对简单,主要用于打印请求日志并对输入参数进行脱敏处理。问题出在负责参数脱敏的SensitiveInfoSerialize.getJson方法上。

通过断点跟踪发现,程序是在MethodBeforeAdviceInterceptor中执行this.advice.before这行代码时进入自定义ControllerInterceptor的。

java 复制代码
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
    private MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        return mi.proceed();
    }
}

ControllerInterceptor 的logBefore

java 复制代码
 /**
     * /**
     * 记录输入日志
     *
     * @param joinPoint
     */
    @Before(value = "controllerService()")
    public void logBefore(JoinPoint joinPoint) {
        LogUtil.bindLogId(UUID.randomUUID().toString());
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        if (ArrayUtils.isNotEmpty(args)) {
            LogHelper.info(logger, "调用", "接口:" + logTool.getServiceKey(), "Class:" + className
                    + ",Method:" + methodName + ",Request:" + SensitiveInfoSerialize.getJson(args[0]), "", "", "");
        }
    }

这段代码的核心逻辑是:

  1. 遍历 Java Bean 的所有字段
  2. 识别带有 SensitiveInfo 注解的 String 类型字段
  3. 对这些字段执行脱敏处理
  4. 对嵌套对象、数组、集合和 Map 进行递归处理

需要注意的是,这是一个内部工具类,虽然具体实现不便展示,但实际使用中存在一些需要注意的问题。

结论

根本原因是对参数进行递归的时候没有排除基本数据类型,导致对基本数据类型的字段进行递归导致耗时很长。

所以使用递归时要特别注意退出条件,不然可能会导致耗时很长甚至栈溢出。

相关推荐
枫叶落雨2221 天前
ShardingSphere 介绍
java
GetcharZp1 天前
告别 TCP 握手延迟!让你的 Go 服务瞬间拥抱 HTTP/3 时代
后端
花花鱼1 天前
Spring Security 与 Spring MVC
java·spring·mvc
oak隔壁找我1 天前
SpringBoot 将项目打包成 Fat JAR(肥包),核心原理
后端
陌殇殇1 天前
001 Spring AI Alibaba框架整合百炼大模型平台 — 快速入门
人工智能·spring boot·ai
言慢行善1 天前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星1 天前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟1 天前
操作系统之虚拟内存
java·服务器·网络
Tong Z1 天前
常见的限流算法和实现原理
java·开发语言