• 阿里问题定位神器 Arthas 的骚操作,定位线上BUG,超给力


    原文:https://mp.weixin.qq.com/s/Y58PFhCgbC-GBjj_sHxvDg

    公司有个渠道系统,专门对接三方渠道使用,没有什么业务逻辑,主要是转换报文和参数校验之类的工作,起着一个承上启下的作用。

    最近在优化接口的响应时间,优化了代码之后,但是时间还是达不到要求;有一个诡异的100ms左右的耗时问题,在接口中打印了请求处理时间后,和调用方的响应时间还有差了100ms左右。比如程序里记录150ms,但是调用方等待时间却为250ms左右。

    下面记录下当时详细的定位&解决流程(其实解决很简单,关键在于怎么定位并找到解决问题的方法)

    定位过程

    分析代码

    渠道系统是一个常见的spring-boot web工程,使用了集成的tomcat。分析了代码之后,发现并没有特殊的地方,没有特殊的过滤器或者拦截器,所以初步排除是业务代码问题

    分析调用流程

    出现这个问题之后,首先确认了下接口的调用流程。由于是内部测试,所以调用流程较少。

    Nginx -反向代理-> 渠道系统

    公司是云服务器,网络走的也是云的内网。由于不明确问题的原因,所以用排除法,首先确认服务器网络是否有问题。

    先确认发送端到Nginx Host是否有问题:

     从ping结果上看,发送端到Nginx主机的延迟是无问题的,接下来查看Nginx到渠道系统的网络。

    从ping结果上看,Nginx到渠道系统服务器网络延迟也是没问题的

    既然网络看似没问题,那么可以继续排除法,砍掉Nginx,客户端直接再渠道系统的服务器上,通过回环地址(localhost)直连,避免经过网卡/dns,缩小问题范围看看能否复现(这个应用和地址是我后期模拟的,测试的是一个空接口):

    从curl日志上看,通过回环地址调用一个空接口耗时也有73ms。这就奇怪了,跳过了中间所有调用节点(包括过滤器&拦截器之类),直接请求应用一个空接口,都有73ms的耗时,再请求一次看看:

    更奇怪的是,第二次请求耗时就正常了,变成了3ms。经查阅资料,linux curl是默认开启http keep-alive的(Keep-Alive的介绍可以参考我的另一篇文章)。就算不开启keep-alive,每次重新handshake,也不至于需要70ms。

    经过不断分析测试发现,连续请求的话时间就会很短,每次请求只需要几毫秒,但是如果隔一段时间再请求,就会花费70ms以上。

    从这个现象猜想,可能是某些缓存机制导致的,连续请求因为有缓存,所以速度快,时间长缓存失效后导致时间长。

    那么这个问题点到底在哪一层呢?tomcat层还是spring-webmvc呢?

    光猜想定位不了问题,还是得实际测试一下,把渠道系统的代码放到本地ide里启动测试能否复现

    但是导入本地Ide后,在Ide中启动后并不能复现问题,并没有70+ms的延迟问题。这下头疼了,本地无法复现,不能Debug,由于问题点不在业务代码,也不能通过加日志的方式来Debug

    这时候可以祭出神器Arthas了

    Arthas分析问题

    Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:

    • 这个类从哪个 jar 包加载的?

    • 为什么会报各种类相关的 Exception?

    • 我改的代码为什么没有执行到?

    • 难道是我没 commit?分支搞错了?

    • 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?

    • 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!

    • 是否有一个全局视角来查看系统的运行状况?

    • 有什么办法可以监控到JVM的实时运行状态?

    上面是Arthas的官方简介,这次我只需要用他的一个小功能trace。动态计算方法调用路径和时间,这样我就可以定位时间在哪个地方被消耗了。

    trace 方法内部调用路径,并输出方法路径上的每个节点上耗时 trace 命令能主动搜索 class-pattern/method-pattern 对应的方法调用路径,渲染和统计整个调用链路上的所有性能开销和追踪调用链路。

    有了神器,那么该追踪什么方法呢?由于我对Tomcat源码不是很熟,所以只能从spring mvc下手,先来trace一下spring mvc的入口:

    [
    arthas@24851
    ]
    $ trace org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
     
    *
    Press
     Q 
    or
     
    Ctrl
    +
    C to abort
    .
    Affect
    (
    class
    -
    cnt
    :
    1
     
    ,
     method
    -
    cnt
    :
    44
    )
     cost 
    in
     
    508
     ms
    .
    `---ts=2019-09-14 21:07:44;thread_name=http-nio-7744-exec-2;id=11;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@7c136917
        `
    ---[
    2.952142ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    buildLocaleContext
    ()
    
    `---ts=2019-09-14 21:07:44;thread_name=http-nio-7744-exec-2;id=11;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@7c136917
        `
    ---[
    18.08903ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    doService
    ()
            
    +---[
    0.041346ms
    ]
     org
    .
    apache
    .
    commons
    .
    logging
    .
    Log
    :
    isDebugEnabled
    ()
     
    #889
            
    +---[
    0.022398ms
    ]
     org
    .
    springframework
    .
    web
    .
    util
    .
    WebUtils
    :
    isIncludeRequest
    ()
     
    #898
            
    +---[
    0.014904ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    getWebApplicationContext
    ()
     
    #910
            
    +---[
    1.071879ms
    ]
     javax
    .
    servlet
    .
    http
    .
    HttpServletRequest
    :
    setAttribute
    ()
     
    #910
            
    +---[
    0.020977ms
    ]
     javax
    .
    servlet
    .
    http
    .
    HttpServletRequest
    :
    setAttribute
    ()
     
    #911
            
    +---[
    0.017073ms
    ]
     javax
    .
    servlet
    .
    http
    .
    HttpServletRequest
    :
    setAttribute
    ()
     
    #912
            
    +---[
    0.218277ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    getThemeSource
    ()
     
    #913
            
    |
       
    `---[0.137568ms] org.springframework.web.servlet.DispatcherServlet:getThemeSource()
            |       `
    ---[
    min
    =
    0.00783ms
    ,
    max
    =
    0.014251ms
    ,
    total
    =
    0.022081ms
    ,
    count
    =
    2
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    getWebApplicationContext
    ()
     
    #782
            
    +---[
    0.019363ms
    ]
     javax
    .
    servlet
    .
    http
    .
    HttpServletRequest
    :
    setAttribute
    ()
     
    #913
            
    +---[
    0.070694ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    FlashMapManager
    :
    retrieveAndUpdate
    ()
     
    #916
            
    +---[
    0.01839ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    FlashMap
    :<
    init
    >()
     
    #920
            
    +---[
    0.016943ms
    ]
     javax
    .
    servlet
    .
    http
    .
    HttpServletRequest
    :
    setAttribute
    ()
     
    #920
            
    +---[
    0.015268ms
    ]
     javax
    .
    servlet
    .
    http
    .
    HttpServletRequest
    :
    setAttribute
    ()
     
    #921
            
    +---[
    15.050124ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    doDispatch
    ()
     
    #925
            
    |
       
    `---[14.943477ms] org.springframework.web.servlet.DispatcherServlet:doDispatch()
            |       +---[0.019135ms] org.springframework.web.context.request.async.WebAsyncUtils:getAsyncManager() #953
            |       +---[2.108373ms] org.springframework.web.servlet.DispatcherServlet:checkMultipart() #960
            |       |   `
    ---[
    2.004436ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    checkMultipart
    ()
            
    |
           
    |
           
    `---[1.890845ms] org.springframework.web.multipart.MultipartResolver:isMultipart() #1117
            |       +---[2.054361ms] org.springframework.web.servlet.DispatcherServlet:getHandler() #964
            |       |   `
    ---[
    1.961963ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    getHandler
    ()
            
    |
           
    |
           
    +---[
    0.02051ms
    ]
     java
    .
    util
    .
    List
    :
    iterator
    ()
     
    #1183
            
    |
           
    |
           
    +---[
    min
    =
    0.003805ms
    ,
    max
    =
    0.009641ms
    ,
    total
    =
    0.013446ms
    ,
    count
    =
    2
    ]
     java
    .
    util
    .
    Iterator
    :
    hasNext
    ()
     
    #1183
            
    |
           
    |
           
    +---[
    min
    =
    0.003181ms
    ,
    max
    =
    0.009751ms
    ,
    total
    =
    0.012932ms
    ,
    count
    =
    2
    ]
     java
    .
    util
    .
    Iterator
    :
    next
    ()
     
    #1183
            
    |
           
    |
           
    +---[
    min
    =
    0.005841ms
    ,
    max
    =
    0.015308ms
    ,
    total
    =
    0.021149ms
    ,
    count
    =
    2
    ]
     org
    .
    apache
    .
    commons
    .
    logging
    .
    Log
    :
    isTraceEnabled
    ()
     
    #1184
            
    |
           
    |
           
    `---[min=0.474739ms,max=1.19145ms,total=1.666189ms,count=2] org.springframework.web.servlet.HandlerMapping:getHandler() #1188
            |       +---[0.013071ms] org.springframework.web.servlet.HandlerExecutionChain:getHandler() #971
            |       +---[0.372236ms] org.springframework.web.servlet.DispatcherServlet:getHandlerAdapter() #971
            |       |   `
    ---[
    0.280073ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    getHandlerAdapter
    ()
            
    |
           
    |
           
    +---[
    0.004804ms
    ]
     java
    .
    util
    .
    List
    :
    iterator
    ()
     
    #1224
            
    |
           
    |
           
    +---[
    0.003668ms
    ]
     java
    .
    util
    .
    Iterator
    :
    hasNext
    ()
     
    #1224
            
    |
           
    |
           
    +---[
    0.003038ms
    ]
     java
    .
    util
    .
    Iterator
    :
    next
    ()
     
    #1224
            
    |
           
    |
           
    +---[
    0.006451ms
    ]
     org
    .
    apache
    .
    commons
    .
    logging
    .
    Log
    :
    isTraceEnabled
    ()
     
    #1225
            
    |
           
    |
           
    `---[0.012683ms] org.springframework.web.servlet.HandlerAdapter:supports() #1228
            |       +---[0.012848ms] javax.servlet.http.HttpServletRequest:getMethod() #974
            |       +---[0.013132ms] java.lang.String:equals() #975
            |       +---[0.003025ms] org.springframework.web.servlet.HandlerExecutionChain:getHandler() #977
            |       +---[0.008095ms] org.springframework.web.servlet.HandlerAdapter:getLastModified() #977
            |       +---[0.006596ms] org.apache.commons.logging.Log:isDebugEnabled() #978
            |       +---[0.018024ms] org.springframework.web.context.request.ServletWebRequest:<init>() #981
            |       +---[0.017869ms] org.springframework.web.context.request.ServletWebRequest:checkNotModified() #981
            |       +---[0.038542ms] org.springframework.web.servlet.HandlerExecutionChain:applyPreHandle() #986
            |       +---[0.00431ms] org.springframework.web.servlet.HandlerExecutionChain:getHandler() #991
            |       +---[4.248493ms] org.springframework.web.servlet.HandlerAdapter:handle() #991
            |       +---[0.014805ms] org.springframework.web.context.request.async.WebAsyncManager:isConcurrentHandlingStarted() #993
            |       +---[1.444994ms] org.springframework.web.servlet.DispatcherServlet:applyDefaultViewName() #997
            |       |   `
    ---[
    0.067631ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    applyDefaultViewName
    ()
            
    |
           
    +---[
    0.012027ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    HandlerExecutionChain
    :
    applyPostHandle
    ()
     
    #998
            
    |
           
    +---[
    0.373997ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    :
    processDispatchResult
    ()
     
    #1008
            
    |
           
    |
       
    `---[0.197004ms] org.springframework.web.servlet.DispatcherServlet:processDispatchResult()
            |       |       +---[0.007074ms] org.apache.commons.logging.Log:isDebugEnabled() #1075
            |       |       +---[0.005467ms] org.springframework.web.context.request.async.WebAsyncUtils:getAsyncManager() #1081
            |       |       +---[0.004054ms] org.springframework.web.context.request.async.WebAsyncManager:isConcurrentHandlingStarted() #1081
            |       |       `
    ---[
    0.011988ms
    ]
     org
    .
    springframework
    .
    web
    .
    servlet
    .
    HandlerExecutionChain
    :
    triggerAfterCompletion
    ()
     
    #1087
            
    |
           
    `---[0.004015ms] org.springframework.web.context.request.async.WebAsyncManager:isConcurrentHandlingStarted() #1018
            +---[0.005055ms] org.springframework.web.context.request.async.WebAsyncUtils:getAsyncManager() #928
            `
    ---[
    0.003422ms
    ]
     org
    .
    springframework
    .
    web
    .
    context
    .
    request
    .
    async
    .
    WebAsyncManager
    :
    isConcurrentHandlingStarted
    ()
     
    #928
    

      

    [
    jboss@VM_10_91_centos tmp
    ]
    $ curl 
    -
    w 
    "@curl-time.txt"
     http
    :
    //127.0.0.1:7744/send
    success
                  http
    :
     
    200
                   dns
    :
     
    0.001s
              redirect
    :
     
    0.000s
          time_connect
    :
     
    0.001s
       time_appconnect
    :
     
    0.000s
      time_pretransfer
    :
     
    0.001s
    time_starttransfer
    :
     
    0.115s
         size_download
    :
     
    7bytes
        speed_download
    :
     
    60.000B
    /
    s
                      
    ----------
            time_total
    :
     
    0.115s
    

      

    本次调用,调用端时间花费115ms,但是从arthas trace上看,spring mvc只消耗了18ms,那么剩下的97ms去哪了呢?

    本地测试后已经可以排除spring mvc的问题了,最后也是唯一可能出问题的点就是tomcat

    可是本人并不熟悉tomcat中的源码,就连请求入口都不清楚,tomcat里需要trace的类都不好找。。。

    不过没关系,有神器Arthas,可以通过stack命令来反向查找调用路径,以org.springframework.web.servlet.DispatcherServlet作为参数:

    stack 输出当前方法被调用的调用路径 很多时候我们都知道一个方法被执行,但这个方法被执行的路径非常多,或者你根本就不知道这个方法是从那里被执行了,此时你需要的是 stack 命令。

    [
    arthas@24851
    ]
    $ stack org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
     
    *
    Press
     Q 
    or
     
    Ctrl
    +
    C to abort
    .
    Affect
    (
    class
    -
    cnt
    :
    1
     
    ,
     method
    -
    cnt
    :
    44
    )
     cost 
    in
     
    495
     ms
    .
    ts
    =
    2019
    -
    09
    -
    14
     
    21
    :
    15
    :
    19
    ;
    thread_name
    =
    http
    -
    nio
    -
    7744
    -
    exec
    -
    5
    ;
    id
    =
    14
    ;
    is_daemon
    =
    true
    ;
    priority
    =
    5
    ;
    TCCL
    =
    org
    .
    springframework
    .
    boot
    .
    web
    .
    embedded
    .
    tomcat
    .
    TomcatEmbeddedWebappClassLoader@7c136917
        
    @org
    .
    springframework
    .
    web
    .
    servlet
    .
    FrameworkServlet
    .
    processRequest
    ()
            at org
    .
    springframework
    .
    web
    .
    servlet
    .
    FrameworkServlet
    .
    doGet
    (
    FrameworkServlet
    .
    java
    :
    866
    )
            at javax
    .
    servlet
    .
    http
    .
    HttpServlet
    .
    service
    (
    HttpServlet
    .
    java
    :
    635
    )
            at org
    .
    springframework
    .
    web
    .
    servlet
    .
    FrameworkServlet
    .
    service
    (
    FrameworkServlet
    .
    java
    :
    851
    )
            at javax
    .
    servlet
    .
    http
    .
    HttpServlet
    .
    service
    (
    HttpServlet
    .
    java
    :
    742
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    231
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    apache
    .
    tomcat
    .
    websocket
    .
    server
    .
    WsFilter
    .
    doFilter
    (
    WsFilter
    .
    java
    :
    52
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    193
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    RequestContextFilter
    .
    doFilterInternal
    (
    RequestContextFilter
    .
    java
    :
    99
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    OncePerRequestFilter
    .
    doFilter
    (
    OncePerRequestFilter
    .
    java
    :
    107
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    193
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    HttpPutFormContentFilter
    .
    doFilterInternal
    (
    HttpPutFormContentFilter
    .
    java
    :
    109
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    OncePerRequestFilter
    .
    doFilter
    (
    OncePerRequestFilter
    .
    java
    :
    107
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    193
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    HiddenHttpMethodFilter
    .
    doFilterInternal
    (
    HiddenHttpMethodFilter
    .
    java
    :
    81
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    OncePerRequestFilter
    .
    doFilter
    (
    OncePerRequestFilter
    .
    java
    :
    107
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    193
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    CharacterEncodingFilter
    .
    doFilterInternal
    (
    CharacterEncodingFilter
    .
    java
    :
    200
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    OncePerRequestFilter
    .
    doFilter
    (
    OncePerRequestFilter
    .
    java
    :
    107
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    193
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    StandardWrapperValve
    .
    invoke
    (
    StandardWrapperValve
    .
    java
    :
    198
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    StandardContextValve
    .
    invoke
    (
    StandardContextValve
    .
    java
    :
    96
    )
            at org
    .
    apache
    .
    catalina
    .
    authenticator
    .
    AuthenticatorBase
    .
    invoke
    (
    AuthenticatorBase
    .
    java
    :
    496
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    StandardHostValve
    .
    invoke
    (
    StandardHostValve
    .
    java
    :
    140
    )
            at org
    .
    apache
    .
    catalina
    .
    valves
    .
    ErrorReportValve
    .
    invoke
    (
    ErrorReportValve
    .
    java
    :
    81
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    StandardEngineValve
    .
    invoke
    (
    StandardEngineValve
    .
    java
    :
    87
    )
            at org
    .
    apache
    .
    catalina
    .
    connector
    .
    CoyoteAdapter
    .
    service
    (
    CoyoteAdapter
    .
    java
    :
    342
    )
            at org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    .
    service
    (
    Http11Processor
    .
    java
    :
    803
    )
            at org
    .
    apache
    .
    coyote
    .
    AbstractProcessorLight
    .
    process
    (
    AbstractProcessorLight
    .
    java
    :
    66
    )
            at org
    .
    apache
    .
    coyote
    .
    AbstractProtocol$ConnectionHandler
    .
    process
    (
    AbstractProtocol
    .
    java
    :
    790
    )
            at org
    .
    apache
    .
    tomcat
    .
    util
    .
    net
    .
    NioEndpoint$SocketProcessor
    .
    doRun
    (
    NioEndpoint
    .
    java
    :
    1468
    )
            at org
    .
    apache
    .
    tomcat
    .
    util
    .
    net
    .
    SocketProcessorBase
    .
    run
    (
    SocketProcessorBase
    .
    java
    :
    49
    )
            at java
    .
    util
    .
    concurrent
    .
    ThreadPoolExecutor
    .
    runWorker
    (
    ThreadPoolExecutor
    .
    java
    :
    1149
    )
            at java
    .
    util
    .
    concurrent
    .
    ThreadPoolExecutor$Worker
    .
    run
    (
    ThreadPoolExecutor
    .
    java
    :
    624
    )
            at org
    .
    apache
    .
    tomcat
    .
    util
    .
    threads
    .
    TaskThread$WrappingRunnable
    .
    run
    (
    TaskThread
    .
    java
    :
    61
    )
            at java
    .
    lang
    .
    Thread
    .
    run
    (
    Thread
    .
    java
    :
    748
    )
    
    ts
    =
    2019
    -
    09
    -
    14
     
    21
    :
    15
    :
    19
    ;
    thread_name
    =
    http
    -
    nio
    -
    7744
    -
    exec
    -
    5
    ;
    id
    =
    14
    ;
    is_daemon
    =
    true
    ;
    priority
    =
    5
    ;
    TCCL
    =
    org
    .
    springframework
    .
    boot
    .
    web
    .
    embedded
    .
    tomcat
    .
    TomcatEmbeddedWebappClassLoader@7c136917
        
    @org
    .
    springframework
    .
    web
    .
    servlet
    .
    DispatcherServlet
    .
    doService
    ()
            at org
    .
    springframework
    .
    web
    .
    servlet
    .
    FrameworkServlet
    .
    processRequest
    (
    FrameworkServlet
    .
    java
    :
    974
    )
            at org
    .
    springframework
    .
    web
    .
    servlet
    .
    FrameworkServlet
    .
    doGet
    (
    FrameworkServlet
    .
    java
    :
    866
    )
            at javax
    .
    servlet
    .
    http
    .
    HttpServlet
    .
    service
    (
    HttpServlet
    .
    java
    :
    635
    )
            at org
    .
    springframework
    .
    web
    .
    servlet
    .
    FrameworkServlet
    .
    service
    (
    FrameworkServlet
    .
    java
    :
    851
    )
            at javax
    .
    servlet
    .
    http
    .
    HttpServlet
    .
    service
    (
    HttpServlet
    .
    java
    :
    742
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    231
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    apache
    .
    tomcat
    .
    websocket
    .
    server
    .
    WsFilter
    .
    doFilter
    (
    WsFilter
    .
    java
    :
    52
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    193
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    RequestContextFilter
    .
    doFilterInternal
    (
    RequestContextFilter
    .
    java
    :
    99
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    OncePerRequestFilter
    .
    doFilter
    (
    OncePerRequestFilter
    .
    java
    :
    107
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    193
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    HttpPutFormContentFilter
    .
    doFilterInternal
    (
    HttpPutFormContentFilter
    .
    java
    :
    109
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    OncePerRequestFilter
    .
    doFilter
    (
    OncePerRequestFilter
    .
    java
    :
    107
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    193
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    HiddenHttpMethodFilter
    .
    doFilterInternal
    (
    HiddenHttpMethodFilter
    .
    java
    :
    81
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    OncePerRequestFilter
    .
    doFilter
    (
    OncePerRequestFilter
    .
    java
    :
    107
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    193
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    CharacterEncodingFilter
    .
    doFilterInternal
    (
    CharacterEncodingFilter
    .
    java
    :
    200
    )
            at org
    .
    springframework
    .
    web
    .
    filter
    .
    OncePerRequestFilter
    .
    doFilter
    (
    OncePerRequestFilter
    .
    java
    :
    107
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    internalDoFilter
    (
    ApplicationFilterChain
    .
    java
    :
    193
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    ApplicationFilterChain
    .
    doFilter
    (
    ApplicationFilterChain
    .
    java
    :
    166
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    StandardWrapperValve
    .
    invoke
    (
    StandardWrapperValve
    .
    java
    :
    198
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    StandardContextValve
    .
    invoke
    (
    StandardContextValve
    .
    java
    :
    96
    )
            at org
    .
    apache
    .
    catalina
    .
    authenticator
    .
    AuthenticatorBase
    .
    invoke
    (
    AuthenticatorBase
    .
    java
    :
    496
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    StandardHostValve
    .
    invoke
    (
    StandardHostValve
    .
    java
    :
    140
    )
            at org
    .
    apache
    .
    catalina
    .
    valves
    .
    ErrorReportValve
    .
    invoke
    (
    ErrorReportValve
    .
    java
    :
    81
    )
            at org
    .
    apache
    .
    catalina
    .
    core
    .
    StandardEngineValve
    .
    invoke
    (
    StandardEngineValve
    .
    java
    :
    87
    )
            at org
    .
    apache
    .
    catalina
    .
    connector
    .
    CoyoteAdapter
    .
    service
    (
    CoyoteAdapter
    .
    java
    :
    342
    )
            at org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    .
    service
    (
    Http11Processor
    .
    java
    :
    803
    )
            at org
    .
    apache
    .
    coyote
    .
    AbstractProcessorLight
    .
    process
    (
    AbstractProcessorLight
    .
    java
    :
    66
    )
            at org
    .
    apache
    .
    coyote
    .
    AbstractProtocol$ConnectionHandler
    .
    process
    (
    AbstractProtocol
    .
    java
    :
    790
    )
            at org
    .
    apache
    .
    tomcat
    .
    util
    .
    net
    .
    NioEndpoint$SocketProcessor
    .
    doRun
    (
    NioEndpoint
    .
    java
    :
    1468
    )
            at org
    .
    apache
    .
    tomcat
    .
    util
    .
    net
    .
    SocketProcessorBase
    .
    run
    (
    SocketProcessorBase
    .
    java
    :
    49
    )
            at java
    .
    util
    .
    concurrent
    .
    ThreadPoolExecutor
    .
    runWorker
    (
    ThreadPoolExecutor
    .
    java
    :
    1149
    )
            at java
    .
    util
    .
    concurrent
    .
    ThreadPoolExecutor$Worker
    .
    run
    (
    ThreadPoolExecutor
    .
    java
    :
    624
    )
            at org
    .
    apache
    .
    tomcat
    .
    util
    .
    threads
    .
    TaskThread$WrappingRunnable
    .
    run
    (
    TaskThread
    .
    java
    :
    61
    )
            at java
    .
    lang
    .
    Thread
    .
    run
    (
    Thread
    .
    java
    :
    748
    )
    

      从stack日志上可以很直观的看出DispatchServlet的调用栈,那么这么长的路径,该trace哪个类呢(这里跳过spring mvc中的过滤器的trace过程,实际排查的时候也trace了一遍,但这诡异的时间消耗不是由这里过滤器产生的)?有一定经验的老司机从名字上大概也能猜出来从哪里下手比较好,那就是org.apache.coyote.http11.Http11Processor.service,从名字上看,http1.1处理器,这可能是一个比较好的切入点。下面来trace一下:

    [
    arthas@24851
    ]
    $ trace org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
     service
    Press
     Q 
    or
     
    Ctrl
    +
    C to abort
    .
    Affect
    (
    class
    -
    cnt
    :
    1
     
    ,
     method
    -
    cnt
    :
    1
    )
     cost 
    in
     
    269
     ms
    .
    `---ts=2019-09-14 21:22:51;thread_name=http-nio-7744-exec-8;id=17;is_daemon=true;priority=5;TCCL=org.springframework.boot.loader.LaunchedURLClassLoader@20ad9418
        `
    ---[
    131.650285ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    service
    ()
            
    +---[
    0.036851ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Request
    :
    getRequestProcessor
    ()
     
    #667
            
    +---[
    0.009986ms
    ]
     org
    .
    apache
    .
    coyote
    .
    RequestInfo
    :
    setStage
    ()
     
    #668
            
    +---[
    0.008928ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    setSocketWrapper
    ()
     
    #671
            
    +---[
    0.013236ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11InputBuffer
    :
    init
    ()
     
    #672
            
    +---[
    0.00981ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11OutputBuffer
    :
    init
    ()
     
    #673
            
    +---[
    min
    =
    0.00213ms
    ,
    max
    =
    0.007317ms
    ,
    total
    =
    0.009447ms
    ,
    count
    =
    2
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    getErrorState
    ()
     
    #683
            
    +---[
    min
    =
    0.002098ms
    ,
    max
    =
    0.008888ms
    ,
    total
    =
    0.010986ms
    ,
    count
    =
    2
    ]
     org
    .
    apache
    .
    coyote
    .
    ErrorState
    :
    isError
    ()
     
    #683
            
    +---[
    min
    =
    0.002448ms
    ,
    max
    =
    0.007149ms
    ,
    total
    =
    0.009597ms
    ,
    count
    =
    2
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    isAsync
    ()
     
    #683
            
    +---[
    min
    =
    0.002399ms
    ,
    max
    =
    0.00852ms
    ,
    total
    =
    0.010919ms
    ,
    count
    =
    2
    ]
     org
    .
    apache
    .
    tomcat
    .
    util
    .
    net
    .
    AbstractEndpoint
    :
    isPaused
    ()
     
    #683
            
    +---[
    min
    =
    0.033587ms
    ,
    max
    =
    0.11832ms
    ,
    total
    =
    0.151907ms
    ,
    count
    =
    2
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11InputBuffer
    :
    parseRequestLine
    ()
     
    #687
            
    +---[
    0.005384ms
    ]
     org
    .
    apache
    .
    tomcat
    .
    util
    .
    net
    .
    AbstractEndpoint
    :
    isPaused
    ()
     
    #695
            
    +---[
    0.007924ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Request
    :
    getMimeHeaders
    ()
     
    #702
            
    +---[
    0.006744ms
    ]
     org
    .
    apache
    .
    tomcat
    .
    util
    .
    net
    .
    AbstractEndpoint
    :
    getMaxHeaderCount
    ()
     
    #702
            
    +---[
    0.012574ms
    ]
     org
    .
    apache
    .
    tomcat
    .
    util
    .
    http
    .
    MimeHeaders
    :
    setLimit
    ()
     
    #702
            
    +---[
    0.14319ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11InputBuffer
    :
    parseHeaders
    ()
     
    #703
            
    +---[
    0.003997ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Request
    :
    getMimeHeaders
    ()
     
    #743
            
    +---[
    0.026561ms
    ]
     org
    .
    apache
    .
    tomcat
    .
    util
    .
    http
    .
    MimeHeaders
    :
    values
    ()
     
    #743
            
    +---[
    min
    =
    0.002869ms
    ,
    max
    =
    0.01203ms
    ,
    total
    =
    0.014899ms
    ,
    count
    =
    2
    ]
     java
    .
    util
    .
    Enumeration
    :
    hasMoreElements
    ()
     
    #745
            
    +---[
    0.070114ms
    ]
     java
    .
    util
    .
    Enumeration
    :
    nextElement
    ()
     
    #746
            
    +---[
    0.010921ms
    ]
     java
    .
    lang
    .
    String
    :
    toLowerCase
    ()
     
    #746   +---[0.008453ms] java.lang.String:contains() #746
            
    +---[
    0.002698ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    getErrorState
    ()
     
    #775
            
    +---[
    0.00307ms
    ]
     org
    .
    apache
    .
    coyote
    .
    ErrorState
    :
    isError
    ()
     
    #775
            
    +---[
    0.002708ms
    ]
     org
    .
    apache
    .
    coyote
    .
    RequestInfo
    :
    setStage
    ()
     
    #777
            
    +---[
    0.171139ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    prepareRequest
    ()
     
    #779
            
    +---[
    0.009349ms
    ]
     org
    .
    apache
    .
    tomcat
    .
    util
    .
    net
    .
    SocketWrapperBase
    :
    decrementKeepAlive
    ()
     
    #794
            
    +---[
    0.002574ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    getErrorState
    ()
     
    #800
            
    +---[
    0.002696ms
    ]
     org
    .
    apache
    .
    coyote
    .
    ErrorState
    :
    isError
    ()
     
    #800
            
    +---[
    0.002499ms
    ]
     org
    .
    apache
    .
    coyote
    .
    RequestInfo
    :
    setStage
    ()
     
    #802
            
    +---[
    0.005641ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    getAdapter
    ()
     
    #803
            
    +---[
    129.868916ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Adapter
    :
    service
    ()
     
    #803
            
    +---[
    0.003859ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    getErrorState
    ()
     
    #809
            
    +---[
    0.002365ms
    ]
     org
    .
    apache
    .
    coyote
    .
    ErrorState
    :
    isError
    ()
     
    #809
            
    +---[
    0.003844ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    isAsync
    ()
     
    #809
            
    +---[
    0.002382ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Response
    :
    getStatus
    ()
     
    #809
            
    +---[
    0.002476ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    statusDropsConnection
    ()
     
    #809
            
    +---[
    0.002284ms
    ]
     org
    .
    apache
    .
    coyote
    .
    RequestInfo
    :
    setStage
    ()
     
    #838
            
    +---[
    0.00222ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    isAsync
    ()
     
    #839
            
    +---[
    0.037873ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    endRequest
    ()
     
    #843
            
    +---[
    0.002188ms
    ]
     org
    .
    apache
    .
    coyote
    .
    RequestInfo
    :
    setStage
    ()
     
    #845
            
    +---[
    0.002112ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    getErrorState
    ()
     
    #849
            
    +---[
    0.002063ms
    ]
     org
    .
    apache
    .
    coyote
    .
    ErrorState
    :
    isError
    ()
     
    #849
            
    +---[
    0.002504ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    isAsync
    ()
     
    #853
            
    +---[
    0.009808ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Request
    :
    updateCounters
    ()
     
    #854
            
    +---[
    0.002008ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    getErrorState
    ()
     
    #855
            
    +---[
    0.002192ms
    ]
     org
    .
    apache
    .
    coyote
    .
    ErrorState
    :
    isIoAllowed
    ()
     
    #855
            
    +---[
    0.01968ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11InputBuffer
    :
    nextRequest
    ()
     
    #856
            
    +---[
    0.010065ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11OutputBuffer
    :
    nextRequest
    ()
     
    #857
            
    +---[
    0.002576ms
    ]
     org
    .
    apache
    .
    coyote
    .
    RequestInfo
    :
    setStage
    ()
     
    #870
            
    +---[
    0.016599ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    processSendfile
    ()
     
    #872
            
    +---[
    0.008182ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11InputBuffer
    :
    getParsingRequestLinePhase
    ()
     
    #688
            
    +---[
    0.0075ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    handleIncompleteRequestLineRead
    ()
     
    #690
            
    +---[
    0.001979ms
    ]
     org
    .
    apache
    .
    coyote
    .
    RequestInfo
    :
    setStage
    ()
     
    #875
            
    +---[
    0.001981ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    getErrorState
    ()
     
    #877
            
    +---[
    0.001934ms
    ]
     org
    .
    apache
    .
    coyote
    .
    ErrorState
    :
    isError
    ()
     
    #877
            
    +---[
    0.001995ms
    ]
     org
    .
    apache
    .
    tomcat
    .
    util
    .
    net
    .
    AbstractEndpoint
    :
    isPaused
    ()
     
    #877
            
    +---[
    0.002403ms
    ]
     org
    .
    apache
    .
    coyote
    .
    http11
    .
    Http11Processor
    :
    isAsync
    ()
     
    #879
            
    `---[0.006176ms] org.apache.coyote.http11.Http11Processor:isUpgrade() #881

    日志里有一个129ms的耗时点(时间比没开arthas的时候更长是因为arthas本身带来的性能消耗,所以生产环境小心使用),这个就是要找的问题点。

    打问题点找到了,那怎么定位是什么导致的问题呢,又如何解决呢?

    继续trace吧,细化到具体的代码块或者内容。trace由于性能考虑,不会展示所有的调用路径,如果调用路径过深,只有手动深入trace,原则就是trace耗时长的那个方法:

    [
    arthas@24851
    ]
    $ trace org
    .
    apache
    .
    coyote
    .
    Adapter
     service
    Press
     Q 
    or
     
    Ctrl
    +
    C to abort
    .
    Affect
    (
    class
    -
    cnt
    :
    1
     
    ,
     method
    -
    cnt
    :
    1
    )
     cost 
    in
     
    608
     ms
    .
    `---ts=2019-09-14 21:34:33;thread_name=http-nio-7744-exec-1;id=10;is_daemon=true;priority=5;TCCL=org.springframework.boot.loader.LaunchedURLClassLoader@20ad9418
        `
    ---[
    81.70999ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    CoyoteAdapter
    :
    service
    ()
            
    +---[
    0.032546ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Request
    :
    getNote
    ()
     
    #302
            
    +---[
    0.007148ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Response
    :
    getNote
    ()
     
    #303
            
    +---[
    0.007475ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    Connector
    :
    getXpoweredBy
    ()
     
    #324
            
    +---[
    0.00447ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Request
    :
    getRequestProcessor
    ()
     
    #331
            
    +---[
    0.007902ms
    ]
     java
    .
    lang
    .
    ThreadLocal
    :
    get
    ()
     
    #331
            
    +---[
    0.006522ms
    ]
     org
    .
    apache
    .
    coyote
    .
    RequestInfo
    :
    setWorkerThreadName
    ()
     
    #331
            
    +---[
    73.793798ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    CoyoteAdapter
    :
    postParseRequest
    ()
     
    #336
            
    +---[
    0.001536ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    Connector
    :
    getService
    ()
     
    #339
            
    +---[
    0.004469ms
    ]
     org
    .
    apache
    .
    catalina
    .
    Service
    :
    getContainer
    ()
     
    #339
            
    +---[
    0.007074ms
    ]
     org
    .
    apache
    .
    catalina
    .
    Engine
    :
    getPipeline
    ()
     
    #339
            
    +---[
    0.004334ms
    ]
     org
    .
    apache
    .
    catalina
    .
    Pipeline
    :
    isAsyncSupported
    ()
     
    #339
            
    +---[
    0.002466ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    Request
    :
    setAsyncSupported
    ()
     
    #339
            
    +---[
    6.01E-4ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    Connector
    :
    getService
    ()
     
    #342
            
    +---[
    0.001859ms
    ]
     org
    .
    apache
    .
    catalina
    .
    Service
    :
    getContainer
    ()
     
    #342
            
    +---[
    9.65E-4ms
    ]
     org
    .
    apache
    .
    catalina
    .
    Engine
    :
    getPipeline
    ()
     
    #342
            
    +---[
    0.005231ms
    ]
     org
    .
    apache
    .
    catalina
    .
    Pipeline
    :
    getFirst
    ()
     
    #342
            
    +---[
    7.239154ms
    ]
     org
    .
    apache
    .
    catalina
    .
    Valve
    :
    invoke
    ()
     
    #342
            
    +---[
    0.006904ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    Request
    :
    isAsync
    ()
     
    #345
            
    +---[
    0.00509ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    Request
    :
    finishRequest
    ()
     
    #372
            
    +---[
    0.051461ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    Response
    :
    finishResponse
    ()
     
    #373
            
    +---[
    0.007244ms
    ]
     java
    .
    util
    .
    concurrent
    .
    atomic
    .
    AtomicBoolean
    :<
    init
    >()
     
    #379
            
    +---[
    0.007314ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Response
    :
    action
    ()
     
    #380
            
    +---[
    0.004518ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    Request
    :
    isAsyncCompleting
    ()
     
    #382
            
    +---[
    0.001072ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    Request
    :
    getContext
    ()
     
    #394
            
    +---[
    0.007166ms
    ]
     java
    .
    lang
    .
    System
    :
    currentTimeMillis
    ()
     
    #401
            
    +---[
    0.004367ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Request
    :
    getStartTime
    ()
     
    #401
            
    +---[
    0.011483ms
    ]
     org
    .
    apache
    .
    catalina
    .
    Context
    :
    logAccess
    ()
     
    #401
            
    +---[
    0.0014ms
    ]
     org
    .
    apache
    .
    coyote
    .
    Request
    :
    getRequestProcessor
    ()
     
    #406
            
    +---[
    min
    =
    8.0E-4ms
    ,
    max
    =
    9.22E-4ms
    ,
    total
    =
    0.001722ms
    ,
    count
    =
    2
    ]
     java
    .
    lang
    .
    Integer
    :<
    init
    >()
     
    #406
            
    +---[
    0.001082ms
    ]
     java
    .
    lang
    .
    reflect
    .
    Method
    :
    invoke
    ()
     
    #406
            
    +---[
    0.001851ms
    ]
     org
    .
    apache
    .
    coyote
    .
    RequestInfo
    :
    setWorkerThreadName
    ()
     
    #406
            
    +---[
    0.035805ms
    ]
     org
    .
    apache
    .
    catalina
    .
    connector
    .
    Request
    :
    recycle
    ()
     
    #410
            
    `---[0.007849ms] org.apache.catalina.connector.Response:recycle() #411

    一段无聊的手动深入trace之后………………

    [
    arthas@24851
    ]
    $ trace org
    .
    apache
    .
    catalina
    .
    webresources
    .
    AbstractArchiveResourceSet
     getArchiveEntries
    Press
     Q 
    or
     
    Ctrl
    +
    C to abort
    .
    Affect
    (
    class
    -
    cnt
    :
    4
     
    ,
     method
    -
    cnt
    :
    2
    )
     cost 
    in
     
    150
     ms
    .
    `---ts=2019-09-14 21:36:26;thread_name=http-nio-7744-exec-3;id=12;is_daemon=true;priority=5;TCCL=org.springframework.boot.loader.LaunchedURLClassLoader@20ad9418
        `
    ---[
    75.743681ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    JarWarResourceSet
    :
    getArchiveEntries
    ()
            
    +---[
    0.025731ms
    ]
     java
    .
    util
    .
    HashMap
    :<
    init
    >()
     
    #106
            
    +---[
    0.097729ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    JarWarResourceSet
    :
    openJarFile
    ()
     
    #109
            
    +---[
    0.091037ms
    ]
     java
    .
    util
    .
    jar
    .
    JarFile
    :
    getJarEntry
    ()
     
    #110
            
    +---[
    0.096325ms
    ]
     java
    .
    util
    .
    jar
    .
    JarFile
    :
    getInputStream
    ()
     
    #111
            
    +---[
    0.451916ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    TomcatJarInputStream
    :<
    init
    >()
     
    #113
            
    +---[
    min
    =
    0.001175ms
    ,
    max
    =
    0.001176ms
    ,
    total
    =
    0.002351ms
    ,
    count
    =
    2
    ]
     java
    .
    lang
    .
    Integer
    :<
    init
    >()
     
    #114
            
    +---[
    0.00104ms
    ]
     java
    .
    lang
    .
    reflect
    .
    Method
    :
    invoke
    ()
     
    #114
            
    +---[
    0.045105ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    TomcatJarInputStream
    :
    getNextJarEntry
    ()
     
    #114
            
    +---[
    min
    =
    5.02E-4ms
    ,
    max
    =
    0.008531ms
    ,
    total
    =
    0.028864ms
    ,
    count
    =
    31
    ]
     java
    .
    util
    .
    jar
    .
    JarEntry
    :
    getName
    ()
     
    #116
            
    +---[
    min
    =
    5.39E-4ms
    ,
    max
    =
    0.022805ms
    ,
    total
    =
    0.054647ms
    ,
    count
    =
    31
    ]
     java
    .
    util
    .
    HashMap
    :
    put
    ()
     
    #116
            
    +---[
    min
    =
    0.004452ms
    ,
    max
    =
    34.479307ms
    ,
    total
    =
    74.206249ms
    ,
    count
    =
    31
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    TomcatJarInputStream
    :
    getNextJarEntry
    ()
     
    #117
            
    +---[
    0.018358ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    TomcatJarInputStream
    :
    getManifest
    ()
     
    #119
            
    +---[
    0.006429ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    JarWarResourceSet
    :
    setManifest
    ()
     
    #120
            
    +---[
    0.010904ms
    ]
     org
    .
    apache
    .
    tomcat
    .
    util
    .
    compat
    .
    JreCompat
    :
    isJre9Available
    ()
     
    #121
            
    +---[
    0.003307ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    TomcatJarInputStream
    :
    getMetaInfEntry
    ()
     
    #133
            
    +---[
    5.5E-4ms
    ]
     java
    .
    util
    .
    jar
    .
    JarEntry
    :
    getName
    ()
     
    #135
            
    +---[
    6.42E-4ms
    ]
     java
    .
    util
    .
    HashMap
    :
    put
    ()
     
    #135
            
    +---[
    0.001981ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    TomcatJarInputStream
    :
    getManifestEntry
    ()
     
    #137
            
    +---[
    0.064484ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    TomcatJarInputStream
    :
    close
    ()
     
    #141
            
    +---[
    0.007961ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    JarWarResourceSet
    :
    closeJarFile
    ()
     
    #151
            
    `---[0.004643ms] java.io.InputStream:close() #155

    发现了一个值得暂停思考的点:


    +---[min=0.004452ms,max=34.479307ms,total=74.206249ms,count=31] org.apache.catalina.webresources.TomcatJarInputStream:getNextJarEntry() #117

    这行代码加载了31次,一共耗时74ms;从名字上看,应该是tomcat加载jar包时的耗时,那么是加载了31个jar包的耗时,还是加载了jar包内的某些资源31次耗时呢?

    TomcatJarInputStream这个类源码的注释写到:

    The purpose of this sub-class is to obtain references to the JarEntry objects for META-INF/ and META-INF/MANIFEST.MF that are otherwise swallowed by the JarInputStream implementation. 大概意思也就是,获取jar包内META-INF/,META-INF/MANIFEST的资源,这是一个子类,更多的功能在父类JarInputStream里。

    其实看到这里大概也能猜到问题了,tomcat加载jar包内META-INF/,META-INF/MANIFEST的资源导致的耗时,至于为什么连续请求不会耗时,应该是tomcat的缓存机制(下面介绍源码分析)

    不着急定位问题,试着通过Arthas最终定位问题细节,继续手动深入trace

    [
    arthas@24851
    ]
    $ trace org
    .
    apache
    .
    catalina
    .
    webresources
    .
    TomcatJarInputStream
     
    *
    Press
     Q 
    or
     
    Ctrl
    +
    C to abort
    .
    Affect
    (
    class
    -
    cnt
    :
    1
     
    ,
     method
    -
    cnt
    :
    4
    )
     cost 
    in
     
    44
     ms
    .
    `---ts=2019-09-14 21:37:47;thread_name=http-nio-7744-exec-5;id=14;is_daemon=true;priority=5;TCCL=org.springframework.boot.loader.LaunchedURLClassLoader@20ad9418
        `
    ---[
    0.234952ms
    ]
     org
    .
    apache
    .
    catalina
    .
    webresources
    .
    TomcatJarInputStream
    :
    createZipEntry
    ()
            
    +---[
    0.039455ms
    ]
     java
    .
    util
    .
    jar
    .
    JarInputStream
    :
    createZipEntry
    ()
     
    #43
            
    `---[0.007827ms] java.lang.String:equals() #44
    
    `
    ---
    ts
    =
    2019
    -
    09
    -
    14
     
    21
    :
    37
    :
    47
    ;
    thread_name
    =
    http
    -
    nio
    -
    7744
    -
    exec
    -
    5
    ;
    id
    =
    14
    ;
    is_daemon
    =
    true
    ;
    priority
    =
    5
    ;
    TCCL
    =
    org
    .
    springframework
    .
    boot
    .
    loader
    .
    LaunchedURLClassLoader@20ad9418
        
    `---[0.050222ms] org.apache.catalina.webresources.TomcatJarInputStream:createZipEntry()
            +---[0.001889ms] java.util.jar.JarInputStream:createZipEntry() #43
            `
    ---[
    0.001643ms
    ]
     java
    .
    lang
    .
    String
    :
    equals
    ()
     
    #46
    #这里一共31个trace日志,删减了剩下的

    从方法名上看,还是加载资源之类的意思。都已经到jdk源码了,这时候来看一下TomcatJarInputStream这个类的源码:

    /**
     * Creates a new <code>JarEntry</code> (<code>ZipEntry</code>) for the
     * specified JAR file entry name. The manifest attributes of
     * the specified JAR file entry name will be copied to the new
     * <CODE>JarEntry</CODE>.
     *
     * @param name the name of the JAR/ZIP file entry
     * @return the <code>JarEntry</code> object just created
     */
    protected
     
    ZipEntry
     createZipEntry
    (
    String
     name
    )
     
    {
        
    JarEntry
     e 
    =
     
    new
     
    JarEntry
    (
    name
    );
        
    if
     
    (
    man 
    !=
     
    null
    )
     
    {
            e
    .
    attr 
    =
     man
    .
    getAttributes
    (
    name
    );
        
    }
        
    return
     e
    ;
    }

    这个createZipEntry有个name参数,从注释上看,是jar/zip文件名,如果能得到文件名这种关键信息,就可以直接定位问题了;还是通过Arthas,使用watch命令,动态监测方法调用数据

    watch方法执行数据观测

    让你能方便的观察到指定方法的调用情况。能观察到的范围为:返回值、抛出异常、入参,通过编写 OGNL 表达式进行对应变量的查看。

    watch 该方法的入参

    1. [arthas@24851]$ watch org.apache.catalina.webresources.TomcatJarInputStream createZipEntry "{params[0]}"

    2. Press Q or Ctrl+C to abort.

    3. Affect(class-cnt:1 , method-cnt:1) cost in 27 ms.

    4. ts=2019-09-14 21:51:14; [cost=0.14547ms] result=@ArrayList[

    5. @String[META-INF/],

    6. ]

    7. ts=2019-09-14 21:51:14; [cost=0.048028ms] result=@ArrayList[

    8. @String[META-INF/MANIFEST.MF],

    9. ]

    10. ts=2019-09-14 21:51:14; [cost=0.046071ms] result=@ArrayList[

    11. @String[META-INF/resources/],

    12. ]

    13. ts=2019-09-14 21:51:14; [cost=0.033855ms] result=@ArrayList[

    14. @String[META-INF/resources/swagger-ui.html],

    15. ]

    16. ts=2019-09-14 21:51:14; [cost=0.039138ms] result=@ArrayList[

    17. @String[META-INF/resources/webjars/],

    18. ]

    19. ts=2019-09-14 21:51:14; [cost=0.033701ms] result=@ArrayList[

    20. @String[META-INF/resources/webjars/springfox-swagger-ui/],

    21. ]

    22. ts=2019-09-14 21:51:14; [cost=0.033644ms] result=@ArrayList[

    23. @String[META-INF/resources/webjars/springfox-swagger-ui/favicon-16x16.png],

    24. ]

    25. ts=2019-09-14 21:51:14; [cost=0.033976ms] result=@ArrayList[

    26. @String[META-INF/resources/webjars/springfox-swagger-ui/springfox.css],

    27. ]

    28. ts=2019-09-14 21:51:14; [cost=0.032818ms] result=@ArrayList[

    29. @String[META-INF/resources/webjars/springfox-swagger-ui/swagger-ui-standalone-preset.js.map],

    30. ]

    31. ts=2019-09-14 21:51:14; [cost=0.04651ms] result=@ArrayList[

    32. @String[META-INF/resources/webjars/springfox-swagger-ui/swagger-ui.css],

    33. ]

    34. ts=2019-09-14 21:51:14; [cost=0.034793ms] result=@ArrayList[

    35. @String[META-INF/resources/webjars/springfox-swagger-ui/swagger-ui.js.map],

    这下直接看到了具体加载的资源名,这么熟悉的名字:swagger-ui,一个国外的rest接口文档工具,又有国内开发者基于swagger-ui做了一套spring mvc的集成工具,通过注解就可以自动生成swagger-ui需要的接口定义json文件,用起来还比较方便,就是侵入性较强。

    删除swagger的jar包后问题,诡异的70+ms就消失了

    1. <!--pom 里删除这两个引用,这两个包时国内开发者封装的,swagger-ui并没有提供java spring-mvc的支持包,swagger只是一个浏览器端的ui+editor

    2. <dependency>

    3. <groupId>io.springfox</groupId>

    4. <artifactId>springfox-swagger2</artifactId>

    5. <version>2.9.2</version>

    6. </dependency>

    7. <dependency>

    8. <groupId>io.springfox</groupId>

    9. <artifactId>springfox-swagger-ui</artifactId>

    10. <version>2.9.2</version>

    11. </dependency>

    那么为什么swagger会导致请求耗时呢,为什么每次请求偶读会加载swagger内部的静态资源呢?

    其实这是tomcat-embed的一个bug吧,下面详细介绍一下该Bug

    Tomcat embed Bug分析&解决 源码分析过程实在太漫长,而且也不是本文的重点,所以就不介绍了, 下面直接介绍下分析结果

    顺便贴一张tomcat处理请求的核心类图

    为什么每次请求会加载Jar包内的静态资源 关键在于org.apache.catalina.mapper.Mapper#internalMapWrapper这个方法,该版本下处理请求的方式有问题,导致每次都校验静态资源。

    为什么连续请求不会出现问题

    因为Tomcat对于这种静态资源的解析是有缓存的,优先从缓存查找,缓存过期后再重新解析。具体参考org.apache.catalina.webresources.Cache,默认过期时间ttl是5000ms。

    为什么本地不会复现

    其实确切的说,是通过spring-boot打包插件后不能复现。由于启动方式的不同,tomcat使用了不同的类去处理静态资源,所以没问题

    如何解决

    升级tomcat-embed版本即可

    当前出现Bug的版本为:

    spring-boot:2.0.2.RELEASE,内置的tomcat embed版本为8.5.31

    升级tomcat embed版本至8.5.40+即可解决此问题,新版本已经修复了

    通过替换springboot pom properties方式 如果项目是maven是继承的springboot,即parent配置为springboot的,或者dependencyManagement中import spring boot包的

    1. <parent>

    2. <groupId>org.springframework.boot</groupId>

    3. <artifactId>spring-boot-starter-parent</artifactId>

    4. <version>2.0.2.RELEASE</version>

    5. <relativePath/> <!-- lookup parent from repository -->

    6. </parent>

    pom中直接覆盖properties即可:

    1. <properties>

    2. <tomcat.version>8.5.40</tomcat.version>

    3. </properties>

    升级spring boot版本

    springboot 2.1.0.RELEASE中的tomcat embed版本已经大于8.5.31了,所以直接将springboot升级至该版本及以上版本就可以解决此问题。

  • 相关阅读:
    从getaddrinfo看Glibc的nss
    跟踪一次网络发送
    Dwarf2结构在gcc中的应用及调试器实现分析
    scrapy基础教程
    leetcode题目清单
    机器学习——入门教程2
    机器学习——入门教程1
    C++ STL之vector用法总结
    Mac下用g++编译opencv程序报错
    Linux下环境变量设置
  • 原文地址:https://www.cnblogs.com/shihaiming/p/12434851.html
Copyright © 2020-2023  润新知