2

我有一个作为 http-server 的 python 脚本:http: //paste2.org/p/89701,当它针对 ApacheBench (ab) 进行基准测试时,并发级别(-c 开关)低于或等于该值我在源代码中的 socket.listen()-call 中指定了一切正常,但是一旦将 apache bench 中的并发级别置于 socket.listen()-call 中的值之上,性能就会下降,例如:

两次调用之间的代码没有任何变化,我不知道出了什么问题——这个问题已经有一天了。另请注意:无论 socket.listen() 设置为什么或 apache 中的并发(-c 开关)设置为什么,相同代码的多路复用版本(我编写以与线程版本进行比较)都可以正常工作。

我在 IRC/python 文档上花了一天时间,在 comp.lang.python 和我的博客上发布 - 我找不到任何人甚至知道可能出了什么问题。帮我!

4

5 回答 5

7

我无法确认您的结果,并且您的服务器编码有问题。我掀起了我自己的服务器,也没有这个问题。让我们把讨论移到一个更简单的层次:

import thread, socket, Queue

connections = Queue.Queue()
num_threads = 10
backlog = 10

def request():
    while 1:
        conn = connections.get()
        data = ''
        while '\r\n\r\n' not in data:
            data += conn.recv(4048)
        conn.sendall('HTTP/1.1 200 OK\r\n\r\nHello World')
        conn.close()

if __name__ == '__main__':
    for _ in range(num_threads):
        thread.start_new_thread(request, ())

    acceptor = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    acceptor.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    acceptor.bind(('', 1234))
    acceptor.listen(backlog)
    while 1:
        conn, addr = acceptor.accept()
        connections.put(conn)

在我的机器上是这样的:

ab -n 10000 -c 10 http://127.0.0.1:1234/ --> 8695.03 [#/sec]
ab -n 10000 -c 11 http://127.0.0.1:1234/ --> 8529.41 [#/sec]
于 2008-10-20T19:56:00.093 回答
4

我还实现了一个异步版本:

import socket, Queue, select

class Request(object):
    def __init__(self, conn):
        self.conn = conn
        self.fileno = conn.fileno
        self.perform = self._perform().next

    def _perform(self):
        data = self.conn.recv(4048)
        while '\r\n\r\n' not in data:
            msg = self.conn.recv(4048)
            if msg:
                data += msg
                yield
            else:
                break
        reading.remove(self)
        writing.append(self)

        data = 'HTTP/1.1 200 OK\r\n\r\nHello World'
        while data:
            sent = self.conn.send(data)
            data = data[sent:]
            yield
        writing.remove(self)
        self.conn.close()

class Acceptor:
    def __init__(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('', 1234))
        sock.listen(10)
        self.sock = sock
        self.fileno = sock.fileno

    def perform(self):
        conn, addr = self.sock.accept()
        reading.append(Request(conn))

if __name__ == '__main__':
    reading = [Acceptor()]
    writing = list()

    while 1:
        readable, writable, error = select.select(reading, writing, [])
        for action in readable + writable:
            try: action.perform()
            except StopIteration: pass

执行:

ab -n 10000 -c 10 http://127.0.0.1:1234/ --> 16822.13 [#/sec]
ab -n 10000 -c 11 http://127.0.0.1:1234/ --> 15704.41 [#/sec]
于 2008-10-20T20:37:12.647 回答
0

我在 tomcat / java 上的 backlog 上找到了这篇文章,它对 backlog 提供了有趣的见解:

例如,如果所有线程都忙于 java 处理请求,内核将处理 SYN 和 TCP 握手,直到它的 backlog 已满。当积压已满时,它将简单地丢弃未来的 SYN 请求。它不会发送 RST,即在客户端上导致“连接被拒绝”,相反,客户端将假定包丢失并重新传输 SYN。希望届时积压的队列将被清除。

正如我所解释的那样,通过要求 ab 创建比您的套接字配置为处理丢弃的数据包更多的同时连接,而不是拒绝,我不知道 ab 是如何处理的。可能是它重新传输 SYN,但可能在等待一段时间后。这甚至可以在某处指定(TCP 协议?)。

如前所述,我不知道,但我希望这暗示了原因。

祝你好运!

于 2008-10-20T19:48:00.390 回答
0

看起来您并没有真正获得并发性。显然,当您执行 socket.accept() 时,主线程不会立即返回等待下一个连接。也许你的连接处理线程只是 python 代码,所以你被 SIL(单解释器锁)顺序化了。

如果线程之间没有繁重的通信,最好使用多进程方案(当然,使用预先生成的进程池)

于 2008-10-20T19:57:04.367 回答
0

好的,所以我在完全不同的服务器上运行代码 - (我在 slicehost 获得的 vps),没有一个问题(一切都按预期工作)所以老实说,我认为我的笔记本电脑现在有问题;p

感谢大家的帮助!

于 2008-10-21T17:21:36.723 回答