代码之家  ›  专栏  ›  技术社区  ›  Andrew Dalke

粘贴.httpserver并使用HTTP/1.1保持活动状态;用httperf和ab测试

  •  3
  • Andrew Dalke  · 技术社区  · 15 年前

    我有一个基于paste.httpserver的web服务器作为HTTP和WSGI之间的适配器。当我使用httperf进行性能测量时,如果每次使用--num-conn启动一个新请求,我每秒可以执行1000多个请求。如果我改为使用--num调用重用连接,那么我每秒可以获得11个请求,速度的1/100。

    如果我尝试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-with-keep-alive请求,并使用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秒,因此性能非常好。

    % 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
    

    检测服务器时,它处理一个请求并等待第二个请求。

    知道发生了什么吗?

    1 回复  |  直到 15 年前
        1
  •  6
  •   Andrew Dalke    15 年前

    经过一番努力,似乎是 Nagle's algorithm 或者延迟的应答,或者它们之间的交互。如果我做了类似的事情,它就会消失

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

    一位朋友提出了明显的问题,我搜索了“200ms套接字延迟”,找到了关于这个问题的描述。

    粘贴trac报告位于 http://trac.pythonpaste.org/pythonpaste/ticket/392 以及在处理程序使用HTTP/1.1时启用TCP_节点延迟的修补程序。