Я ошеломлен: странная проблема с питоном и сокетами + потоками - PullRequest
2 голосов
/ 20 октября 2008

У меня есть скрипт Python, который является http-сервером: http://paste2.org/p/89701, при сравнении его с ApacheBench (ab) с уровнем параллелизма (ключ -c), который меньше или равен значению, указанному в socket.listen () - вызов в исходном коде, все работает нормально, но как только уровень параллелизма в apache bench окажется выше значения в socket.listen () - производительность вызова падает до минимума, например:

Ничего не меняется в коде между двумя вызовами, я не могу понять, в чем дело - я уже один день занимаюсь этой проблемой. Также обратите внимание на то, что: мультиплексная версия того же кода (которую я написал для сравнения с многопоточной версией) работает FINE независимо от того, какое свойство socket.listen () установлено или какое значение имеет параллелизм (ключ -c) в apache.

Я провел день на IRC / python docs, опубликованном на comp.lang.python и в моем блоге - я не могу найти НИКАКОГО, кто даже имеет представление о том, что может быть не так. Помоги мне!

Ответы [ 5 ]

7 голосов
/ 20 октября 2008

Я не могу подтвердить ваши результаты, и ваш сервер закодирован подозрительно. Я взбил свой собственный сервер и у меня тоже нет этой проблемы. Давайте переместим обсуждение на более простой уровень:

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]
4 голосов
/ 21 октября 2008

Для этого я также реализовал асинхронную версию:

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]
0 голосов
/ 21 октября 2008

Хорошо, поэтому я запустил код на совершенно другом сервере - (vps, который я получил на slicehost), ни одной проблемы (все работает как положено), так что, честно говоря, я думаю, что сейчас что-то не так с моим ноутбуком; *

Спасибо за помощь всем!

0 голосов
/ 20 октября 2008

похоже, что вы не получаете параллелизма. очевидно, когда вы выполняете socket.accept (), основной поток не сразу возвращается к ожиданию следующего соединения. может быть, ваш поток обработки соединений представляет собой только код Python, поэтому вы получаете секвенирование с помощью SIL (блокировки одного интерпретатора).

если между потоками нет большой нагрузки, лучше использовать многопроцессную схему (с пулом предварительно порожденных процессов, конечно)

0 голосов
/ 20 октября 2008

Я нашел эту статью в бэклоге на tomcat / java, которая дает интересную информацию о бэклоге:

например, если все потоки заняты в обработке запросов java, ядро будет обрабатывать рукопожатия SYN и TCP пока его отставание не заполнится. когда отставание заполнено, оно просто упадет будущие запросы SYN. это не отправит RST, то есть вызывающее «Отказ в соединении» на клиенте, вместо этого клиент будет предположим, что пакет был потерян и повторно передать SYN. надеюсь, Очередь невыполненных работ будет очищена то.

Как я понимаю, попросив ab создать более одновременное соединение, чем ваше сокет настроен на обработку пакетов, сбрасывается, не отказывается, и я не знаю как А.Б. справляется с этим. Может случиться так, что он передает SYN, но, возможно, после ожидания какое-то время. Это может даже где-то прослеживаться (протокол TCP?).

Как уже было сказано, я не знаю, но надеюсь, что это намекает на причину.

Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...