Библиотека сайта 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
