• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Разработка сетевых приложений с gevent
 

Разработка сетевых приложений с gevent

on

  • 10,159 views

Это слайды моего доклада на DevConf 2010.

Это слайды моего доклада на DevConf 2010.

Statistics

Views

Total Views
10,159
Views on SlideShare
9,747
Embed Views
412

Actions

Likes
13
Downloads
98
Comments
2

11 Embeds 412

http://devel.ownport.net 222
http://jetfix.ru 84
http://www.slideshare.net 43
http://localhost 26
http://www.jetfix.ru 21
http://feeds.feedburner.com 5
http://d.ownport.net 4
https://twitter.com 3
http://109.174.125.69 2
http://2025136836330523605_30aabb266766bacbdafd91140b9f91453a6af966.blogspot.com 1
http://devel-ownport.blogspot.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

12 of 2 previous next

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • Информативные слайды, спасибо.
    Are you sure you want to
    Your message goes here
    Processing…
  • Андрей, спасибо большое за материал. В голове все разложилось по полочкам! :)
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Разработка сетевых приложений с gevent Разработка сетевых приложений с gevent Presentation Transcript

    • Разработка сетевых приложений с gevent Андрей Попп 8mayday@gmail.com http://braintrace.ru @andreypopp
    • Сетевые сервисы должны уметь одновременно обрабатывать несколько клиентских запросов. Андрей Попп: Разработка сетевых приложений с gevent
    • Современные сетевые сервисы должны уметь одновременно обрабатывать огромное количество клиентских запросов. Андрей Попп: Разработка сетевых приложений с gevent
    • Стратегии организации I/O Основные стратегии обработки соединений относительно организации I/O: Блокирующий I/O – необходимо несколько потоков ОС. Неблокирующий I/O + мултиплексор – достаточно даже одного потока ОС. Андрей Попп: Разработка сетевых приложений с gevent
    • Блокирующий I/O Необходимо использовать отдельный поток на каждое активное соединение. Много активных соединений = много активных потоков = большое количество потребляемой памяти. Переключение контекста исполнения обходится дорого. В Python есть GIL. Андрей Попп: Разработка сетевых приложений с gevent
    • Блокирующий I/O Объективно не подходит для обслуживания большого количество одновременных соединений. Андрей Попп: Разработка сетевых приложений с gevent
    • Неблокирующий I/O Операции на сокетах не блокируют поток – они производяться только тогда, когда доступны. Для обслуживания нескольких активных соединений достаточно даже одного потока. Меньшее количество потребляемой памяти. Обычно приходится выстраивать код приложения ввиде обработчиков событий на сокетах. Андрей Попп: Разработка сетевых приложений с gevent
    • Неблокирующий I/O Использвование неблокирующего I/O кажется более подходящим решением проблемы. Андрей Попп: Разработка сетевых приложений с gevent
    • Неблокирующий I/O Но какие распространённые библиотеки/фрэймворки мы имеем для Python: asyncore, Twisted, Tornado. Андрей Попп: Разработка сетевых приложений с gevent
    • С Twisted приходится писать асинхронный код – это неудобно! def handle_client ( req ): deferred = m a k e _ a p i _ r e q u e s t () deferred . addCallback ( handle_api_resp , req ) deferred . addErrback ( handle_error ) return deferred def h and le _a p i_ re s p ( api_resp , req ): deferred = m ak e _d b_ r eq ue s t () deferred . addCallback ( handle_db_resp , api_resp , req ) return deferred def handle _db_re sp ( db_resp , api_response , req ): # work with api_resp and db_resp request . write (" success ") def handle_error ( failure , req ): # handle error request . write (" error ") return failure Андрей Попп: Разработка сетевых приложений с gevent
    • Синхронный код писать проще и получается он понятнее. def handle_client ( request ): try : api_response = m a k e _ a p i _ r e q u e s t () db_response = ma k e_ d b_ re q ue st () # work with api_response and db_response request . write (" success ") except Exception : request . write (" error ") raise Но приходится использовать блокирующий I/O. Андрей Попп: Разработка сетевых приложений с gevent
    • Что делать? Нужно искать компромис! Андрей Попп: Разработка сетевых приложений с gevent
    • Микропотоки Микропотоки или “зелёные” потоки или userspace-потоки: Это как функции, исполнение которых можно приостановить, а потом – продолжить. Работают внутри одного или нескольких потоков ОС. Для их исполнения необходим планировщик. Обычно дёшевы в плане потребления памяти и переключения контекста. Андрей Попп: Разработка сетевых приложений с gevent
    • Микропотоки + Неблокирующий I/O Чтобы микропотоки исполнялись им необходим планировщик. Предлагается следующий вариант: Как только микропоток пытается выполнить I/O, он передаёт управление планировщику. После того, как выполнение I/O становится доступным для микропотока – планировщик возвращает ему управление. Андрей Попп: Разработка сетевых приложений с gevent
    • Блокируется только микропоток, который пытается выполнить I/O, а не весь интерпретатор. Андрей Попп: Разработка сетевых приложений с gevent
    • Это называется кооперативная многозадачность – потоки сами решают когда передать исполнение другим. Андрей Попп: Разработка сетевых приложений с gevent
    • Существует также преемптивная или вытесняющая многозадачность – поток вытесняется планировщиком после определённого количества выполненных инструкций или по истичении определённого времени. Андрей Попп: Разработка сетевых приложений с gevent
    • Но разве микропотоки есть в Python? Андрей Попп: Разработка сетевых приложений с gevent
    • Микропотоки в Python – Генераторы Можно реализовать микропотоки в Python c помощью генераторов (PEP 342, начиная с версии Python 2.5). Чтобы передать исполнение – делаем yield. К сожалению: Кооперация с помощью yield – это слишком явно и неудобно, приходиться самим думать, когда отдавать управление. Генераторы не сохраняют весь стэк во время остановки – yield должен быть всегда на самом верху. Андрей Попп: Разработка сетевых приложений с gevent
    • Микропотоки в Python – greenlet Микропотоки с библиотекой greenlet: Микропоток или просто гринлет это объект greenlet. Кооперация посредством вызова метода greenlet.switch. greenlet – это “выжимка” из Stackless Python. Андрей Попп: Разработка сетевых приложений с gevent
    • Как работают гринлеты from greenlet import greenlet >>> def test1 (): ... print ’one ’ ... gr2 . switch () ... print ’two ’ ... >>> def test2 (): ... print ’ three ’ ... gr1 . switch () ... print ’ four ’ ... >>> gr1 = greenlet ( test1 ) >>> gr2 = greenlet ( test2 ) >>> gr1 . switch () one three two Андрей Попп: Разработка сетевых приложений с gevent
    • Микропотоки реализованные с помощью greenlet удобны – они не страдают от недостатков генераторов. Андрей Попп: Разработка сетевых приложений с gevent
    • Теперь нам нужен планировщик, который будет контролировать исполнение гринлетов, руководствуясь событиями I/O. Андрей Попп: Разработка сетевых приложений с gevent
    • gevent = libevent + greenlet Такой планировщик предоставляет нам библиотека gevent. Андрей Попп: Разработка сетевых приложений с gevent
    • Почему используется libevent Почему gevent использует libevent для обработки событий: Это быстрая библиотека, написанная на языке C – сам цикл полностью в C коде. Libevent используется длительное время и хорошо себя зарекомендовала (Chromium, Memcached, Io). Предоставляет встраиваемый HTTP-сервер – evhttp. Имеет API для работы с DNS – evdns. Андрей Попп: Разработка сетевых приложений с gevent
    • Как устроен gevent Общая схема Цикл обработки событий libevent работает в отдельном гринлете – этот гринлет называется хаб. Хаб запускается неявно и только при необходимости. Кооперация между гринлетами происходит через хаб: Гринлет может переключиться только на хаб. Гринлет может получить управление только через хаб. Андрей Попп: Разработка сетевых приложений с gevent
    • Как устроен gevent Организация I/O Чтобы совершить I/O наш гринлет должен: 1 Отправить запрос на I/O в цикл обработки событий. 2 Переключиться на хаб. 3 Хаб запускает выполнение других гринлетов. 4 ... 5 Как только запрос на I/O выполнен, хаб переключается обратно на наш гринлет. Андрей Попп: Разработка сетевых приложений с gevent
    • Блокируется только гринлет, который пытается выполнить I/O, а не весь интерпретатор. Андрей Попп: Разработка сетевых приложений с gevent
    • Сетевой I/O с gevent Чтобы выполнять I/O гринлеты должны использовать кооперативный gevent.socket. Его API полностью повторяет socket стандартной библиотеки Python. Андрей Попп: Разработка сетевых приложений с gevent
    • Сетевой I/O с gevent Кстати, gevent.socket.getaddrinfo, gevent.socket.gethostbyname используют evdns и тоже являются блокирующими только для вызывающего их гринлета. Андрей Попп: Разработка сетевых приложений с gevent
    • Пример: конкурентный эхосервер с gevent Реализация from gevent import socket , spawn def serve (( host , port ) , handler ): acceptor = socket . socket ( socket . AF_INET , socket . STREAM ) acceptor . bind (( host , port )) while True : client , address = acceptor . accept () spawn ( handler , client , address ) def handler ( sock , address ): f = sock . makefile () while True : line = f . readline () if not line : break f . write ( line ) f . flush () Андрей Попп: Разработка сетевых приложений с gevent
    • Пример: конкурентный эхосервер с gevent Обработка соединений Обработка соединения происходит в отдельном гринлете: from gevent import socket , spawn def serve (( host , port ) , handler ): acceptor = socket . socket ( socket . AF_INET , socket . STREAM ) acceptor . bind (( host , port )) while True : client , address = acceptor . accept () spawn(handler, client, address) def handler ( sock , address ): f = sock . makefile () while True : line = f . readline () if not line : break f . write ( line ) f . flush () Андрей Попп: Разработка сетевых приложений с gevent
    • Пример: конкурентный эхосервер с gevent Точки кооперации В этих точках гринлет отдаёт управление циклу libevent: from gevent import socket , spawn def serve (( host , port ) , handler ): acceptor = socket . socket ( socket . AF_INET , socket . STREAM ) acceptor . bind (( host , port )) while True : client, address = acceptor.accept() spawn ( handler , client , address ) def handler ( sock , address ): f = sock . makefile () while True : line = f.readline() if not line : break f.write(line) f.flush() Андрей Попп: Разработка сетевых приложений с gevent
    • Оказалось достаточно использовать gevent.socket вместо socket и вызвать gevent.spawn в нужном месте. Андрей Попп: Разработка сетевых приложений с gevent
    • Пример: конкурентный эхосервер с gevent Используем StreamServer Нужно использовать gevent.server.StreamServer: from gevent . server import StreamServer def handler ( sock , address ): f = sock . makefile () while True : line = f . readline () if not line : break f . write ( line ) f . flush () StreamServer (( ’ localhost ’ , 6000) , handler ). serve_forever () Андрей Попп: Разработка сетевых приложений с gevent
    • Мы умеем создавать новые гринлеты (gevent.spawn) и использовать gevent.socket. Посмотрим, что ещё мы можем делать с gevent. Андрей Попп: Разработка сетевых приложений с gevent
    • Базовые возможности gevent Ждём завершения работы гринлета Ждём пока гринлет прекратит свою работу: >>> task = gevent . spawn ( lambda a , b : a + b , 1 , 2) >>> task . join () Если нам нужен результат работы гринлета: >>> task = gevent . spawn ( lambda a , b : a + b , 1 , 2) >>> task . get () 3 В случае, если гринлет прекратил работу из-за исключения: >>> task = gevent . spawn ( lambda a , b : a / b , 1 , 0) >>> task . get () Traceback ( most recent call last ): ... Z e r o D i vi s i o n E r r o r : integer division or modulo by zero Андрей Попп: Разработка сетевых приложений с gevent
    • Базовые возможности gevent Преждевременное завершение гринлета Чтобы завершить выполнение гринлета: >>> task = gevent . spawn ( lambda a , b : a + b , 1 , 2) >>> task . kill () Андрей Попп: Разработка сетевых приложений с gevent
    • Базовые возможности gevent Приостанавливаем выполнение гринлета Иногда нужно приостановить выполнение гринлета: >>> def some_work (): ... # do some work ... gevent . sleep (10) ... # continue Функция gevent.sleep аналогична time.sleep, только “засыпает” не весь интерпретатор, а отдельный гринлет. Андрей Попп: Разработка сетевых приложений с gevent
    • Базовые возможности gevent Обработка таймаутов Обработка таймаутов осуществляется с gevent.Timeout: timeout = Timeout (10) timeout . start () try : # do some work except gevent . Timeout : # handle timeout finally : timeout . cancel () . . . или как контекст-менеджер: try : with gevent . Timeout (10): # do some work except gevent . Timeout : # handle timeout Андрей Попп: Разработка сетевых приложений с gevent
    • Управляем несколькими гринлетами Объединяем гринлеты в группы Иногда нужно управлять несколькими гринлетами сразу: >>> tasks = gevent . pool . GreenletSet () >>> for i in range (10): ... tasks . spawn ( do_some_work , i ) >>> tasks . join () . . . или. . . >>> tasks = gevent . pool . GreenletSet () >>> tasks . map ( lambda a : a **2 , range (10)) [0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81] Андрей Попп: Разработка сетевых приложений с gevent
    • Управляем несколькими гринлетами Работаем с пулом гринлетов А иногда бывает нужно ограничить количество одновременно выполняемых гринлетов в группе: >>> tasks = gevent . pool . Pool ( size =5) >>> for i in range (10): ... tasks . spawn ( do_some_work , i ) >>> tasks . join () В данном случае будет одновременно исполняться только 5 гринлетов. Таким образом можно, например, ограничить количество одновременно обрабатываемых соединений. Андрей Попп: Разработка сетевых приложений с gevent
    • HTTP-сервисы с gevent Используем evhttp Модуль gevent.http предоставляет API для использования evhttp, но нас больше интересует WSGI. Андрей Попп: Разработка сетевых приложений с gevent
    • HTTP-сервисы с gevent WSGI сервер Модуль gevent.wsgi – реализация WSGI на базе gevent.http: from gevent . wsgi import WSGIServer def hello_world ( environ , star t_resp onse ): star t_resp onse ( ’200 OK ’ , [( ’ Content - Type ’ , ’ text / html ’)]) return [" It works !"] WSGIServer (( ’ localhost ’ , 8000) , hello_world ). serve_forever () Можно использовать практически любой WSGI фрэймворк/библиотеку: Django, Werkzeug, WebOb, repoze.bfg, Pylons. Андрей Попп: Разработка сетевых приложений с gevent
    • Используем gevent с другими библиотеками Как уже говорилось, API gevent.socket полностью повторяет socket из стандартной библиотеки Python. Андрей Попп: Разработка сетевых приложений с gevent
    • Используем gevent с другими библиотеками Предоставляем фабрику кооперативных сокетов Если библиотека позволяет пользовательскому коду подменять класс используемого сокета: from s om e n e t w o r k l i b r a r y import Client from gevent import socket class C o o p e r a t i v e G e v e n t A w a r e C l i e n t ( Client ): def create_socket ( self ): sock = socket . socket ( socket . AF_INET , socket . STREAM ) return sock Но что делать, если не позволяет? Андрей Попп: Разработка сетевых приложений с gevent
    • Используем gevent с другими библиотеками Monkey patching gevent предоставляет возможность пропатчить модуль socket стандартной библиотеки: from gevent import monkey monkey . patch_socket () После этого, код, который использует модуль socket будет кооперироваться. Андрей Попп: Разработка сетевых приложений с gevent
    • Используем gevent с другими библиотеками Monkey patching Кроме этого в gevent.monkey: patch_time() – заменяем time.sleep() на кооперативный gevent.sleep(). patch_thread() – создаём гринлеты вместо потоков ОС, также патчит threading.local. patch_os(), patch_ssl(), patch_select() – . . . patch_all() – патчим всё. Андрей Попп: Разработка сетевых приложений с gevent
    • Пример: используем gevent с urllib2 from gevent . pool import Pool from gevent import monkey monkey . patch_all () import urllib2 tasks = Pool ( size =20) urls = [ ’ http :// www . gevent . org ’ , ...] def print_head ( url ): print ’ Starting %s ’ % url data = urllib2 . urlopen ( url ). read () print ’% s : % s bytes : %r ’ % ( url , len ( data ) , data [:50]) for url in urls : tasks . spawn ( print_head , url ) tasks . join () Андрей Попп: Разработка сетевых приложений с gevent
    • Используем gevent с другими библиотеками Я также использовал gevent совместно с SQLAlchemy, boto. Андрей Попп: Разработка сетевых приложений с gevent
    • Где используется gevent Несколько проектов, которые используют gevent: Gunicorn – WSGI HTTP сервер, может использовать gevent для обработки запросов. pastegevent – используем gevent.wsgi для запуска WSGI приложений вместе с PasteDeploy. gevent-mysql – драйвер для MySQL, написанный на Cython, использующий API gevent. psycogreen – отдельная ветка psycopg, которая работает с асинхронными библиотеками, например с gevent. Андрей Попп: Разработка сетевых приложений с gevent
    • Некоторые ограничения Как это обычно бывает, существуют некоторые ограничения: После os.fork() необходимо вызывать gevent.reinit(). Библиотеку можно использовать только в одном потоке ОС – ограничение libevent 1.4. Блокирующий stdin – вскоре будет исправлено. Библиотеки которые не используют socket блокируют интерпретатор полностью – можно выполнять их в отдельном потоке ОС. Андрей Попп: Разработка сетевых приложений с gevent
    • Какие темы я не затронул Остались темы, которые я не затронул: Линки между гринлетами. Примитивы синхронизации – gevent.event. Синхронные очереди – gevent.queue. Андрей Попп: Разработка сетевых приложений с gevent
    • Полезные ссылки http://gevent.org – официальный сайт и документация. http://bitbucket.org/denis/gevent/ – исходный код. http://groups.google.com/group/gevent – рассылка. http://blog.gevent.org/ – блог проекта. http://twitter.com/gevent – twitter проекта. И наконец #gevent на irc.freenode.net. Андрей Попп: Разработка сетевых приложений с gevent
    • Спасибо! Андрей Попп: Разработка сетевых приложений с gevent