Витая асинхронная запись Python с использованием отложенного - PullRequest
2 голосов
/ 15 июля 2010

Что касается инфраструктуры Python Twisted, может кто-нибудь объяснить мне, как асинхронно писать очень большую строку данных для потребителя, скажем, объект protocol.transport?

Я думаю, что мне не хватаетwrite(data_chunk) функция, которая возвращает Deferred.Вот что я хотел бы сделать:

data_block = get_lots_and_lots_data()
CHUNK_SIZE = 1024 # write 1-K at a time.
def write_chunk(data, i):
  d = transport.deferredWrite(data[i:i+CHUNK_SIZE])
  d.addCallback(write_chunk, data, i+1)
write_chunk(data, 0)

Но, после дня, проведенного в блуждании по Twisted API / Documentation, я не могу найти что-то подобное эквивалентности deferredWrite.Чего мне не хватает?

Ответы [ 2 ]

8 голосов
/ 16 июля 2010

Как говорит Жан-Поль, вам следует использовать IProducer и IConsumer , но вы также должны отметить, что отсутствие deferredWrite является несколько преднамеренным упущением.

Во-первых, создание Deferred для потенциально каждого байта данных, которые записываются, является проблемой производительности: мы попробовали это в проекте web2 и обнаружили, что это была самая значительная проблема производительности во всей системе, и мы пытаемся избежать этой ошибки, поскольку мы возвращаем web2 код twisted.web.

Однако, что более важно, наличие Deferred, которое возвращается, когда write "завершается", может создать обманчивое впечатление: на другом конце провода получено данных, которые вы ' мы отправили. Там нет разумного способа разглядеть это. Прокси-серверы, интеллектуальные маршрутизаторы, ошибки приложений и всевозможные ухищрения в сети могут заставить вас думать, что ваши данные действительно поступили на другой конец соединения, даже если они никогда не обрабатываются. Если вам необходимо знать, что другой конец обработал ваши данные, убедитесь, что в протоколе вашего приложения есть подтверждающее сообщение, которое передается только после того, как данные получены и обработаны.

Основная причина использования производителей и потребителей в этом коде - это в первую очередь избегать выделения памяти. Если ваш код действительно считывает все данные, которые он собирается записать, в первую очередь в гигантскую строку в памяти (data_block = get_lots_and_lots_data() это прямо подразумевает это), то вы не потеряете много, если выполните transport.write(data_block). Транспорт будет просыпаться и отправлять порцию данных так часто, как это возможно. Кроме того, вы можете просто набрать transport.write(hugeString), а затем transport.loseConnection(), и транспорт на самом деле не отключится, пока все данные не будут отправлены или соединение не прервано иным образом. (Опять же: если вы не дождетесь подтверждения, вы не будете знать, попали ли данные туда. Но если вы просто хотите сбросить несколько байтов в сокет и забыть об этом, это работает нормально.)

Если get_lots_and_lots_data() действительно читает файл , вы можете использовать включенный класс FileSender . Если это что-то вроде файла, но не совсем, может быть полезной реализация FileSender .

1 голос
/ 15 июля 2010

В Twisted обычно обрабатываются большие объемы данных с использованием API производителя / потребителя . Это не дает вам метод write, который возвращает Deferred, но он дает вам уведомление о том, когда пришло время писать больше данных.

...