Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
      Languages 
      Kernels 
      Packages 
      Books 
      Tests 
      OS 
      Forum 
      Математика 
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 Linux Kernel 2.6...3146 
 Clickhouse...369 
 Go Web ...348 
 Ethreal 4...326 
 Trees...325 
 C++ Patterns 3...309 
 Ext4 FS...293 
 William Gropp...284 
 Максвелл 3...282 
 Steve Pate 1...272 
 Secure Programming for Li...269 
 Ethreal 1...269 
 Rodriguez 6...268 
 Ethreal 3...261 
 Gary V.Vaughan-> Libtool...261 
 Стивенс 9...258 
 DevFS...251 
 Assembler...250 
 Ulrich Drepper...248 
 Стивенс 10...244 
 
  01.01.2024 : 3621733 посещений 

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