urllib.request连接到HTTP服务器的持久性

Kud*_*dzu 4 python http python-3.x

我想在我们的一个Web服务器上进行一些性能测试,以查看服务器如何处理大量持久连接.不幸的是,我对HTTP和Web测试并不十分熟悉.这是我迄今为止获得的Python代码:

import http.client
import argparse
import threading


def make_http_connection():
    conn = http.client.HTTPConnection(options.server, timeout=30)
    conn.connect()


if __name__ == '__main__':
    parser = argparse.ArgumentParser()

    parser.add_argument("num", type=int, help="Number of connections to make (integer)")
    parser.add_argument("server", type=str, help="Server and port to connect to. Do not prepend \'http://\' for this")

    options = parser.parse_args()

    for n in range(options.num):
        connThread = threading.Thread(target = make_http_connection, args = ())
        connThread.daemon = True
        connThread.start()

    while True:
        try:
            pass
        except KeyboardInterrupt:
            break
Run Code Online (Sandbox Code Playgroud)

我的主要问题是:如何保持这些连接存活?我设置了一个很长的超时,但这是一个非常粗糙的方法,我甚至不确定它会影响连接.只需要每隔一段时间请一个字节或两个字节吗?

(另外,在一个不相关的说明中,有没有比while True:我代码末尾的丑陋块更好的等待键盘中断的程序?)

jfs*_*jfs 8

urllib.request不支持持久连接.'Connection: close'代码中有硬编码.但http.client部分支持持久连接(包括传统的http/1.0 keep-alive).所以问题标题可能会产生误导.


我想在我们的一个Web服务器上进行一些性能测试,以查看服务器如何处理大量持久连接.不幸的是,我对HTTP和Web测试并不十分熟悉.

您可以使用现有的http测试工具,例如slowloris,httperf,而不是自己编写.


如何保持这些连接存活?

要关闭http/1.1连接,客户端应显式指定Connection: close头,否则服务器认为连接是持久的(尽管它可能随时关闭它,并且在尝试读取/写入连接之前http.client不会知道它).

conn.connect()几乎立即返回,你的线程结束.要强制每个线程维护到服务器的http连接,您可以:

import time

def make_http_connection(*args, **kwargs):
    while True: # make new http connections
        h = http.client.HTTPConnection(*args, **kwargs)
        while True: # make multiple requests using a single connection
            try:
                h.request('GET', '/') # send request; make conn. on the first run
                response = h.getresponse()
                while True: # read response slooowly
                    b = response.read(1) # read 1 byte
                    if not b:
                       break
                    time.sleep(60) # wait a minute before reading next byte
                    #note: the whole minute might pass before we notice that 
                    #  the server has closed the connection already
            except Exception:
                break # make new connection on any error
Run Code Online (Sandbox Code Playgroud)

注意:如果服务器返回,'Connection: close'则每个连接只有一个请求.


(另外,在一个不相关的说明中,有一个更好的程序等待键盘中断而不是丑陋而True:在我的代码末尾阻止?)

要等到所有线程完成或KeyboardInterrupt发生,您可以:

while threads:
    try:
        for t in threads[:]: # enumerate threads
            t.join(.1) # timeout 0.1 seconds
            if not t.is_alive():
               threads.remove(t)
    except KeyboardInterrupt:
        break
Run Code Online (Sandbox Code Playgroud)

或类似的东西:

while threading.active_count() > 1:
    try:
        main_thread = threading.current_thread()
        for t in threading.enumerate(): # enumerate all alive threads
            if t is not main_thread:
               t.join(.1)
    except KeyboardInterrupt:
        break
Run Code Online (Sandbox Code Playgroud)

后者可能由于各种原因而无法工作,例如,如果存在虚拟线程,例如在不使用threading模块的情况下在C扩展中启动的线程.

concurrent.futures.ThreadPoolExecutor提供了比threading模块更高的抽象级别,它可以隐藏一些复杂性.

您可以在单个线程中同时打开多个连接,而不是使用每个连接模型,例如,使用requests.asyncgevent直接打开.