2

http サーバーである python スクリプトがあります: http://paste2.org/p/89701、値以下の同時実行レベル (-c スイッチ) で ApacheBench (ab) に対してベンチマークするときソースコードのsocket.listen()-callで指定されたものはすべて正常に動作しますが、Apacheベンチの同時実行レベルをsocket.listen()-callの値よりも高くするとすぐにパフォーマンスが低下します。いくつかの例:

2 つの呼び出しの間でコードに変更はありません。何が問題なのかわかりません。この問題に 1 日悩まされています。また、次の点にも注意してください。同じコードの多重化バージョン (スレッド化されたバージョンと比較するために書いたもの) は、socket.listen() がどのように設定されているか、または apache の同時実行 (-c スイッチ) がどのように設定されているかに関係なく、正常に動作します。

私は、comp.lang.python と私のブログに投稿された IRC/python ドキュメントに 1 日を費やしました。助けて!

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

わかりましたので、まったく別のサーバーでコードを実行しました-(slicehostで取得したvps)、単一の問題ではありません(すべてが期待どおりに機能します)ので、正直なところ、ラップトップに何か問題があると思います;p

でもみんな助けてくれてありがとう!

于 2008-10-21T17:21:36.723 に答える
0

実際には同時実行性が得られていないようです。どうやら、socket.accept() を実行すると、メイン スレッドはすぐに次の接続の待機に戻りません。おそらく、接続処理スレッドは Python コードのみであるため、SIL (単一インタープリダー ロック) によって順次化されています。

スレッド間の通信量が多くない場合は、マルチプロセス スキームを使用することをお勧めします (もちろん、事前に生成されたプロセスのプールを使用します)。

于 2008-10-20T19:57:04.367 に答える
0

バックログで興味深い洞察を提供する tomcat / Java のバックログに関するこの記事を見つけました。

たとえば、Java 処理要求ですべてのスレッドがビジー状態の場合、カーネルはバックログがいっぱいになるまで SYN および TCP ハンドシェイクを処理します。バックログがいっぱいになると、将来の SYN リクエストが単純にドロップされます。RST を送信しません。つまり、クライアントで「接続が拒否されました」が発生します。代わりに、クライアントはパッケージが失われたと見なし、SYN を再送信します。うまくいけば、それまでにバックログ キューが解消されているはずです。

私が解釈したように、ソケットがパケットを処理するように構成されているよりも多くの同時接続を作成するようにabに依頼することにより、拒否されるのではなくドロップされます。abがそれをどのように処理するかわかりません。SYN を再送信している可能性がありますが、しばらく待った後である可能性があります。これはどこかで指定されているかもしれません (TCP プロトコル?)。

前述のとおり、私にはわかりませんが、これが原因のヒントになることを願っています。

幸運を!

于 2008-10-20T19:48:00.390 に答える