2012-03-19 123 views
3

我想在我们的一个Web服务器上执行一些性能测试,以查看服务器如何处理大量持久连接。不幸的是,我并不熟悉HTTP和Web测试。这里的Python代码我已经得到了这个至今:连接到HTTP服务器的urllib.request连接的持久性

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:块一个更好的方法?)

+1

你是什么意思的“持续连接”?服务器的正常活动将用于客户端连接,服务器发送响应并关闭连接。 – jjm 2012-03-19 15:39:44

+1

@jjm使用“Connection:Keep-Alive”标题([link](http://en.wikipedia.org/wiki/HTTP_persistent_connection))保持活动的特定连接。 – Kudzu 2012-03-19 20:05:49

回答

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'再有就是每个连接一个请求。


(此外,在一个不相关的音符,在那里等待键盘比丑陋的,而真正的中断一个更好的方法:在我的代码结束块)

要等到所有线程完成或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 

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

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

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

+0

这个答案包含很多帮助。谢谢,J.F.! – Kudzu 2012-03-20 13:13:14

+0

@Kudzu:我已经更新了提到'ThreadPoolExecutor','requests.async'这个提供更高级别接口的问题。 – jfs 2012-03-20 13:56:48

0

我要我的知识以外的位基地在这里,但我会假定当函数make_http_connection()完​​成时你的线程finnishes。也就是说,如果你想要他们所有你想包括:

while condition: 
    pass 

在功能的结尾。我想你希望他们都同时活跃起来?然后让该函数修改全局变量并使用该条件对options.num测试此值,以便进程将在它们开始终止之前等待它们全部运行。

附带疑问,猜猜你在这里瞄准什么,难道你不能只是要求线程来计算你有多少活线程,并继续运行,直到没有剩下的线程?

threading.active_count() 

这在这里讨论读取键盘,如果这是你所需要的:

Polling the keyboard

2

如果很多真的是很多那么你可能希望使用异步io不线程。

requests + gevent = grequests

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连接。

+1

注意:“很多”可能会导致[打开的文件过多](https://github.com/kennethreitz/requests/issues/239) – jfs 2012-06-17 15:19:36

0

你真的应该使用像Funkload这样的基准工具来做到这一点。如果您没有使用HTTP的经验,尝试从头开始进行性能测试就会导致不好的结果。