Java Votary

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            48 隨筆 :: 1 文章 :: 80 評(píng)論 :: 0 Trackbacks
          解決JAVA服務(wù)器性能問題

          通過負(fù)載測(cè)試和分析來改善JAVA服務(wù)器應(yīng)用的性能

          作者:Ivan Small

          譯者:xMatrix





          版權(quán)聲明:任何獲得Matrix授權(quán)的網(wǎng)站,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明
          作者:Ivan Small;xMatrix
          原文地址:http://www.javaworld.com/javaworld/jw-02-2005/jw-0207-server-p3.html
          中文地址:http://www.matrix.org.cn/resource/article/43/43998_server_capacity.html
          關(guān)鍵詞: server capacity

          摘要

          改善JAVA服務(wù)器的性能需要模擬負(fù)載下的服務(wù)器。創(chuàng)建一個(gè)模擬環(huán)境、搜集數(shù)據(jù)并且分析結(jié)果可能是對(duì)許多開發(fā)人員的挑戰(zhàn)。這篇文章中的示例介紹了JAVA服務(wù)器性能分析的概念和工具。作者使用這個(gè)示例來研究超額請(qǐng)求次數(shù)下內(nèi)存使用和同步竟?fàn)幍挠绊憽?br>作者Ivan Small

          項(xiàng)目團(tuán)隊(duì)已經(jīng)很熟悉如何組織一些具體的任務(wù)并完成他們。簡(jiǎn)單的性能問題很容易由一個(gè)開發(fā)人員分離并解決。然而大的性能問題,通常在系統(tǒng)處于高負(fù)載情況下發(fā)生,就不是這么簡(jiǎn)單能處理的了。這些問題需要一個(gè)獨(dú)立的測(cè)試環(huán)境、一個(gè)模擬的負(fù)載,并且需要仔細(xì)地分析和跟蹤。

          在這篇文章中,我使用比較通用的工具和設(shè)備創(chuàng)建了一個(gè)測(cè)試環(huán)境。我會(huì)專注于兩個(gè)性能問題,內(nèi)存和同步,他們很難用簡(jiǎn)單的分析得到。通過一個(gè)具體的例子,我希望比較容易地解決復(fù)雜的性能問題而且可以提供處理問題過程中的細(xì)節(jié)。

          改善服務(wù)器的性能

          服 務(wù)器的性能改善是依賴于數(shù)據(jù)的。沒有可靠的數(shù)據(jù)基礎(chǔ)而更改應(yīng)用或環(huán)境會(huì)導(dǎo)致更差的結(jié)果。分析器提供有用的JAVA服務(wù)器應(yīng)用信息,但由于從單用戶負(fù)載下的 數(shù)據(jù)與多用戶負(fù)載下得到的數(shù)據(jù)是完全不同的,這導(dǎo)致分析器的數(shù)據(jù)并不精確。在開發(fā)階段使用分析器來優(yōu)化應(yīng)用的性能是一個(gè)好的方式,但在高負(fù)載下的應(yīng)用分析 可以取到更好的效果。


          在負(fù)載下分析服務(wù)器應(yīng)用的性能需要一些基本的元素:
                  1、可控的進(jìn)行應(yīng)用負(fù)載測(cè)試的環(huán)境。
                  2、可控的人造負(fù)載使得應(yīng)用滿負(fù)荷運(yùn)行。
                  3、來自監(jiān)視器、應(yīng)用和負(fù)載測(cè)試工具自身的數(shù)據(jù)搜集。
                  4、性能改變的跟蹤。

          不要低估最后一個(gè)需求(性能跟蹤)的重要性因?yàn)槿绻荒芨櫺阅苣憔筒荒軐?shí)際的管理項(xiàng)目。性能上10-20%的改善對(duì)單用戶環(huán)境來說并沒有什么不同,但對(duì)支持人員來說就不一樣了。20%的改善是非常大的,而且通過跟蹤性能的改善,你可以提供重要的反饋和持續(xù)跟蹤。
          雖然性能跟蹤很重要,但有時(shí)為了使后續(xù)的測(cè)試更加精確而不得不拋棄先前的測(cè)試結(jié)果。在性能測(cè)試中,改善負(fù)載測(cè)試的精確性可能需要修改模擬環(huán)境,而這些變化是必須的,通過變化前后的負(fù)載測(cè)試你可以觀察到其中的轉(zhuǎn)變。


          可控的環(huán)境        
          可 控的環(huán)境最少也需要兩臺(tái)獨(dú)立的機(jī)器和第三臺(tái)控制的機(jī)器。其中一臺(tái)用來生成負(fù)載,另一臺(tái)作為控制機(jī)與前一臺(tái)建立測(cè)試應(yīng)用并接受反饋,第三臺(tái)機(jī)器運(yùn)行應(yīng)用。此 外,負(fù)載和應(yīng)用機(jī)器間的網(wǎng)絡(luò)應(yīng)該與局域網(wǎng)分開。控制機(jī)接受運(yùn)行應(yīng)用機(jī)器的反饋如操作系統(tǒng)、硬件使用率、應(yīng)用(特別是VM)的狀態(tài)。

          負(fù)載模擬
          最精確的模擬通常用實(shí)際的用戶數(shù)據(jù)和WEB服務(wù)器端的訪問日志。如果你還沒有實(shí)際布署或者缺少實(shí)際的用戶數(shù)據(jù),你可以通過構(gòu)造類似的場(chǎng)景或詢問銷售和產(chǎn)品管理團(tuán)隊(duì)或做一些有依據(jù)的猜想。協(xié)調(diào)負(fù)載測(cè)試和實(shí)際用戶體驗(yàn)是一個(gè)持續(xù)的過程。

          在 模擬中一些用戶場(chǎng)景是必須的。如在一個(gè)通用地址薄應(yīng)用中,你應(yīng)該區(qū)分更新和查詢操作。在我的測(cè)試應(yīng)用中GrinderServlet類只有一個(gè)場(chǎng)景。單用 戶連接10次訪問這個(gè)servlet(在每一次訪問間有一段暫停)。雖然這個(gè)應(yīng)用很小,我認(rèn)為這可以重復(fù)一些常見的東西。用戶通常不會(huì)連接給服務(wù)器請(qǐng)求而 沒有間斷。如果沒有間斷,我們可能不能得到更精確的實(shí)際用戶上限。

          串行10個(gè)請(qǐng)求的另一個(gè)原因是實(shí)際應(yīng)用中不會(huì)只有一個(gè)HTTP請(qǐng)求。單一而又分離的請(qǐng)求可以影響環(huán)境中的許多因素。對(duì)Tomcat來說,會(huì)為每一個(gè)請(qǐng)求創(chuàng)建一個(gè)會(huì)話,并且HTTP協(xié)議允許不同的請(qǐng)求重用連接。我會(huì)修改一下負(fù)載測(cè)試來避免混洧。

          GrinderServlet類不會(huì)執(zhí)行任何排序操作,但這個(gè)需求在大部分應(yīng)用中都很普通。在這些應(yīng)用中,你需要?jiǎng)?chuàng)建模擬的數(shù)據(jù)集并且用他們來構(gòu)造相關(guān)用例的負(fù)載測(cè)試。

          例如,如果用例涉及到用戶登錄一個(gè)WEB應(yīng)用,從可能的用戶列表中選取隨機(jī)的用戶會(huì)只使用一個(gè)用戶更精確。否則,你可能不經(jīng)意地使用了系統(tǒng)緩存或其他的優(yōu)化或一些微妙的東西,而這會(huì)使得結(jié)果不正確。

          負(fù)載測(cè)試軟件
          負(fù) 載測(cè)試軟件可以構(gòu)造測(cè)試場(chǎng)景并且對(duì)服務(wù)進(jìn)行負(fù)載測(cè)試。我會(huì)在下面的示例中使用OpenSTA測(cè)試軟件。這軟件簡(jiǎn)單易學(xué),結(jié)果也很容易導(dǎo)出,并且支持參數(shù)化 腳本,還可以監(jiān)視信息的變化,他的主要缺點(diǎn)是基于Windows,但在這兒不是個(gè)問題。當(dāng)然還有很多可選項(xiàng)如Apache的JMeter和Mercury 的LoadRunner。

          The GrinderServlet

          列表1中顯示了GrinderServlet類,列表2中顯示了Grinder類
          Listing 1

          package pub.capart;

          import java.io.*;
          import java.util.*;
          import javax.servlet.*;
          import javax.servlet.http.*;

          public class GrindServlet extends HttpServlet {
             protected void doGet(HttpServletRequest req, HttpServletResponse res)
                   throws ServletException, IOException {
                Grinderv1 grinder = Grinderv1.getGrinder();
                long t1 = System.currentTimeMillis();
                grinder.grindCPU(13);
                long t2 = System.currentTimeMillis();

                PrintWriter pw = res.getWriter();
                pw.print("<html>\n< body> \n");
                pw.print("Grind Time = "+(t2-t1));
                pw.print("< body> \n< /html> \n");
             }
          }


          Listing 2

          package pub.capart;

          /**
          * This is a simple class designed to simulate an application consuming
          * CPU, memory, and contending for a synchronization lock.
          */
          public class Grinderv1 {
             private static Grinderv1 singleton = new Grinderv1();
             private static final String randstr =
                "this is just a random string that I'm going to add up many many times";

             public static Grinderv1 getGrinder() {
                return singleton;
             }
             public synchronized void grindCPU(int level) {
                StringBuffer sb = new StringBuffer();
                String s = randstr;
                for (int i=0;i<level;++i) {
                   sb.append(s);
                   s = getReverse(sb.toString());
                }
             }
             public String getReverse(String s) {
                StringBuffer sb = new StringBuffer(s);
                sb = sb.reverse();
                return sb.toString();
             }
          }


          類 很簡(jiǎn)單,但他們會(huì)產(chǎn)生兩個(gè)很常見的問題。咋一看瓶頸可能由grindCPU()方法的同步修飾符引起,但實(shí)際上內(nèi)存消耗才是真正的問題所在。如圖1,我的 第一個(gè)負(fù)載測(cè)試顯示了常見的負(fù)載變化。在這里負(fù)載變化很重要因?yàn)槟阏谀M一個(gè)高的負(fù)載。這種熱身的方式也更精確因?yàn)楸苊饬薐SP編譯引起的問題。我通常 習(xí)慣于在進(jìn)行負(fù)載測(cè)試前先進(jìn)行單用戶模擬。

          image
          Figure 1        

          我 在這篇文章中會(huì)使用相同的容量小結(jié)圖。在執(zhí)行負(fù)載測(cè)試時(shí)還有更多的可用信息,但這里只用了有用的部分。最上面的面板包含每秒完成的請(qǐng)求數(shù)和請(qǐng)求時(shí)間信息。 第二個(gè)面板包含活動(dòng)用戶數(shù)和失敗率,我將超時(shí)、不正確的服務(wù)器應(yīng)答和長(zhǎng)于5秒的請(qǐng)求認(rèn)為是失敗的。第三個(gè)面板包含JVM內(nèi)存統(tǒng)計(jì)和CPU使用率。CPU值 是所有處理器的用戶時(shí)間的平均值,這里所有的測(cè)試機(jī)器都是雙CPU的。內(nèi)存統(tǒng)計(jì)圖包含垃圾回收表和每秒垃圾回收數(shù)。

          圖1中兩個(gè)最明顯的數(shù)據(jù)是50%的CPU使用率和大量?jī)?nèi)存使用和釋放。從列表2中可以看出這個(gè)原因。同步修飾符導(dǎo)致所有進(jìn)程串行處理,就好像只用了一個(gè)CPU,而算法導(dǎo)致大量?jī)?nèi)存消耗在局部變量上。

          通過CPU是個(gè)受限的資源,如果在這個(gè)測(cè)試中我可以完全利用到兩個(gè)CPU的話就可以提高一倍的性能。垃圾回收器運(yùn)行得如此頻繁以致于不能忽略。在測(cè)試中每秒釋放的內(nèi)存達(dá)到100M,很顯然這是個(gè)限制因素。失敗數(shù)這么大明顯這個(gè)應(yīng)用是不可用的。

          監(jiān)視

          在生成合理的用戶負(fù)載后,監(jiān)視工具需要收集進(jìn)程的運(yùn)行狀況。在我的測(cè)試環(huán)境中可以收集到各種有用的信息:

          1、        所有計(jì)算機(jī)、網(wǎng)絡(luò)設(shè)備
          2、        等等的使用率
          3、        JVM的統(tǒng)計(jì)數(shù)據(jù)。
          4、        個(gè)別JAVA方法所花費(fèi)的時(shí)間。
          5、        數(shù)據(jù)庫(kù)性能信息,6、        包括SQL查詢的統(tǒng)計(jì)。
          7、        其他應(yīng)用相關(guān)的信息

          當(dāng) 然這些監(jiān)視也會(huì)影響負(fù)載測(cè)試,但如果影響比較小也可以忽略。基本上如果我們想獲取所有上面的信息,肯定會(huì)影響測(cè)試的性能。但如果不是一次獲取所有信息還是 有可能保證負(fù)載測(cè)試的有效性。僅對(duì)特定的方法設(shè)置定時(shí)器,僅獲取低負(fù)載的硬件信息和低頻率地獲取樣例數(shù)據(jù)。當(dāng)然不加載監(jiān)視器來做測(cè)試是最好的,然后和加載 監(jiān)視器的測(cè)試來做比較。雖然有時(shí)候侵入式監(jiān)視是個(gè)好主意,但就不可能有監(jiān)視結(jié)果了。


          獲取所有監(jiān)視數(shù)據(jù)到一個(gè)中央控制器來做分析是 最好的,但使用動(dòng)態(tài)運(yùn)行時(shí)工具也可以提供有用的信息。例如,命令行工具如PS、TOP、VMSTAT可以提供UNIX機(jī)器的信息;性能監(jiān)視器工具可以提供 WINDOWS機(jī)器的信息;而TeamQuest, BMC Patrol, SGI's Performance Co-Pilot, and ISM's PerfMan這樣的工具會(huì)在所有的測(cè)試環(huán)境中的機(jī)器安裝代理并且將需要的信息傳回中央控制機(jī),這樣就可以提供文本或可視化的信息。在本文中,我使用開源 的Performance Co-Pilot作為測(cè)試統(tǒng)計(jì)的工具。我發(fā)現(xiàn)他對(duì)測(cè)試環(huán)境的影響最小,并且以相對(duì)直接的方式來提供數(shù)據(jù)。

          JAVA 分析器提供很多信息,但通常對(duì)負(fù)載測(cè)試來說影響太大而沒有太多的用處。工具甚至可以讓你在負(fù)載服務(wù)器上做一些分析,但這也很容易便測(cè)試無效。在這些測(cè)試 中,我激活了詳細(xì)的垃圾收集器來收集內(nèi)存信息。我也使用jconsole 和jstack工具(包含在J2SE 1.5中)來檢查高負(fù)載下的VM。我沒有保留這些測(cè)試用例中負(fù)載測(cè)試的結(jié)果因?yàn)槲艺J(rèn)為這些數(shù)據(jù)不是很正確。


          同步瓶頸

          在 診斷服務(wù)器問題時(shí)線程的信息是非常有用的,特別是對(duì)同步之類的問題。jstack工具可以連接到運(yùn)行的進(jìn)程并且保存每一個(gè)線程的堆棧信息。在UNIX系統(tǒng) 可以用信號(hào)量3來保存線程的堆棧信息,在WINDOWS系統(tǒng)的控制臺(tái)中可以用Ctrl-Break。在第一項(xiàng)測(cè)試中,jstack指出許多線程在 grindCPU()方法中被阻塞。

          你可以已經(jīng)注意到列表2中g(shù)rindCPU()方法的同步修飾符實(shí)際上并不必須。我在后一項(xiàng)測(cè)試中刪除了他,如圖2顯示

          image
          Figure 2        

          在圖2中,你會(huì)注意到性能下降了。雖然我使用了更多的CPU,但吞吐量和失敗數(shù)都更差了。雖然垃圾回收周期變了,但每秒依然需要回收100M。顯然我們還沒有找到主要的瓶頸。
          非 竟?fàn)幍耐较鄬?duì)于簡(jiǎn)單的函數(shù)調(diào)用還是很費(fèi)時(shí)的。竟?fàn)幮缘耐骄透M(fèi)時(shí)了,因?yàn)槌藘?nèi)存需要同步外,VM還需要維護(hù)等待的線程。在這種狀況下,這些代價(jià)實(shí)際 上要小于內(nèi)存瓶頸。實(shí)際上,通過消除了同步瓶頸,VM內(nèi)存系統(tǒng)承擔(dān)了更多的壓力最后導(dǎo)致更差的吞吐量,即使我使用了更多的CPU。顯然最好的方式是從最大 的瓶頸開始,但有時(shí)這也不是很容易確定的。當(dāng)然,確保VM的內(nèi)存處理足夠正常也是一個(gè)好的開始方向。

          內(nèi)存瓶頸

          現(xiàn)在我會(huì)首先也定位內(nèi)存問題。列表3是GrinderServlet的重構(gòu)版本,使用了StringBuffer實(shí)例。圖3顯示了測(cè)試結(jié)果。

          Listing 3

          package pub.capart;

          /**
          * This is a simple class designed to simulate an application consuming
          * CPU, memory, and contending for a synchronization lock.
          */
          public class Grinderv2 {
             private static Grinderv2 singleton = new Grinderv2();
             private static final String randstr =
                "this is just a random string that I'm going to add up many many times";
             private StringBuffer sbuf = new StringBuffer();
             private StringBuffer sbufrev = new StringBuffer();

             public static Grinderv2 getGrinder() {
                return singleton;
             }
             public synchronized void grindCPU(int level) {
                sbufrev.setLength(0);
                sbufrev.append(randstr);
                sbuf.setLength(0);
                for (int i=0;i<level;++i) {
                   sbuf.append(sbufrev);
                   reverse();
                }
                return sbuf.toString();
             }

             public String getReverse(String s) {
                StringBuffer sb = new StringBuffer(s);
                sb = sb.reverse();
                return sb.toString();
             }
          }


          image
          Figure 3        

          通 常重用StringBuffer并不是一個(gè)好主意,但這里我只是為了重現(xiàn)一些常見的問題,而不量提供解決方案。內(nèi)存數(shù)據(jù)已經(jīng)從圖上消失了因?yàn)闇y(cè)試中沒有垃 圾回收器運(yùn)行。吞吐量戲劇性的增加而CPU使用率又回到了50%。列表3不只是優(yōu)化了內(nèi)存,但我認(rèn)為主要了改善了過度的內(nèi)存消耗。

          檢視同步瓶頸

          列表4另一個(gè)GrinderServlet類的重構(gòu)版本,實(shí)現(xiàn)了一個(gè)小的資源池。圖4顯示了測(cè)試結(jié)果。
          Listing 4

          package pub.capart;

          /**

          * This is just a dummy class designed to simulate a process consuming
          * CPU, memory, and contending for a synchronization lock.
          */
          public class Grinderv3 {
             private static Grinderv3 grinders[];
             private static int grinderRoundRobin = 0;
             private static final String randstr =
                "this is just a random string that I'm going to add up many many times";
             private StringBuffer sbuf = new StringBuffer();
             private StringBuffer sbufrev = new StringBuffer();

             static {
                grinders = new Grinderv3[10];
                for (int i=0;i<grinders.length;++i) {
                   grinders[i] = new Grinderv3();
                }
             }
             public synchronized static Grinderv3 getGrinder() {
                Grinderv3 g = grinders[grinderRoundRobin];
                grinderRoundRobin = (grinderRoundRobin +1) % grinders.length;
                return g;
             }
             public synchronized void grindCPU(int level) {
                sbufrev.setLength(0);
                sbufrev.append(randstr);
                sbuf.setLength(0);
                for (int i=0;i<level;++i) {
                   sbuf.append(sbufrev);
                   reverse();
                }
                return sbuf.toString();
             }
             public String getReverse(String s) {
                StringBuffer sb = new StringBuffer(s);
                sb = sb.reverse();
                return sb.toString();
             }
          }
            


          image
          Figure 4        


          吞吐量有一定的增加,而且使用更少的CPU資源。竟?fàn)幒头蔷範(fàn)幮酝蕉际琴M(fèi)時(shí)的,但通常最大的同步消耗是減少了系統(tǒng)的可伸縮性。我的負(fù)載測(cè)試不再滿足系統(tǒng)的需求了,因此我增加了虛擬的用戶數(shù),如圖5 所示。

          image
          Figure 5        


          在圖5 中吞吐量在負(fù)載達(dá)到飽和時(shí)下降了一些然后在負(fù)載減少時(shí)又提高了。此外注意到測(cè)試使得CPU使用率達(dá)到100%,這意味著測(cè)試超過了系統(tǒng)的最佳吞吐量。負(fù)載測(cè)試的一個(gè)產(chǎn)出是性能計(jì)劃,當(dāng)應(yīng)用的負(fù)載超過他的容量時(shí)會(huì)產(chǎn)生更低的吞吐量。


          水平可伸縮性

          水平伸縮允許更大的性能,但并不一定是費(fèi)用相關(guān)的。運(yùn)行在多個(gè)服務(wù)器上的應(yīng)用通常比較運(yùn)行在單個(gè)VM上的應(yīng)用復(fù)雜。但水平伸縮支持在性能上的最大增加。

          圖6是我的最后一項(xiàng)測(cè)試的結(jié)果。我已經(jīng)在三臺(tái)基本一致的機(jī)器上使用了負(fù)載平衡,只是在內(nèi)存和CPU速度上稍有不同。總的吞吐量要高于三倍的單機(jī)結(jié)果,而且CPU從來沒有完全利用。在圖6中我只顯示了一臺(tái)機(jī)器上的CPU結(jié)果,其他的是一樣的。

          image
          Figure 6        


          小結(jié)

          我曾經(jīng)花了9個(gè)月來布署一個(gè)復(fù)雜的JAVA應(yīng)用,但卻沒有時(shí)間來做性能計(jì)劃。但差勁的性能使得用戶合約幾乎中止。開發(fā)人員使用分析器花了很長(zhǎng)時(shí)間找到幾個(gè)小問題但沒有解決根本的瓶頸,而且被后續(xù)的問題完全迷惑了。最后通過負(fù)載測(cè)試找到解決方法,但你可以想到其中的處境。

          又一次我碰得更難的問題,應(yīng)用只能達(dá)到所預(yù)期性能的1/100。但通過前期檢測(cè)到的問題和認(rèn)識(shí)到負(fù)載測(cè)試的必要性,這個(gè)問題很快被解決了。負(fù)載測(cè)試相對(duì)于整個(gè)軟件開發(fā)的花費(fèi)并不多,但其所歸避的風(fēng)險(xiǎn)就高多了。

          關(guān)于作者
          Ivan Small擁有14年的軟件開發(fā)經(jīng)驗(yàn)。他在LBNL從開發(fā)Supernovae Cosmology Project開始他的職業(yè)生涯。這個(gè)項(xiàng)目是導(dǎo)致反重力和無限擴(kuò)展宇宙理論被發(fā)現(xiàn)的兩個(gè)項(xiàng)目之一。他從此工作于數(shù)據(jù)挖掘和企業(yè)級(jí)JAVA應(yīng)用。現(xiàn)在他是 nnovative Interfaces公司的首席軟件工程師。

          資源
          ·javaworld.com:javaworld.com
          ·Matrix-Java開發(fā)者社區(qū):http://www.matrix.org.cn/
          ·JAVA性能調(diào)優(yōu)第二版:http://www.amazon.com/exec/obidos/ASIN/0596003773/javaworld
          ·并發(fā)編程技術(shù):JAVA并發(fā)編程第二版:http://www.amazon.com/exec/obidos/ASIN/0201310090/javaworld
          ·JAVA網(wǎng)站分析:JAVA網(wǎng)站的性能分析:http://www.amazon.com/exec/obidos/ASIN/0201844540/javaworld
          ·JAVA性能:高性能JAVA平臺(tái)計(jì)算:http://www.amazon.com/exec/obidos/ASIN/0130161640/javaworld
          ·JAVA2性能和術(shù)語指南:http://www.amazon.com/exec/obidos/ASIN/0130142603/javaworld
          ·BEA WebLogic服務(wù)器性能調(diào)優(yōu),包含有用的一般信息:BEA WebLogic服務(wù)器上J2EE應(yīng)用性能測(cè)試:http://www.amazon.com/exec/obidos/ASIN/1904284000/javaworld
          ·JAVA性能調(diào)優(yōu):http://www.javaperformancetuning.com
          ·過度的JAVA同步:“輕量級(jí)線程”:http://www-106.ibm.com/developerworks/java/library/j-threads1.html
          ·負(fù)載和性能測(cè)試工具:http://www.softwareqatest.com/qatweb1.html#LOAD
          posted on 2005-12-02 21:59 Dion 閱讀(5792) 評(píng)論(0)  編輯  收藏 所屬分類: Web服務(wù)器

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 巴林左旗| 蒙自县| 寿宁县| 侯马市| 白玉县| 夏邑县| 甘孜县| 宣化县| 凯里市| 昌邑市| 寻甸| 双辽市| 渭源县| 绵阳市| 沈阳市| 林州市| 卓尼县| 区。| 哈巴河县| 霍山县| 彭山县| 河池市| 来安县| 溆浦县| 广饶县| 大渡口区| 嵩明县| 安仁县| 右玉县| 柳林县| 麟游县| 确山县| 河间市| 佛坪县| 巢湖市| 报价| 南平市| 吴旗县| 高平市| 宜都市| 中宁县|