Наши партнеры

UnixForum





Библиотека сайта 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, включают следующие:
  • Ограничения сети и файловой системы.
  • Особенности буферизации.
  • Метод установления факта разрыва соединения.
  • Данные, возвращаемые в случаях ошибок.

Реализация циклов обработки событий в рамках Twisted также заботится о корректном использовании низкоуровневых не использующих блокировок API, а также о корректной работе в случае нестандартных критических ситуаций. В рамках языка Python API IOCP не раскрывается вообще, поэтому Twisted использует свою собственную реализацию.

Управление цепочками функций обратного вызова

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

Давайте рассмотрим некоторые ловушки, появляющиеся в управляемых событиями программах, в ходе сравнения синхронной и асинхронной версии игрушечной утилиты для получения страницы на основе строки URL, разработанной с использованием похожего на Python псевдокода:

Синхронная версия программы для получения страницы на основе строки URL:
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()
Асинхронная версия программы для получения страницы на основе строки URL:
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