我想在我们的一个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
我的主要问题是:我如何保持这些连接存活?我设置了一个很长的超时,但这是一个非常粗略的方法,我甚至不确定它会影响连接。只是偶尔请求一两个字节吗?
(另外,在一个不相关的说明中,等待键盘中断的程序比代码末尾的丑陋while True:
块更好吗?)
答案 0 :(得分: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
won't know about it直到它尝试读取/写入连接)。
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
注意:如果服务器返回'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
或类似的东西:
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
后者可能由于各种原因而无法工作,例如,如果存在虚拟线程,例如在不使用threading
模块的情况下以C扩展名启动的线程。
concurrent.futures.ThreadPoolExecutor提供了比threading
模块更高的抽象级别,它可以隐藏一些复杂性。
您可以在单个线程中同时打开多个连接,而不是直接使用requests.async
或gevent
,而不是每个连接模型。
答案 1 :(得分:2)
如果很多真的很多那么你可能想要使用异步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)
请求support持久HTTP连接。
答案 2 :(得分:0)
我在这里的知识基础之外,但我会假设你的线程在函数make_http_connection()完成时完成。也就是说,如果你想要它们,你想要包括:
while condition:
pass
在功能结束时。我想你们希望他们同时变得活跃起来?然后让函数修改一个全局变量,并使用条件对options.num测试该值,以便进程在它们开始终止之前等待所有进程运行。
提出问题,猜测你的目标是什么,你不能只是要求线程计算你有多少活线程并继续运行直到没有剩下?
threading.active_count()
这里讨论了阅读键盘,如果你需要的话:
答案 3 :(得分:0)
你真的应该使用像Funkload这样的基准工具来做到这一点。如果您没有使用HTTP的经验,那么尝试从头开始进行性能测试肯定会导致不良结果。