2014-01-13 70 views
0

想象一下,有時需要很長時間才能響應某些HTTP(POST/GET/etc)請求的web應用程序 - 您如何在服務器端找到這樣的請求?如何識別活動(長時間運行)的HTTP請求的URI?

到目前爲止,我已經使用Tomcat的AccessLogValve看到「完成」的要求,但是,這並不讓我看到了「正在進行」(卡)的人:(

對於例如:

  • netstat我能夠識別長壽命插座可能給我當前卡住請求(不是的URI雖然)的計數,但保持HTTP否定這一做法

  • 我可以STACKDUMP應用服務器(kill -3 <server_pid>)多次和猜測哪些線程運行長和反向工程的URI - 不是一個聰明的辦法要麼

  • 我可以在網絡面前注入路由器/代理, -app服務器(替代主機名稱,克隆證書),它會顯示我當前正在運行的調用 - 不是一個簡單的方法

  • 我可能會不斷地運行tcpdump並解析流量以保留當前運行的URI列表,但是如何處理httpS呢?

  • 我發現的最接近的是tomcat7的StuckThreadDetectionValve,它定期報告長時間運行的調用,但它輸出堆棧跟蹤(不是URI),並且不提供「實時」數據(例如只有定期輪詢,洪泛日誌和可以看到的1-60秒前的狀態,而不是「現在」)

也許我只是缺少/俯瞰至關重要/核心/ tomcat的基本功能之一?或者也許weblogic(或任何其他應用服務器)有強大的東西提供這個?

我有點失落,沒有這樣簡單和必要的功能。 幫助?請?

+0

對於我遇到這個問題的一些報告,我讓他們在開始之前向表中插入一條記錄,並在完成時更新它,以跟蹤它運行的時間以及它被卡住的頻率。但是我提前知道哪些是servlet問題。 – developerwjk

+0

@developerwjk - 是的,我正在尋找類似的東西,但在tomcat範圍內(或任何其他應用程序服務器)級別上,而不是在servlet級別上。 – Vlad

回答

0

行 - 創建我自己的是一種適當和簡單的方法,共享下面。阿帕奇做返工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文件和產生新卡塔利娜-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可以方便地隨時檢索:

[[email protected] 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 
1

不幸的是,沒有一種簡單的方法可以獲取需要很長時間的空中HTTP請求列表。正如你所提到的,幾秒鐘內進行幾次線程轉儲會告訴你哪些線程正在緩慢地執行HTTP操作(因爲線程堆棧在等待響應的每個線程中都是相同的)。但是,它並沒有告訴你更多的東西,除非你可以用代碼回到帶有URL的靜態代碼片段。但是,您可以執行線程轉儲並確定線程ID,然後進行堆轉儲並在堆轉儲中查找這些線程。雖然不是直截了當,絕對不簡單,但您可以獲取正在使用的URL,已經等待的時間等等。

+0

嘗試過這種方式,但堆轉儲很大,暫時鎖定服務器,分析器花費的時間(4分鐘以上)使其遠離互動。我找到了一種簡單/直接的方式,並用單獨的答案回答。 – Vlad