Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Асинхронное программирование на Twisted

Этот документ - для новичков в твистед , но уже знакомых с питоном , а также концептуально - с серверами , клиентами , сокетами. Будет дан обзор параллельному программированию - concurrent programming : non-blocking код или asynchronous код.

Введение в concurrent programming

Многие задачи требуют много времени на вычисления :

  1. Большая сложность вычислений - напр. факториалы для больших чисел
  2. Ожидание данных для получения результата.

Ожидание ответа

Одной из главных особенностей сетевого программирования является ожидание данных. Пусть у нас есть функция , которая отсылает e-mail. Этой функции нужно приконнектиться к удаленному серверу, дождаться реплики от него, проверить его , послать емайл, дождаться подтверждения, и отсоединиться.

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

В сетевом программировании возможно выполнять сразу несколько задач независимо от того , пришло ли время для них.

Не-ожидание данных

Один из алгоритмов:

  1. для каждого коннекта выделяется отдельный процесс.
  2. для каждого коннекта выделяется отдельный поток
  3. использовать не-блокирующий системный вызов для каждого коннекта

Не-блокирующий вызов

В Twisted используется третья модель: non-blocking calls.

Когда есть много коннектов , и с ними работает приложение , а не операционная система, то обычно используется функция, которая проверяет каждый коннект, готов ли он на чтение или на запись - в этом случае говорят об асинхронном - asynchronous, event-driven или callback-based - программировании.

В этом случае , наш пример с отсылкой емайла будет работать так:

  1. вызывается функция коннекта к удаленному серверу
  2. эта функция вернется сразу, а сообщение о том , что емайл отослан, будет послано после коннекта
  3. когда произойдет коннект , будет послано сообщение

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

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()
 

Итоги

  1. для нетривиальных сетевых программ необходим параллелизм;
  2. Twisted framework поддерживает параллелизм в форме асинхронных вызовов;
  3. Twisted framework имеет обьект Deferred, управляющий callback-ми
  4. на примере функции getPage мы разобрали, как работает Deferred ;
  5. к Deferred можно приаттачить как callbacks, так и errbacks
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье
igor
  super statia po twisted!!!
2011-06-25 15:46:31