V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hengyunabc
V2EX  ›  Java

Arthas 实践:是哪个 Controller 处理了请求?

  •  
  •   hengyunabc ·
    hengyunabc · 2019-06-05 10:47:08 +08:00 · 2043 次点击
    这是一个创建于 2027 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景

    Arthas 是阿里巴巴开源的 Java 诊断利器,深受开发者喜爱。

    之前分享了 Arthas 怎样排查 404/401 的问题: http://hengyunabc.github.io/arthas-spring-boot-404-401/

    我们可以快速定位一个请求是被哪些Filter拦截的,或者请求最终是由哪些Servlet处理的。

    但有时,我们想知道一个请求是被哪个 Spring MVC Controller 处理的。如果翻代码的话,会比较难找,并且不一定准确。

    通过 Arthas 可以精确定位是哪个Controller处理请求。

    Demo

    还是以这个 demo 为例: https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-404-401

    启动之后,访问: http://localhost:8080/user/1,会返回一个 user 对象。那么这个请求是被哪个Controller处理的呢?

    trace 定位 DispatcherServlet

    我们先试下跟踪Servlet

    trace javax.servlet.Servlet *
    

    从 trace 的结果可以看出来,请求最终是被DispatcherServlet#doDispatch()处理了,但是没有办法知道是哪个Controller处理。

    `---[27.453122ms] org.springframework.web.servlet.DispatcherServlet:doDispatch()
        +---[0.005822ms] org.springframework.web.context.request.async.WebAsyncUtils:getAsyncManager() #929
        +---[0.107365ms] org.springframework.web.servlet.DispatcherServlet:checkMultipart() #936
        |   `---[0.062451ms] org.springframework.web.servlet.DispatcherServlet:checkMultipart()
        |       `---[0.016924ms] org.springframework.web.multipart.MultipartResolver:isMultipart() #1093
        +---[2.103935ms] org.springframework.web.servlet.DispatcherServlet:getHandler() #940
        |   `---[2.036042ms] org.springframework.web.servlet.DispatcherServlet:getHandler()
    

    watch 定位 handler

    trace 结果里把调用的行号打印出来了,我们可以直接在 IDE 里查看代码(也可以用 jad 命令反编译):

    // org.springframework.web.servlet.DispatcherServlet
    	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		HttpServletRequest processedRequest = request;
    		HandlerExecutionChain mappedHandler = null;
    		boolean multipartRequestParsed = false;
    
    		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
    		try {
    			ModelAndView mv = null;
    			Exception dispatchException = null;
    
    			try {
    				processedRequest = checkMultipart(request);
    				multipartRequestParsed = (processedRequest != request);
    
    				// Determine handler for the current request.
    				mappedHandler = getHandler(processedRequest);
    				if (mappedHandler == null || mappedHandler.getHandler() == null) {
    					noHandlerFound(processedRequest, response);
    					return;
    				}
    
    • 仔细看代码,可以发现mappedHandler = getHandler(processedRequest);得到了处理请求的 handler

    下面用watch命令来获取getHandler函数的返回结果。

    watch之后,再次访问 http://localhost:8080/user/1

    $ watch org.springframework.web.servlet.DispatcherServlet getHandler returnObj
    Press Q or Ctrl+C to abort.
    Affect(class-cnt:1 , method-cnt:1) cost in 332 ms.
    ts=2019-06-04 11:38:06; [cost=2.75218ms] result=@HandlerExecutionChain[
        logger=@SLF4JLocationAwareLog[org.apache.commons.logging.impl.SLF4JLocationAwareLog@665c08a],
        handler=@HandlerMethod[public com.example.demo.arthas.user.User com.example.demo.arthas.user.UserController.findUserById(java.lang.Integer)],
        interceptors=null,
        interceptorList=@ArrayList[isEmpty=false;size=2],
        interceptorIndex=@Integer[-1],
    ]
    

    可以看到处理请求的 handler 是 om.example.demo.arthas.user.UserController.findUserById

    总结

    • Spring MVC 的请求是在DispatcherServlet分发,查找到对应的mappedHandler来处理
    • 使用 Arthas 时,灵活结合代码,可以快速精确定位问题

    链接

    原文地址

    1 条回复    2019-06-05 11:41:39 +08:00
    fuchaofather
        1
    fuchaofather  
       2019-06-05 11:41:39 +08:00
    确实好用
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1184 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 23:50 · PVG 07:50 · LAX 15:50 · JFK 18:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.