如何识别活动(长时间运行)HTTP请求的URI?

时间:2014-01-13 23:25:26

标签: java java-ee tomcat weblogic tomcat-valve

想象一个webapp(有时)需要很长时间才能响应某些HTTP(POST / GET / etc)请求 - 你如何在服务器端找到这样的请求?

到目前为止,我已经使用tomcat AccessLogValve来查看" 已完成"请求,但这并不能让我看到" 正在进行中" (卡住):(

例如:

  • 使用netstat我能够识别长期存在的套接字,它可以给我一些当前卡住的请求(虽然不是URI)但是HTTP保持无效这种方法< / p>

  • 我可以多次堆叠app-server(kill -3 <server_pid>)并猜测哪些线程运行时间长,并对URI进行逆向工程 - 不是一种智能方式

  • 我可以在网络应用服务器(替代主机名,克隆证书)前注入一个路由器/代理,这将显示当前正在运行的呼叫 - 不是一个简单的方法

  • 我可以继续运行tcpdump并解析流量以保留当前运行的URI列表,但是如何处理httpS呢?

  • 我发现最接近的是tomcat7&#39; s StuckThreadDetectionValve,它定期报告长时间运行的呼叫,但它会输出堆栈跟踪(不是URI)并且不提供& #34;活&#34;数据(例如,只定期轮询,充斥日志并让我们看到1-60秒前的状态,但不是&#34;现在&#34;)

也许我只是缺少/忽略了一个重要的/核心/基本的tomcat功能?或者weblogic(或任何其他app-server)可以为此提供强大的功能?

如果没有这样简单而重要的功能,我就会迷失方向。 救命?请?

2 个答案:

答案 0 :(得分:1)

不幸的是,没有一种简单的方法来获取花费很长时间的飞行中HTTP请求的列表。正如您所提到的,在几秒钟内进行几次线程转储会告诉您哪些线程正在缓慢执行HTTP操作(因为每个线程堆栈在等待响应的每个线程堆栈中都是相同的)。但是,除非您可以使用URL将代码返回到静态代码段,否则它不会告诉您更多。但是,您可以获取线程转储并识别线程ID,然后进行堆转储并在堆转储中查找这些线程。虽然不是直截了当而且绝对不简单,但您可以获取正在使用的URL,等待的时间等等。

答案 1 :(得分:1)

好的 - 创建我自己的 Valve 是一种正确而简单的方法,下面分享。 Apache确实多次重做 AccessLogValve ,但所有修订都遵循相同的概念:

  1. invoke(...)方法只使用getNext().invoke(request,response)来调用一系列剩余的阀门和实际的处理程序/执行程序
  2. log(...)方法在上面的完成
  3. 后调用

    所以我们只需要:

    • 还会在<{strong> log(...)
    • 之前调用getNext().invoke(request,response)
    • 修改log(...)以区分“之前”和“之后”调用

    最简单的方法是:

    @Override
    public void invoke(Request request, Response response) throws IOException, ServletException {
        log(request, response, -1); // negative time indicates "before"
        super.invoke(request, response);
    }
    

    但是tomcat_6.0.16代码不能很好地扩展,所以我用Thread.getName()和“之前”/“之后”指示符对日志消息(以硬编码方式)作为前缀。此外,我更喜欢使用反射来访问private AccessLogValve.getDate()

    package org.apache.catalina.valves;
    
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.Date;
    import javax.servlet.ServletException;
    import org.apache.catalina.connector.Request;
    import org.apache.catalina.connector.Response;
    
    public class PreAccessLogValve extends AccessLogValve {
        @Override
        public void invoke(Request request, Response response) throws IOException, ServletException {
            long timeStart = System.currentTimeMillis();
            log(request, response, -timeStart); // negative time indicates "before" request
            getNext().invoke(request, response);
            log(request, response, System.currentTimeMillis() - timeStart); // actual (positive) - "after"
        }
    
        public void log(Request request, Response response, long time) {
            if (started && getEnabled() && null != logElements && (null == condition || null == request.getRequest().getAttribute(condition))) {
                StringBuffer result = new StringBuffer();
                try {
                    Date date = (Date) methodGetDate.invoke(this); 
                    for (int i = 0; i < logElements.length; i++) {
                        logElements[i].addElement(result, date, request, response, time);
                    }
                } catch (Throwable t) { t.printStackTrace(); }
                log(Thread.currentThread().getName() + (time<0?" > ":" < ") + result.toString());
            }
        }
    
        private static final Method methodGetDate;
        static {
            Method m = null;
            try {
                m = AccessLogValve.class.getDeclaredMethod("getDate");
                m.setAccessible(true);
            } catch (Throwable t) { t.printStackTrace(); }
            methodGetDate = m;
        }
    }
    

    使用 catalina.jar + servlet-api.jar 编译上面的代码并生成新的 catalina-my.jar ,它被放入tomcat / lib文件夹。之后 - 我修改了server.xml以获得:

    <Valve className="org.apache.catalina.valves.PreAccessLogValve"
        directory="/tmp" prefix="test." suffix=".txt"
        pattern="%a %t %m %U %s %b %D" resolveHosts="false" buffered="false"/>
    

    以下是示例输出:

    http-8007-exec-1 > 10.61.105.105 [18/Jan/2014:05:54:14 +0000] POST /admin/0$en_US/secure/enduser/search.do 200 - -1390024454470
    http-8007-exec-5 > 10.61.105.105 [18/Jan/2014:05:54:17 +0000] GET /admin/0$en_US/secure/enduser/search.do 200 - -1390024457300
    http-8007-exec-5 < 10.61.105.105 [18/Jan/2014:05:54:17 +0000] GET /admin/0$en_US/secure/enduser/search.do 200 13933 44
    http-8007-exec-3 > 10.61.105.105 [18/Jan/2014:05:54:17 +0000] GET /admin/html/main.js 200 - -1390024457490
    http-8007-exec-3 < 10.61.105.105 [18/Jan/2014:05:54:17 +0000] GET /admin/html/main.js 200 3750 0
    http-8007-exec-5 > 10.61.105.105 [18/Jan/2014:05:54:17 +0000] GET /admin/images/layout/logo.gif 200 - -1390024457497
    http-8007-exec-5 < 10.61.105.105 [18/Jan/2014:05:54:17 +0000] GET /admin/images/layout/logo.gif 200 1996 0
    http-8007-exec-1 < 10.61.105.105 [18/Jan/2014:05:54:24 +0000] POST /admin/0$en_US/secure/enduser/search.do 200 13308 10209
    

    这样可以随时轻松检索所有“进行中”的URI:

    [root@serv1 tomcat]# awk '{if(">"==$2){if($1 in S)print S[$1];S[$1]=$0}else delete S[$1]}END{for(i in S)print S[i]}' test
    http-8007-exec-4 > 10.61.105.105 [18/Jan/2014:06:13:20 +0000] GET /admin/images/1x1blank.gif 200 - -13
    http-8007-exec-2 > 10.61.105.105 [18/Jan/2014:06:13:16 +0000] POST /admin/servlet/handlersvr 200 - -13