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

Python 到HTTP服务器的urllib.request连接的持久性,python,http,python-3.x,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()

我想在我们的一台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
我的主要问题是:如何保持这些连接?我设置了一个长超时,但这是一个非常粗糙的方法,我甚至不确定它是否会影响连接。只是偶尔请求一两个字节就行了吗


(另外,另一个不相关的注意事项是,是否有比丑陋的
等待键盘中断更好的过程,而True:
在我的代码末尾阻塞?

我在这里有点超出了我的知识基础,但我假设当函数make_http_connection()完成时,您的线程将终止。)。也就是说,如果您想要它们,那么您需要包括:

while condition:
    pass
在函数的末尾。我想你想让他们同时活跃起来?然后让函数修改一个全局变量,并使用条件根据options.num测试该值,以便进程在开始终止之前将等待所有进程运行

附带的问题是,猜猜你们的目标是什么,难道你们不能让线程来计算你们有多少个活动线程,并一直运行直到并没有剩余的线程吗

threading.active_count()
这里讨论的是阅读键盘,如果这是您需要的:


urllib.request
不支持持久连接。代码中有
“连接:关闭”
硬编码。但是
http.client
部分支持持久连接(包括旧式http/1.0
保持活动状态
)。因此,问题的标题可能会产生误导


我想在我们的一台web服务器上做一些性能测试,看看服务器如何处理大量持久连接。不幸的是,我不太熟悉HTTP和web测试

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


我如何保持这些连接

要关闭http/1.1连接,客户端应明确指定
connection:close
头,否则服务器会认为该连接是持久的(尽管它可能会在任何时候关闭该连接,直到它尝试读取/写入该连接为止)

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
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
注意:如果服务器返回
“连接:关闭”
,则每个连接只有一个请求


(还有一点与此无关,等待键盘中断的过程是否比丑陋的while True:代码末尾的block更好?)

要等待所有线程完成或键盘中断发生,您可以:

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
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
或者像这样:

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
由于各种原因,后者可能不起作用,例如,如果存在伪线程,例如在没有使用
线程
模块的情况下在C扩展中启动的线程

提供比
线程化
模块更高的抽象级别,并且可以隐藏一些复杂性

您可以在单个线程中同时打开多个连接,例如,直接使用
requests.async
gevent

如果很多是真正的很多,那么您可能希望使用异步io而不是线程

+=

GRequests允许您将请求与Gevent一起使用,以轻松地生成异步HTTP请求

import grequests

urls = [
    'http://www.heroku.com',
    'http://tablib.org',
    'http://httpbin.org',
    'http://python-requests.org',
    'http://kennethreitz.com'
]

requests = (grequests.get(u) for u in urls)
responses = grequests.map(requests)

请求持久的HTTP连接。

您真的应该使用类似于此的基准测试工具。如果您没有使用HTTP的经验,尝试从头开始进行性能测试肯定会导致糟糕的结果。

您所说的“持久连接”是什么意思?您的服务器的正常活动是客户端连接,服务器发送响应并关闭连接。@jjm特别指定使用“connection:Keep alive”头()保持活动的连接。此答案中包含了大量帮助。谢谢,J.F@Kudzu:我已经更新了这个问题,提到了
ThreadPoolExecutor
请求。异步
提供了更高级别的接口。注意:“很多”可能会导致