Библиотека сайта rus-linux.net
Twisted
Глава 21 из книги "Архитектура приложений с открытым исходным кодом", том 2.Оригинал: Twisted
Автор: Jessica McKellar
Перевод: А. Панин
Шаблон проектирования reactor
Twisted реализует шаблон проектирования reactor, который описывает возможность демультиплексирования и распределения событий от множества источников между их обработчиками в однопоточном окружении.
Основной частью фреймворка Twisted является цикл обработки событий, спроектированный согласно шаблону reactor. Этот цикл располагает информацией о событиях сети, файловой системы и таймеров. Он ожидает и при наступлении обрабатывает эти события, абстрагируясь от специфичного для платформы поведения и представляя интерфейсы для осуществления ответа на события в любой точке сетевого стека без сложностей.
while True: timeout = time_until_next_timed_event() events = wait_for_events(timeout) events += timed_events_until(now()) for event in events: event.process()
Цикл обработки событий основывается на API poll
(описанном в спецификации Single UNIX Specification, Version 3 (SUSv3)), используемом в качестве стандарта для всех платформ. Дополнительно Twisted поддерживает несколько платформо-специфичных гибко масштабируемых API для мультиплексирования. Платформо-специфичные циклы обработки событий могут основываться на KQueue, методе мультиплексирования, на основе механизма kqueue
из состава FreeBSD, epoll
в системах с поддержкой интерфейса epoll
(на данный момент это Linux 2.6) и IOCP на основе технологии Windows Input/Output Completion Ports.
- Ограничения сети и файловой системы.
- Особенности буферизации.
- Метод установления факта разрыва соединения.
- Данные, возвращаемые в случаях ошибок.
Реализация циклов обработки событий в рамках Twisted также заботится о корректном использовании низкоуровневых не использующих блокировок API, а также о корректной работе в случае нестандартных критических ситуаций. В рамках языка Python API IOCP не раскрывается вообще, поэтому Twisted использует свою собственную реализацию.
Управление цепочками функций обратного вызова
Функции обратного вызова являются фундаментальной частью процесса разработки управляемых событиями систем и способом указания со стороны цикла обработки событий на факт наступления событий. По мере роста управляемых событиями программ, обработка и удачных и неудачных вариантов событий в рамках приложения значительно усложняется. Невозможность регистрации подходящей функции обратного вызова может привести к блокировке программы на операции обработки события, которая никогда не будет выполнена , при этом ошибки могут распространяться по цепочке функций обратного вызова от сетевого стека через уровни приложения.
Давайте рассмотрим некоторые ловушки, появляющиеся в управляемых событиями программах, в ходе сравнения синхронной и асинхронной версии игрушечной утилиты для получения страницы на основе строки URL, разработанной с использованием похожего на Python псевдокода:
import getPage def processPage(page): print page def logError(error): print error def finishProcessing(value): print "Завершение работы..." exit(0) url = "http://google.com" try: page = getPage(url) processPage(page) except Error, e: logError(error) finally: finishProcessing()
from twisted.internet import reactor import getPage def processPage(page): print page finishProcessing() def logError(error): print error finishProcessing() def finishProcessing(value): print "Завершение работы..." reactor.stop() url = "http://google.com" # Функция getPage принимает следующие аргументы: строку url, # функцию обратного вызова для использования в случае удачного получения страницы, # функцию обратного вызова для обработки ошибки getPage(url, processPage, logError) reactor.run()
В случае асинхронной программы для получения страницы на основе URL вызов reactor.run()
запускает цикл обработки сообщений. И в синхронной, и в асинхронной версиях гипотетическая функция getPage
выполняет работу по получению страницы. Функция processPage
вызывается в случае успешного получения страницы, а функция logError
вызывается в случае возникновения исключения (Exception
) при попытке получения страницы. В любом случае после этого вызывается функция finishProcessing
.
Вызов функции logError
из асинхронной версии заменяется на часть except
блока try/except
в синхронной версии. Вызов функции processPage
заменяет оператор else
и безусловный вызов функции finishProcessing
заменяет оператор finally
.
В синхронной версии с помощью структуры блока try/except
, вызывается только одна из функций logError
и processPage
и функция finishProcessing
всегда вызывается единожды; в асинхронной версии разработчик ответственен за использование корректных цепочек функций обратного вызова для обработки удачного или неудачного завершения процесса. Если бы в случае ошибки программирования вызов функции finishProcessing
не использовал функции processPage
и logError
вместе с соответствующими цепочками функций обратных вызовов, цикл обработки событий не прервал бы никогда свою работу и программа выполнялась бы вечно.
Этот простейший пример указывает на сложности, которые расстраивали разработчиков Twisted в течение первых лет работы над проектом. Ответом на эти сложности послужило увеличение размеров объекта с названием Deferred
.
Продолжение статьи: Объекты Deferred