Какая асинхронная библиотека Python лучше всего подходит для моего кода? Asyncore? Витая? - PullRequest
18 голосов
/ 08 декабря 2010

У меня есть программа, над которой я работаю, которая будет читать из двух «сетевых источников» одновременно. Я хотел попробовать асинхронный подход, а не использовать многопоточность. Это заставило меня задуматься, какую библиотеку использовать ...

Я придумал простой пример кода, который как бы демонстрирует, что будет делать моя программа:

import sniffer

def first():
    for station in sniffer.sniff_wifi():
        log(station.mac())

def second():
    for station in sniffer.sniff_ethernet():
        log(station.mac())

first()
second()

Два sniffer метода выглядят примерно так:

def sniff_wifi(self):

    while True:
        yield mac_address

Цикл while True явно блокирует их.

Я хочу использовать asyncore для этого, так как он является частью стандартной библиотеки. Никакие сторонние зависимости не являются бонусом. Однако это не значит, что я не буду использовать его, если вы порекомендуете мне сделать ...

Могу ли я добиться того, что я пытаюсь сделать с асинкором? Если да, не могли бы вы показать мне, как преобразовать мой пример кода в «асинхронный код»? Знаете ли вы какие-нибудь хорошие асинхронные учебники?

Ответы [ 3 ]

50 голосов
/ 08 декабря 2010

Витая лучше во всех отношениях.Это более портативный, более функциональный, простой, более масштабируемый, лучше обслуживаемый, лучше документированный, и он может сделать вкусный омлет.Asyncore, для всех намерений и целей, устарел.

Трудно продемонстрировать все способы, которыми Twisted превосходит в коротком ответе (как я мог продемонстрировать http / * 1006 DNS * / SSH / SMTP / POP / IMAP / ИРЦ / XMPP / процесс-нерестовый / мульти-в качестве примера рассмотрим сервер в кратком примере?), поэтому вместо этого я остановлюсь на одном из самых распространенных заблуждений, которые, как кажется, имеют люди в отношении Twisted: его использование сложнее или сложнее, чем asyncore.

Давайте начнем с асинхронного примера.Чтобы избежать предвзятого представления, я буду использовать пример от кого-то еще, кто все еще любит асинкоры.Вот простой асинхронный пример , взятый из блога Ричарда Джонса (с краткими комментариями, опущенными).

Во-первых, вот сервер:

import asyncore, socket

class Server(asyncore.dispatcher):
    def __init__(self, host, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.bind(('', port))
        self.listen(1)

    def handle_accept(self):
        socket, address = self.accept()
        print 'Connection by', address
        EchoHandler(socket)

class EchoHandler(asyncore.dispatcher_with_send):
    def handle_read(self):
        self.out_buffer = self.recv(1024)
        if not self.out_buffer:
            self.close()

s = Server('', 5007)
asyncore.loop()

и вот клиент:

import asyncore, socket

class Client(asyncore.dispatcher_with_send):
    def __init__(self, host, port, message):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((host, port))
        self.out_buffer = message

    def handle_close(self):
        self.close()

    def handle_read(self):
        print 'Received', self.recv(1024)
        self.close()

c = Client('', 5007, 'Hello, world')
asyncore.loop()

Есть несколько неясных случаев, когда этот код не обрабатывает правильно, но объяснение их скучно и сложно, и код уже сделал этот ответ достаточно длинным.

Теперь, вот код, который делает то же самое с Twisted.Во-первых, сервер:

from twisted.internet import reactor, protocol as p

class Echo(p.Protocol):
    def dataReceived(self, data):
        self.transport.write(data)

class EchoFactory(p.Factory):
    def buildProtocol(self, addr):
        print 'Connection by', addr
        return Echo()

reactor.listenTCP(5007, EchoFactory())
reactor.run()

А теперь, клиент:

from twisted.internet import reactor, protocol as p

class EchoClient(p.Protocol):
    def connectionMade(self):
        self.transport.write(self.factory.data)

    def dataReceived(self, data):
        print 'Received:', data
        self.transport.loseConnection()

class EchoClientFactory(p.ClientFactory):
    protocol = EchoClient
    def __init__(self, data):
        self.data = data

reactor.connectTCP('localhost', 5007, EchoClientFactory('Hello, world'))
reactor.run()

Есть пара вещей, на которые я хотел бы обратить ваше внимание.Прежде всего, пример Twisted на 25% короче, даже для чего-то такого тривиального.40 строк для Asyncore, только 30 для Twisted.По мере усложнения вашего протокола это различие будет становиться все больше и больше, так как вам нужно будет писать все больше и больше кода поддержки асинхронности, который был бы предоставлен вам Twisted.

Во-вторых, Twisted предоставляет полная абстракция .В примере asyncore вы должны использовать модуль socket для создания реальной сети;Asyncore обеспечивает только мультиплексирование.Это проблема, если вам нужно портативное поведение на платформах, таких как Windows .Это также означает, что у asyncore полностью отсутствуют средства для асинхронного взаимодействия подпроцесса на других платформах;Вы не можете вставить произвольные файловые дескрипторы в вызов select() в Windows.

В-третьих, пример Twisted является транспортно-нейтральным .Ни один из Echo и EchoFactory и EchoClient и EchoClientFactory не имеет никакого отношения к TCP.Вы можете превратить эти классы в библиотеку, которая может быть подключена через SSH, SSL или сокет UNIX, или канал, только изменив один вызов connectTCP / listenTCP внизу.Это важно, так как поддерживать что-то вроде TLS непосредственно в логике вашего протокола очень сложно.Например, «запись» в TLS вызовет «чтение» на нижнем уровне.Таким образом, вам нужно разделить эти проблемы, чтобы разобраться в них.

Наконец, в зависимости от вашего варианта использования, если вы имеете дело с MAC-адресами и сетевыми фреймами напрямую, Twisted содержит Twisted Pair , низкоуровневая библиотека для работы с IP и сетями уровня Ethernet.Это не самая активно поддерживаемая часть Twisted;код довольно старый.Но это должно сработать, и если этого не произойдет, мы серьезно отнесемся к ошибкам и (в конце концов) увидим, что они исправлены.Насколько я знаю, нет никакой сопоставимой библиотеки для asyncore, и она, конечно же, сама не содержит такого кода.

3 голосов
/ 08 декабря 2010

Asyncore хорош, но не очень многофункциональн, поэтому вы можете столкнуться с проблемами позже, когда ваше приложение будет расти.Это, как говорится, здорово прототипировать вещи.Подход довольно прост.Вы определяете методы для обработки определенных событий в классе (когда чтение возможно, когда запись возможна и т. Д.), А затем создаете подкласс этого класса из класса asyncore.dispatcher (я думаю).

Официальные документы для модуля , а также превосходная статья Дага Хеллмана PyMOTW об этом являются хорошими источниками, чтобы проверить документы и примеры.

Если ваш протокол является диалоговым (например, отправьте это, получите это), вы можете проверить модуль asynchat, также распространяемый со стандартной библиотекой идей.

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

0 голосов
/ 10 апреля 2012

Curl был разработан, чтобы быть неблокирующим во всех ракурсах и избегать использования выбора, который является дорогостоящей операцией во время асинхронного ввода-вывода. На низком уровне, curl использует наиболее оптимальные возможные решения, поэтому на сегодняшний день не существует инфраструктуры, которая могла бы работать лучше, чем curl, хотя могли бы существовать платформы, которые могли бы обеспечивать аналогичную производительность.

Как говорится, как насчет написания ваших собственных сокетов? Это очень просто в Python и может дать вам удивительную производительность, когда вы знаете, что делаете, и четко понимаете свои цели.

...