2009-11-23 61 views
3

我有一個基於paste.httpserver的web服務器作爲HTTP和WSGI之間的一個分離器。當我使用httperf進行性能測量時,如果每次使用--num-conn啓動一個新請求,我可以每秒處理超過1,000個請求。如果我使用--num-call來重新使用連接,那麼我每秒獲得約11個請求,即速度的1/100。paste.httpserver和HTTP/1.1保持存活下來;使用httperf和ab進行測試

如果我嘗試ab,我會得到一個超時。

我的測試是

% ./httperf --server localhost --port 8080 --num-conn 100 
... 
Request rate: 1320.4 req/s (0.8 ms/req) 
... 

% ./httperf --server localhost --port 8080 --num-call 100 
... 
Request rate: 11.2 req/s (89.4 ms/req) 
... 

這裏有一個簡單的重複性服務器

from paste import httpserver 

def echo_app(environ, start_response): 
    n = 10000 
    start_response("200 Ok", [("Content-Type", "text/plain"), 
           ("Content-Length", str(n))]) 
    return ["*" * n] 

httpserver.serve(echo_app, protocol_version="HTTP/1.1") 

這是一個多線程的服務器,這是很難的個人資料。這裏有一個變化,這是單線程:

from paste import httpserver 

class MyHandler(httpserver.WSGIHandler): 
    sys_version = None 
    server_version = "MyServer/0.0" 
    protocol_version = "HTTP/1.1" 

    def log_request(self, *args, **kwargs): 
     pass 


def echo_app(environ, start_response): 
    n = 10000 
    start_response("200 Ok", [("Content-Type", "text/plain"), 
           ("Content-Length", str(n))]) 
    return ["*" * n] 

# WSGIServerBase is single-threaded 
server = httpserver.WSGIServerBase(echo_app, ("localhost", 8080), MyHandler) 
server.handle_request() 

剖析與

% python2.6 -m cProfile -o paste.prof paste_slowdown.py 

%httperf --client=0/1 --server=localhost --port=8080 --uri=/ \ 
    --send-buffer=4096 --recv-buffer=16384 --num-conns=1 --num-calls=500 

擊中它,我得到這樣

>>> p=pstats.Stats("paste.prof") 
>>> p.strip_dirs().sort_stats("cumulative").print_stats() 
Sun Nov 22 21:31:57 2009 paste.prof 

     109749 function calls in 46.570 CPU seconds 

    Ordered by: cumulative time 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.000 0.000 46.571 46.571 {execfile} 
     1 0.001 0.001 46.570 46.570 paste_slowdown.py:2(<module>) 
     1 0.000 0.000 46.115 46.115 SocketServer.py:250(handle_request) 
     1 0.000 0.000 44.675 44.675 SocketServer.py:268(_handle_request_noblock) 
     1 0.000 0.000 44.675 44.675 SocketServer.py:301(process_request) 
     1 0.000 0.000 44.675 44.675 SocketServer.py:318(finish_request) 
     1 0.000 0.000 44.675 44.675 SocketServer.py:609(__init__) 
     1 0.000 0.000 44.675 44.675 httpserver.py:456(handle) 
     1 0.001 0.001 44.675 44.675 BaseHTTPServer.py:325(handle) 
     501 0.006 0.000 44.674 0.089 httpserver.py:440(handle_one_request) 
    2001 0.020 0.000 44.383 0.022 socket.py:373(readline) 
     501 44.354 0.089 44.354 0.089 {method 'recv' of '_socket.socket' objects} 
     1 1.440 1.440 1.440 1.440 {select.select} 
     .... 

您可以將配置文件幾乎所有的時間都是我一個recv。

我決定保釋httpref和我自己寫的HTTP/1.1與 - 保活請求和使用的netcat發送:

GET/HTTP/1.1 
Location: localhost 
Connection: Keep-Alive 
Content-Length: 0 

GET/HTTP/1.1 
Location: localhost 
Connection: Keep-Alive 
Content-Length: 0 

... repeat 97 more times, to have 99 keep-alives in total ... 

GET/HTTP/1.1 
Location: localhost 
Connection: Close 
Content-Length: 0 

我與

nc localhost 8080 < ~/src/send_to_paste.txt 

總時間發送對於100個請求是0.03秒,所以它的性能非常好。

這表明的httperf做得不對(但它是一種廣泛使用的和受人尊敬的一段代碼),所以我想「AB」

% ab -n 100 -k localhost:8080/ 
This is ApacheBench, Version 1.3d <$Revision: 1.73 $> apache-1.3 
Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 
Copyright (c) 2006 The Apache Software Foundation, http://www.apache.org/ 

Benchmarking localhost (be patient)... 
Server timed out 

: Operation now in progress 

插裝服務器,它處理一個請求,正在等待第二。

想知道發生了什麼?

回答

6

經過一番努力,這似乎是要麼Nagle's algorithm或延遲ACK,或它們之間的相互作用。它會消失,如果我做類似的東西

server.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 

我是如何追蹤它?首先,我安裝了socket.py中的每個'recv',以便我能夠確定哪個recv正在等待。我會看到11箇中有5個recv的延遲了將近200ms。我無法弄清楚爲什麼有任何延誤。然後,我使用Wireshark觀看消息,並注意到它實際上是從服務器發送到客戶端的延遲。這意味着來自我的客戶端的傳出消息在TCP層中。

一位朋友提出了這個問題,我搜索了「200ms套接字延遲」,發現了這個問題的描述。

粘貼trac報告位於http://trac.pythonpaste.org/pythonpaste/ticket/392以及修補程序,該修補程序在處理程序使用HTTP/1.1時啓用TCP_NODELAY。

+0

我自己學會了這個難題:https://github.com/williame/hellepoll/blob/556a302e690ded6627bd1d87b67aceb6f0592b27/http.cpp#L182 – Will 2011-10-28 09:32:11