一步一步写APM(二)--拦截Tomcat监控WebApp

知识准备,org.apache.catalina.core.StandardWrapperValve

StandardWrapper代表的是一个Servlet,那么它也就是容器调用栈中的最终点.Http请求到达后进行的处理也不单单只是调用了Servlet的方法.它还经过了一层的过滤器.它对外服务与上层容器类似,也是调用了基础阀的方法.那么所有这些操作也是在StandardWrapperValve这个阀中发生的.

因此,如果我们能够拦截到StandardWrapperValve中的相关函数,那么,所有HTTP请求都会经过我们的拦截器,这就实现了我们监控所有用户调用以及请求的目的。具体关于StandardWrapperValve的资料此处不详细说明,请自行谷歌。

开始改造,阶段Tag:改造拦截Tomcat

  1. 添加一个tomcat用于测试,此处省略代码

  2. 依然给tomcat运行添加javaagent参数,此处是Idea的配置界面,运行后控制台出现Enter premain.....即可: image.png

  3. 修改premain拦截的类以及拦截的方法

new AgentBuilder.Default().type(named("org.apache.catalina.core.StandardWrapperValve")).transform(new AgentBuilder.Transformer() {
            @Override
            public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader) {
                System.out.println("transform...");

                ClassInstanceMethodInterceptor methodInterceptor = new ClassInstanceMethodInterceptor();
                ElementMatcher matcher = named("invoke");
                builder = builder.method(not(isStatic()).and(matcher)).intercept(MethodDelegation.to(methodInterceptor));

                return builder;
            }
        }).with(...)...

这里拦截类org.apache.catalina.core.StandardWrapperValveinvoke方法

  1. 重新package APMAgent,并重新运行tomcat。
D:\Develop\apache-tomcat-7.0.78_2\bin\catalina.bat run
.....
Enter premain.....
.....
信息: Server startup in 126 ms
Connected to server
[2018-02-26 12:01:57,944] Artifact APMAgentTestTomcat:war: Artifact is being deployed, please wait...
transform...
.....
intercept invoke
intercept invoke
intercept invoke

此时,每刷新一次页面,控制台就会打印一次intercept invoke,所以已经实现拦截用户的请求

  1. 现在我们需要一些用户请求的详细信息,修改ClassInstanceMethodInterceptor类的intercept方法
long start = System.currentTimeMillis();
        HttpServletRequest request = (HttpServletRequest) allArguments[0];

        Object result = null;
        try {
            result = zuper.call();
        } catch (Throwable t) {
            t.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("intercept " + method.getName() + " ,user requested url:" + request.getRequestURI() + " ,costs:" + (end - start));

        return result;
  1. 再次package 运行tomcat
intercept invoke ,user requested url:/ ,costs:2
intercept invoke ,user requested url:/test ,costs:3

到目前为止,我们已经能够监控用户的访问URL以及本次访问的耗时,如果完善的话,我们就可以监控所有用户请求,并且可以找出哪些请求的耗时较长需要优化。

结语

当然,调用链的不止于此,既然称为链,那么就一定有一台完整的链式解构,我们暂时只完成了一个节点的监控。但是如果将这里拓展开,比如Controller中有redis请求,有mysql请求,这些我们都监控起来的话我们就能找出一台链中哪些不见耗费的时间较长,那么这些部分就是我们需要优化的地方。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×