• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • Apache性能分析

    發表于:2007-05-25來源:作者:點擊數: 標簽:
    有關Apache的 性能分析 分析對象 適用于 UNIX 系統的Apache1.3 分析內容 Apache服務器的運行流程()以及有可能進行的優化 分析的目標 記錄Apache運行過程中各個部分消耗的時間記錄,提出優化的方案 開始部分: 由于沒有什么可以具體借鑒的文檔,我們從分析A

    有關Apache的性能分析

    分析對象
    適用于UNIX系統的Apache1.3
    分析內容
    Apache服務器的運行流程()以及有可能進行的優化
    分析的目標
    記錄Apache運行過程中各個部分消耗的時間記錄,提出優化的方案
    開始部分:
    由于沒有什么可以具體借鑒的文檔,我們從分析Apache的運行流程入手,一下內容是我從網上查到的有關Apache運行記錄(被請求的對象是一個6k左右的靜態網頁):

    accept(15, {sin_family=AF_INET, sin_port=htons(22283), sin_addr=inet_addr("127.0.0.1")}, [16]) = 3
    flock(18, LOCK_UN) = 0
    sigaction(SIGUSR1, {SIG_IGN}, {0x8059954, [], SA_INTERRUPT}) = 0
    getsockname(3, {sin_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0
    setsockopt(3, IPPROTO_TCP1, [1], 4) = 0
    read(3, "GET /6k HTTP/1.0\r\nUser-Agent: "..., 4096) = 60
    sigaction(SIGUSR1, {SIG_IGN}, {SIG_IGN}) = 0
    time(NULL) = 873959960
    gettimeofday({873959960, 404935}, NULL) = 0
    stat("/home/dgaudet/ap/apachen/htdocs/6k", {st_mode=S_IFREG|0644, st_size=6144, ...}) = 0
    open("/home/dgaudet/ap/apachen/htdocs/6k", O_RDONLY) = 4
    mmap(0, 6144, PROT_READ, MAP_PRIVATE, 4, 0) = 0x400ee000
    writev(3, [{"HTTP/1.1 200 OK\r\nDate: Thu, 11"..., 245}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 6144}], 2) = 6389
    close(4) = 0
    time(NULL) = 873959960
    write(17, "127.0.0.1 - - [10/Sep/1997:23:39"..., 71) = 71
    gettimeofday({873959960, 417742}, NULL) = 0
    times({tms_utime=5, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 446747
    shutdown(3, 1 /* send */) = 0
    oldselect(4, [3], NULL, [3], {2, 0}) = 1 (in [3], left {2, 0})
    read(3, "", 2048) = 0
    close(3) = 0
    sigaction(SIGUSR1, {0x8059954, [], SA_INTERRUPT}, {SIG_IGN}) = 0
    munmap(0x400ee000, 6144) = 0
    flock(18, LOCK_EX) = 0
    分類:
    我們可以大致的對以上內容進行分類
    1:socket請求接收部分,很清楚,應該是
    accept(15, {sin_family=AF_INET, sin_port=htons(22283), sin_addr=inet_addr("127.0.0.1")}, [16]) = 3
    getsockname(3, {sin_family=AF_INET, sin_port=htons(8080), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0
    setsockopt(3, IPPROTO_TCP1, [1], 4) = 0
    read(3, "GET /6k HTTP/1.0\r\nUser-Agent: "..., 4096) = 60

    2:中斷信號的處理部分:
    sigaction(SIGUSR1, {SIG_IGN}, {0x8059954, [], SA_INTERRUPT}) = 0
    sigaction(SIGUSR1, {SIG_IGN}, {SIG_IGN}) = 0
    sigaction(SIGUSR1, {0x8059954, [], SA_INTERRUPT}, {SIG_IGN}) = 0
    3:讀取請求的靜態網頁并返回:
    stat("/home/dgaudet/ap/apachen/htdocs/6k", {st_mode=S_IFREG|0644, st_size=6144, ...}) = 0
    open("/home/dgaudet/ap/apachen/htdocs/6k", O_RDONLY) = 4
    mmap(0, 6144, PROT_READ, MAP_PRIVATE, 4, 0) = 0x400ee000
    writev(3, [{"HTTP/1.1 200 OK\r\nDate: Thu, 11"..., 245}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 6144}], 2) = 6389
    close(4) = 0
    4:日志記錄:
    time(NULL) = 873959960
    write(17, "127.0.0.1 - - [10/Sep/1997:23:39"..., 71) = 71
    gettimeofday({873959960, 417742}, NULL) = 0
    times({tms_utime=5, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 446747
    5:關閉連接:
    shutdown(3, 1 /* send */) = 0
    oldselect(4, [3], NULL, [3], {2, 0}) = 1 (in [3], left {2, 0})
    read(3, "", 2048) = 0
    close(3) = 0
    6:刪除mmap文件鏈接
    munmap(0x400ee000, 6144) = 0
    分析:
    子進程調度
    Unix上的Apache是應用了預分支模型的服務器。父進程的責任僅在于繁衍子進程,它從不響應來自socket的任何請求。真正處理連接的是子進程,每個子進程在終止之前會(逐一地)為多個連接服務。父進程根據服務器負載的變化(通過監視記分板,記分板由子進程負責保持同步)生成新的或者殺掉舊的子進程。
    在以上的分類過程中,有兩句話    
    flock(18, LOCK_UN) = 0
       flock(18, LOCK_EX) = 0
    沒有明確的含義,我們從這里開始。我在Apache代碼http_main.c中找到了這個調用,它很明顯是一段互斥作用的實現,它根據操作系統的不同,最終這組互斥調用都被封裝到一組叫做
    accept_mutex_off
    accept_mutex_on
    的函數中。
    到這里就很清楚了,這部分是Apache針對不同的操作系統對于由哪個子進程接收請求的調度模塊,在這里Apache采取了如下的實現模式,在任意時間只能有一個空閑的子進程擁有接收請求的權限,這種權限的分配方式由系統提供的互斥量方法解決,這是一種串行化接收請求的解決方案,簡化后的代碼如下
    for (;;)
    {
        for (;;)
        {
            fd_set accept_fds;
            FD_ZERO (&accept_fds);
            for (i = first_socket; i <= last_socket; ++i)
            {
                FD_SET (i, &accept_fds);
            }
            rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL);
            if (rc < 1) continue;
            new_connection = -1;
            for (i = first_socket; i <= last_socket; ++i)
            {
                if (FD_ISSET (i, &accept_fds))
                {
                    new_connection = accept (i, NULL, NULL);
                    if (new_connection != -1)
                        break;
                }
            }
            if (new_connection != -1)
                break;
        }
        process the new_connection;
    }
    Apache提供了如下的宏定義以改變互斥實現的方式ap_config.h
    HAVE_USLOCK_SERIALIZED_ACCEPT
    HAVE_PTHREAD_SERIALIZED_ACCEPT
    HAVE_SYSVSEM_SERIALIZED_ACCEPT
    HAVE_FCNTL_SERIALIZED_ACCEPT
    HAVE_FLOCK_SERIALIZED_ACCEPT
    HAVE_OS2SEM_SERIALIZED_ACCEPT
    HAVE_TPF_CORE_SERIALIZED_ACCEPT
    HAVE_BEOS_SERIALIZED_ACCEPT
    HAVE_NONE_SERIALIZED_ACCEPT
    我們可以在編譯之前增加編譯選現來實現不同的互斥調用以獲得更高的性能,由于時間限制,我只找到了少部分這方面編譯的資料
    USE_SYSVSEM_SERIALIZED_ACCEPT (1.3版及以后)
      此方法借助SysV的信號量(semaphores)實現互斥。但不巧的是SysV信號量有一些負面作用。一是Apache可能在清除信號量之前非正常終止;二是在使用信號量API時需要考慮到任何與服務器UID相同的CGI程序可以進行拒絕服務攻擊(就是說所有的CGI程序都可以這樣做,除非使用suexec或cgiwrapper之類的方法)。所以,這種方法并不被IRIX之外的系統廣泛采納(由于大多數IRIX系統上,使用前兩種方法的代價太大)。 
    USE_USLOCK_SERIALIZED_ACCEPT 
     ?。?.3版及以后)此方法僅在IRIX上可用。它調用usconfig(2)創建互斥量。雖然這種方法避免了對SysV信號量的種種爭議,但它不是IRIX的缺省方案。這是由于在單處理器的IRIX系統 (5.3或6.2)上,uslock代碼比SysV信號量慢兩個數量級;但在多處理器的IRIX中前者比后者快一個數量級。這無非使問題復雜化了。所以在多處理器IRIX系統上,我們需要用如下的附加參數編譯Apache:
        在EXTRA_CFLAGS中添加-DUSE_USLOCK_SERIALIZED_ACCEPT 
        USE_PTHREAD_SERIALIZED_ACCEPT 
     ?。?.3版及以后)此方法實現了POSIX標準互斥量。它理應可以工作在任何實現了全部POSIX線程規范的系統上,但事實是只有在Solaris 2.5或以上的系統及特定的配置中才能工作。如果嘗試這種方法的話,需要小心服務器掛起或者沒有響應。服務器在只輸出靜態網頁的情況下運行得很好。
    以上言及的方案對多socket服務器是相當不錯的,但只有一個socket的情況又如何呢?理論上,由于在連接請求到來之前所有子進程將阻塞在accept中,單個socket不會產生上述種種問題。但實際上,上述非阻塞解決方案所帶來的"回旋(spinning)"問題在這里只不過被掩蓋起來了。在絕大多數TCP協議棧的實現中,一個接請求到來時內核將喚醒所有阻塞在accept中的進程。它們之一將得到此請求并返回用戶空間,其余的進程將返回內核重新休眠。這將帶來與多socket非阻塞解決方案相同的資源浪費。

      由于這點原因,我們發現如果為socket串行化,許多系統表現得更"友好"--即使是一個socket的情況。這是單個socket串行化作為絕大多數情況的缺省配置的原因。在Linux上不甚精確的(Linux 2.0.30 / 雙Pentium Pro 166 w / 128Mb內存)實驗表明,對每次請求而言,串行化的單個socket僅比沒有串行化的socket損失不到3%的性能。但未串行化的socket顯示出每次連接請求100毫秒的延時。這也可能僅僅由于過長的通訊距離造成的。如果您不想串行化單個socket,可以定義宏SINGLE_LISTEN_UNSERIALIZED_ACCEPT。這樣,僅有一個socket的服務器將不會串行化。
    共享內存的使用
    Apache利用一種叫做記分板(scoreboard)的技術在父、子進程間通訊。它的理想實現是在共享內存中。有的操作系統允許我們直接訪問共享內存,或者提供它們的確切端口。在這些系統中的典型實現就是共享內存記分板。其他的系統則將磁盤上的文件作為缺省實現。磁盤文件不僅低效而且不穩定(又沒有什么優勢)。請為您的操作系統仔細閱讀src/main/conf.h文件,并在其中尋找USE_MMAP_SCOREBOARD或者USE_SHMGET_SCOREBOARD。定義它們之一(以及相應的HAVE_MMAP和HAVE_SHMGET)將允許Apache使用共享內存。
    讀取靜態網頁
    我們發現在在靜態網頁的讀取過程中,Apache采用了mmap方式來映射文件信息,我們知道在對于小文件的讀取這種方式并沒有多少好處,Apache通過一個叫做MMAP_THRESHOLD的宏定義來確定是否采用mmap的文件映射方式,對于大于MMAP_THRESHOLD的文件Apache才采用mmap方式,在http_core.c中這個我們可以找到這個宏定義,它的默認值是8096,資料顯示這一數值是對于SunOS4,我們可以針對不同的操作系統修改這一數值來提高性能,這依賴于大量的測試
    有關性能的配置文件httpd.conf
    在過程中出現了多次的時間函數函數gettimeofday,times的調用,這些調用是由于在編譯Apache的過程中包含了mod_status并且將ExtendedStatus設置為On造成的,這些調用都是為了在報告中含有時間戳,我們可以將ExtendedStatus設置為OFF來去掉這些調用。
    除此之外,HostnameLookups設定也是導致性能下降的罪魁禍首。DNS解析消耗了大量的時間,除此之外,如果使用了任何allow from domain或deny from domain命令,所付出的代價將是兩次DNS查詢帶來的延時(在一次逆向查詢后跟著一次正向查詢,以保證前者得到的結果是真實的)。因此為了得到最理想的性能應避免使用HostnameLookups
    其它有關性能編譯選項
    如果我們不打算支持動態加載模塊的話,編譯服務器時請設定參數-DDYNAMIC_MODULE_LIMIT=0。這將節省出為動態加載模塊而分配的內存。
    性能監控模塊
    mod_status和mod_info to模塊
    在Apache服務器中的 mod_status 和 mod_info to 來告訴我們目前服務器的工作情況 
    mod_status:
    使用 mod_status,允許我們使用URL:http://servername/server-status來通過mod_status生成并報告服務器狀態信息。我們可以知道誰在你的服務器上看些什么東西,以及有多少人連在Web 服務器上,每個子進程的運行狀態等等信息

    mod_info 和 mod_status 
    這兩個模塊可以提供十分有用的信息,而且十分方便。 
    mod_status 能準確地告訴你,你的服務器正在“想”什么。你可以知道有哪些人在瀏覽您的網站,有多少子進 程在運行,以及這些進程在干嗎。系統自從上次啟動以來已經運行了多少時間。
    當然我們還可以得到更多的信息,修改http.conf
    ExtendedStatus  on
    我們還可以得到一張每一 個子進程及其所作工作的列表。 對于每一個子進程而言,信息包括它的PID ,以及它占用的CPU 時間和已經運行的時間。對于服務器而言,信息包括得到服務器啟動以后的合計點擊數,CPU的利用率以及每分鐘點擊數,還有傳輸給客戶端的總計字節數。 

    啟用方式,修改httpd.conf文件,增加以下信息
    SetHandler server-status 
    Order deny,allow 
    Deny from all 
    Allow from .your_domain.com 
    mod_info
    啟用mod_info,允許使用URL:http://servername/server-info來遠程報告服務器配置信息,mpd-info 是一個分類的擴展模塊,需要需要mod_info.c支持。

    確認安裝之后,修改httpd.conf,增加以下信息
    SetHandler server-info 
    Order deny,allow 
    Deny from all 
    Allow from .your-domain.com 
    URL:http://servername/server-info顯示的啟示就是你編譯到Apache 里面的東西的列表以及其他針對服務器的各種特性。如果你輸入:http://your.server/server-info/ 就可以看到服務器內置的模塊列表或者通過DSO 加載的模塊列表。 
    這對于安裝和配置特定的服務器來說是十分有用的。特別是用來對錯誤的配置文件查找問題時。 
    結論以及目標
    1:Apache運行過程時間的記錄
    通過修改http:.conf配置文件,
    LogFormat "%h %l %u %t \"%r\" %>s %b" common(默認)
    增加%D(毫秒級,僅對于Apache2適用),%T(秒級)參數,增加對每個請求的時間過程記錄
    LogFormat "%h %l %u %t \"%r\" %>s %b %D" common(默認)
    2:加載mod_status和mod_info to模塊
    雖然加載mod_status和mod_info to模塊會帶來一定的性能損失,但是一個完善的監控機制還是有必要的,方便我們對Apache的參數調整后的檢測,這要比查看單條的請求記錄來得更直觀和有效
    3:進一步的希望
    我們覺得以上的信息仍然不夠,需要更加量化的信息,比如我們需要每個請求的數據接收,處理,日志的時間記錄,我們可能需要去修改Apache的源代碼,以達到以上的目標

     小蔡的救贖 回復于:2004-11-16 00:19:46
    你好,這篇文章,我在很多地方看到引用,
    但是對其中單socket串行accept一直不太理解,
    我明白服務器可以由多個服務端口,
    由此可能會有多個偵聽套接字,
    但好像套接字單個或是多個,
    與accept串行沒有關系,
    對多socket串行,我是這樣理解的:
    多個子進程互斥查詢是否有新的連接請求并accept,
    由此形成了accept的串行,
    而單socket也是同樣形成串行accept,
    所以accept的串行好像只是和多進程有關啊 :em14: 
    對不起,問的問題比較菜  :em06:

     tma 回復于:2004-11-16 12:45:05
    不錯

     小蔡的救贖 回復于:2004-11-17 21:45:31
    沒有人解答嗎? 
    謝謝 :cry:

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>