Этот документ - для новичков в твистед , но уже знакомых с питоном , а также концептуально - с серверами , клиентами , сокетами. Будет дан обзор параллельному программированию - concurrent programming : non-blocking код или asynchronous код.
Введение в concurrent programming
Многие задачи требуют много времени на вычисления :
- Большая сложность вычислений - напр. факториалы для больших чисел
- Ожидание данных для получения результата.
Ожидание ответа
Одной из главных особенностей сетевого программирования является ожидание данных. Пусть у нас есть функция , которая отсылает e-mail. Этой функции нужно приконнектиться к удаленному серверу, дождаться реплики от него, проверить его , послать емайл, дождаться подтверждения, и отсоединиться.
Каждый из этих шагов занимает время. На программу сразу накладываются ограничения: она не может послать много писем одновременно, она вообще не может делать ничего , пока письмо отсылается.
В сетевом программировании возможно выполнять сразу несколько задач независимо от того , пришло ли время для них.
Не-ожидание данных
Один из алгоритмов:
- для каждого коннекта выделяется отдельный процесс.
- для каждого коннекта выделяется отдельный поток
- использовать не-блокирующий системный вызов для каждого коннекта
Не-блокирующий вызов
В Twisted используется третья модель: non-blocking calls.
Когда есть много коннектов , и с ними работает приложение , а не операционная система, то обычно используется функция, которая проверяет каждый коннект, готов ли он на чтение или на запись - в этом случае говорят об асинхронном - asynchronous, event-driven или callback-based - программировании.
В этом случае , наш пример с отсылкой емайла будет работать так:
- вызывается функция коннекта к удаленному серверу
- эта функция вернется сразу, а сообщение о том , что емайл отослан, будет послано после коннекта
- когда произойдет коннект , будет послано сообщение
Преимущество этой схемы в том, что пока емайл будет отправляться, остальная часть программы будет продолжать делать свою работу, т.е. открывать коннекты для других емайлов. Т.е. программа не будет висеть в ожидании коннекта.
Callbacks
В сетевом приложении есть специальный тип сообщения о том, что данные готовы - это т.н. callback. Приложение вызывает обычную функцию, а у этой функции одним из аргументов будет другая - коллбэк-функция - которая будет вызвана в нужный момент. callback function - отложенная функция , возвращающая данные.
В обычном - синхронном программировании - функция запрашивает данные, ждет их, и идет дальше. В асинхронном программировании, функция запрашивает данные, и если их нет, позволяет библиотеке сделать вызов callback function , когда эти данные прийдут.
Deferreds
Twisted использует т.н. Deferred
обьект
для управления последовательности callback.
Клиентское приложение привязывает серию функций к deferred,
которые будут вызваны в порядке получения асинхронных запросов -
эти функции называются callbacks, или callback chain,
а также другую группу функций, вызываемых в случае ошибок - errbacks или
errback chain.
Сначала вызывается первый callback, когда данные приходят,
затем обьект Deferred
начинает рулить очередью этих калл-бэков.
Проблемы, решаемые Deferreds
Вторая проблема, решаемая при параллельном программировании - при вычислении задач может возникнуть задержка,связанная с их интенсивностью. Занятый диск, перегруженная база данных, сетевой трафик относится также к этому классу.
Deferreds работает так, что позволяет Twisted-программам работать, а не висеть,
независимо от того, пришли данные или нет.
Это реализовано с помощью интерфейса callbacks.
Библиотеки всегда в нужный момент сделают вызов либо
Deferred.callback
либо Deferred.errback
.
Приложения будут обрабатывать callbacks и errbacks в порядке их вызова.
Базовая идея Deferreds в том, чтобы CPU был максимально освобожден от рутины ожидания.
В Twisted, сигнал для вызова ожидающей callback-функции возвращается Deferred. После этого вызывается нужный callbacks .
Deferreds - сигнал о том , что данные еще не готовы
В нашем примере с емайл, родительская функция вызывает функцию коннекта к удаленному серверу. Эта функция коннекта должна вернуться немедленно. Как родительская функция узнает, что коннекта до сих пор нет?
В Twisted есть обьект, который просигнализирует об этом.
Когда коннект-функция вернет управление, этот обьект просигнализирует, что коннект не сделан.
Называется он
twisted.internet.defer.Deferred
object.
Deferred имеет 2 цели. Первая говорит о том, что этот обьект - сигнал о том, что результат до сих пор не достигнут. Вторая - обьект можно запросить, когда данные будут получены.
Callbacks
Запросить у Deferred сами данные - сделать в Deferred вызов функции по приходу данных.
Одна из функций , возвращаемая Deferred - twisted.web.client.getPage
.
В этом примере мы делаем вызов
getPage
, когда возвращается Deferred, и привязываем callback,
чтобы прочитать контент страницы по приходу данных:
from twisted.web.client import getPage from twisted.internet import reactor def printContents(contents): ''' This is the 'callback' function, added to the Deferred and called by it when the promised data is available ''' print "The Deferred has called printContents with the following contents:" print contents # Stop the Twisted event handling system -- this is usually handled # in higher level ways reactor.stop() # call getPage, which returns immediately with a Deferred, promising to # pass the page contents onto our callbacks when the contents are available deferred = getPage('http://iakovlev.org/') # add a callback to the deferred -- request that it run printContents when # the page content has been downloaded deferred.addCallback(printContents) # Begin the Twisted event handling system to manage the process -- again this # isn't the usual way to do this reactor.run()
Как применить Deferreds сразу для 2-х callbacks,при этом результат первого передать во второй: :
from twisted.web.client import getPage from twisted.internet import reactor def lowerCaseContents(contents): ''' This is a 'callback' function, added to the Deferred and called by it when the promised data is available. It converts all the data to lower case ''' return contents.lower() def printContents(contents): ''' This a 'callback' function, added to the Deferred after lowerCaseContents and called by it with the results of lowerCaseContents ''' print contents reactor.stop() deferred = getPage('http://iakovlev.org/') # add two callbacks to the deferred -- request that it run lowerCaseContents # when the page content has been downloaded, and then run printContents with # the result of lowerCaseContents deferred.addCallback(lowerCaseContents) deferred.addCallback(printContents) reactor.run()
Error handling: errbacks
Callback может вернуть как данные , так и ошибку: дисконнект, ошибочные данные, ошибку протокола и т.д. Можно добавить в Deferred error handlers ('errbacks'), который будет срабатывать, когда произойдет ошибка :
from twisted.web.client import getPage from twisted.internet import reactor def errorHandler(error): ''' This is an 'errback' function, added to the Deferred which will call it in the event of an error ''' # this isn't a very effective handling of the error, we just print it out: print "An error has occurred: <%s>" % str(error) # and then we stop the entire process: reactor.stop() def printContents(contents): ''' This a 'callback' function, added to the Deferred and called by it with the page content ''' print contents reactor.stop() # We request a page which doesn't exist in order to demonstrate the # error chain deferred = getPage('http://twistedmatrix.com/does-not-exist') # add the callback to the Deferred to handle the page content deferred.addCallback(printContents) # add the errback to the Deferred to handle any errors deferred.addErrback(errorHandler) reactor.run()
Итоги
- для нетривиальных сетевых программ необходим параллелизм;
- Twisted framework поддерживает параллелизм в форме асинхронных вызовов;
- Twisted framework имеет обьект Deferred, управляющий callback-ми
- на примере функции
getPage
мы разобрали, как работает Deferred ; - к Deferred можно приаттачить как callbacks, так и errbacks
Оставьте свой комментарий ! | ||||
---|---|---|---|---|
|