Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Coroutine-based servers with Gevent.

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Coroutine-based servers with Gevent.

Avatar for Maksim Kamenkov

Maksim Kamenkov

July 15, 2013

Other Decks in Programming

Transcript

  1. §  Блокирующий I/O – необходимо несколько процессов (потоков) ОС § 

    Неблокирующий (Асинхронный) I/O + мультиплексор – достаточно одного процесса Стратегии организации I/O
  2. §  Необходимо использовать отдельный процесс (поток) на каждое новое соединение.

    §  Много активный соединений -> много процессов -> большой расход памяти. §  Переключение контекста исполнения – дорого. §  GIL. Проблемы использования потоков в Python. Блокирующий I/O Модели prefork, process-per-request
  3. Неблокирующий I/O §  Операции на сокетах не блокируют поток, производятся

    только когда доступны. §  Пока одни соединения “ждут”, можно обрабатывать другие. Достаточно одного процесса (потока). §  Относительно небольшой расход памяти, нет переключения контекста. §  Программу приходится реализовывать в виде обработчиков на события I/O. Бумеранг код.
  4. Неблокирующий I/O Что уже есть? Twisted, Tornado, asyncore, Nodejs, …

    Больше: http://nichol.as/asynchronous-servers-in-python
  5. Неблокирующий I/O def handle_async(): // api call client = AsyncHTTPClient()

    client.get('api.pandadoc.com/token', callback=_on_fetched) def _on_fetched(reponse): // process response and save result to db token = parse (response) db = AsyncDatabaseClient() db.save_token(token, callback=_on_saved) def _on_saved(status): // status string print 'Status: ' + str(status) Писать callbacks утомительно :(
  6. def handle_sync(): // api call client = HTTPClient() response =

    client.get('api.pandadoc.com/token’) // process response and save result to db token = parse (response) db = DatabaseClient() status = db.save_token(token) // status string print 'Status: ' + str(status) §  Синхронный код писать и читать проще. §  Большинство стандартных библиотек написано в синхронном стиле §  Но, приходиться использовать блокирующий I/O
  7. Микропотоки (coroutines) Определение и примеры: http://en.wikipedia.org/wiki/Coroutine §  Программные компоненты, подобные

    функции, исполнение которых можно приостановить, а потом снова продолжить. §  Работают на уровне пользователя внутри одного или нескольких потоков OC. §  Для исполнения микропотоков нужен планировщик. §  Дешевы в плане потребления памяти и переключения контекста.
  8. Планировщик Зачем нужен планировщик и как обрабатывать IO в зеленных

    потоках? §  Когда микропоток хочет выполнить I/O он отдает управление планировщику. §  Когда I/O для данного микропотока становится доступным, планировщик возобновляет работу микропотока. Таким образом callbacks заменяются на переключения между микропотоками.
  9. Многозадачность Это называется кооперативная многозадачность – потоки сами решают когда

    передать исполнение другим. Существует также преемптивная или вытесняющая многозадачность – поток вытесняется планировщиком после определённого количества выполненных инструкций или по истечении определённого времени.
  10. Микропотоки в Python. Генераторы Начиная с версии 2.5 в Python

    появились генераторы (PEP 342). Что бы передать исполнение можно использовать yield (send/next чтобы возобновить) Недостатки: §  Кооперация с yield слишком явно, приходиться думать самому, когда передавать исполнение. §  Генераторы не сохраняют весь стек во время остановки – yield должен быть всегда на самом верху. §  Циклические вызовы внутри генераторов не работают. Больше тут: http://dabeaz.com/coroutines/
  11. Микропотоки в Python. greenlet §  Микропоток или просто гринлет это

    объект greenlet. §  Кооперация посредством вызова метода greenlet.switch. §  greenlet – это “выжимка” из Stackless Python. Официальная документация: http://greenlet.readthedocs.org/
  12. Пример работы greenlet 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
  13. libevent §  Это быстрая библиотека, написанная на языке C –

    сам цикл полностью в C коде. §  Libevent используется длительное время и хорошо себя зарекомендовала (Chromium, Memcached). §  Предоставляет встраиваемый HTTP-сервер – evhttp. §  Имеет API для работы с DNS – evdns. Libev – будет использована в следующих версиях gevent, как более стабильная альтернатива (http://blog.gevent.org/2011/04/28/libev-and-libevent )
  14. Как устроен gevent §  Цикл обработки событий I/O libevent (libev)

    работает в отдельном гринлете, который называется хаб (hub). §  Хаб запускается неявно и только при необходимости. §  Кооперация гринлетов происходить только через хаб §  Гринлет может переключиться только на хаб. §  Гринлет может получить управления только через хаб.
  15. Пример работы gevent §  Что бы выполнить I/O на сокете

    гринлет должен: §  Отправить запрос на I/O в цикл обработки событий. §  Переключиться на хаб. §  Хаб запускает цикл обработки событий и при необходимости передает управление другим гринлетам. §  …… §  Когда сокет готов выполнить I/O (не заблокировав поток), хаб возобновляет выполнение нашего гринлета в точке останова.
  16. Уже хорошо, но все еще нужно явно запрашивать I/O и

    следить за переключением на хаб. Нужно ли?
  17. Чтобы выполнять I/O гринлеты должны использовать кооперативный gevent.socket. Его API

    полностью повторяет socket стандартной библиотеки Python. Запросы к DNS тоже асинхронные :)
  18. Пример простого echo-сервера from gevent import socket, spawn def server((host,

    post), handler): acceptor = socket.socket(socket.AF_INET, socket.STREAM) acceptor.bind((host, port)) while True: client, address = acceptor.accept() spawn(handler, client, address) def handle(sock, address): f = sock.makefile() while True: line = f.readline() if not line: break f.write(line) f.flush() - Управление передается циклу libevent - Порождаем гиринлет на каждое клиентское соединение
  19. Monkey patching Механизм, который позволят сделать стандартные библиотеки кооперируемыми. Gevent

    позволяет “патчить” следующие модули: §  socket §  ssl §  os §  time §  select §  thread and trheading from gevent import monkey monkey.patch_socket() monkey.patch_all() … http://en.wikipedia.org/wiki/Monkey_patch
  20. Использование с другими библиотеками и фреймворками Gevent успешно используется с

    Pymongo, SQLAlchemy, boto, … Так же gunicorn, Falsk, Celery, Sentry, Django, … Motor для Tornado: http://emptysqua.re/blog/motor-internals-how-i- asynchronized-a-synchronous-library/
  21. Основные возможности Gevent §  Cоздание и завершение гринлетов. Ожидание гринлетов.

    §  Обработка таймаутов. §  Пулы гринлетов. §  Поддержка http протокола. Быстрый WSGI сервис. §  Кооперативные сокеты с поддержкой SSL и DNS запросов. §  Поддержка thread-local переменных §  Примитивы синхронизации (Event, Lock) §  Синхронные очереди (Queue) §  Линки между гринлетами
  22. Проблемы с gevent и greenlet §  После os.fork() необходимо вызывать

    gevent.reinit(). §  Библиотеку можно использовать только в одном потоке ОС – ограничение libevent 1.4. §  Не масштабируется на многоядерных системах. Лечится использованием гибридной модели (gunicorn.org) §  Библиотеки которые не используют socket блокируют интерпретатор полностью – можно выполнять их в отдельном потоке ОС. §  Циклические ссылки в гринлетах могут привести к утечке памяти из-за проблем со сборщиком мусора (специфика реализации greenlet) §  Вызов функции (callback) в питоне обойдется дешевле чем переключение гринлета. §  Потеря или затирание исключений (нужно очень что-то сломать J).
  23. Где использовать gevent §  Web-краулеры и подобные системы §  Парсинг

    документов §  Моделирование процессов реального мира (игровые события) §  Real-time сообщения
  24. Ресурсы Презентация составлена по мотивам “Разработка сетевых приложений с gevent”

    - http://www.slideshare.net/andreypopp/report-4114331 §  http://www.gevent.org/ §  http://sdiehl.github.io/gevent-tutorial/ §  http://mrjoes.tumblr.com/post/3197071123/gevent §  http://habrahabr.ru/company/ideco/blog/146340/ §  http://blip.tv/pycon-us-videos-2009-2010-2011/pycon-2011-using- coroutines-to-create-efficient-high-concurrency-web- applications-4900984 §  http://blip.tv/pycon-us-videos-2009-2010-2011/pycon-2011-an- outsider-s-look-at-co-routines-4899200 §  http://gunicorn.org/