2

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

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

例如:

  • netstat我能够识别长期存在的套接字,这可以让我计算当前卡住的请求(虽然不是 URI),但是 HTTP 保持活动使这种方法无效

  • kill -3 <server_pid>我可以多次堆栈转储应用程序服务器(

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

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

  • 我发现最接近的是 tomcat7 的StuckThreadDetectionValve,它会定期报告长时间运行的调用,但它输出堆栈跟踪(不是 URI)并且不提供“实时”数据(例如,仅定期轮询,淹没日志并让我们查看状态1-60 秒前,但不是“现在”)

也许我只是缺少/忽略了重要/核心/基本的 tomcat 功能之一?或者也许 weblogic(或任何其他应用程序服务器)为此提供了强大的功能?

没有这样简单而重要的功能,我有点迷失了。帮助?请?

4

2 回答 2

2

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

  1. invoke(...)方法仅用于getNext().invoke(request,response)调用剩余阀门链和实际的处理程序/执行程序
  2. log(...)上述完成调用方法

所以我们只需要:

  • 也调用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
于 2014-01-18T07:27:18.193 回答
1

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

于 2014-01-15T01:34:50.263 回答