Сегодня выступал на HighLoad++ с докладом Twisted Framework – фреймворк для написания сетевых приложений в Python.
Введение
Последнее время в области web происходит смещение внимания с тяжелых application-серверов, которые тратят на обработку запроса сотни миллисекунд, а то и секунды, к более легковесным сервисам, передающим меньшие объемы данных с минимальной задержкой. Переход от генерации десятков и сотен килобайт HTML-кода в ответ на запрос к передаче изменений в данных, запакованных в JSON и измеряемых сотнями байт. В качестве примеров таких сервисов можно привести Gmail, FriendFeed, Twitter Live Search и т.п.
Для обеспечения минимальной задержки для пользователя необходимо либо поддерживать постоянное соединение (например, Adobe Flash, RTMP) или использовать технику HTTP long polling в сочетании с keep alive. Так или иначе на стороне сервера это приводит к появлению большого количества одновременных соединений (тысячи, десятки тысяч), по каждому из которых передается не такой большой объем данных. Эту ситуацию называют обычно проблемой C10k.
Для обработки соединений архитектурный выбор на стороне сервера не такой большой: процесс на соединение, нить на соединение, комбинированный вариант процесс-нити или асинхронный ввод-вывод (возможно, в сочетании с дополнительными процессами или нитями). При наличии более 10 тысяч одновременных соединений с точки зрения расхода ресурсов совершенно невозможно представить создание 10 тысяч процессов; 10 тысяч нитей также вряд ли будет разумным решением. Необходимо дополнительно учесть, что при наличии такого большого числа соединений объем работы по каждому из них относительно невелик, большинство их них простаивают в ожидании поступления новых данных. Поэтому бóльшая часть процессов или нитей будет просто находиться в состоянии ожидания, расходуя впустую системные ресурсы. Асинхронный ввод-вывод позволяет осуществлять неблокирующийся сетевой ввод-вывод по тысячам открытых сокетов в рамках одной нити выполнения (одного процесса). Механизмы реализации в разных ОС разные, например: select(), poll(), epoll(), kqueue() и т.п. Примеры приложений, использующих асинхронный ввод-вывод:
- nginx (используются дополнительные процессы для обслуживания задач, требующих большего объема CPU);
- haproxy;
- memcached;
- и другие.
Тем не менее, асинхронный ввод-вывод не является универсальным решением: для сервера БД это вряд ли было бы хорошим способом организации обслуживания соединений, так как для обработки каждого запроса требуется большой объем дискового ввода-вывода и процессорного времени, что не позволяет это сделать в рамках одного процесса.
Twisted Framework — это обширный набор классов и модулей для реализации асинхронных сетевых приложений. Twisted Framework — это:
- ядро, абстрагирующее все операции асинхронного ввода-вывода и использующее соответствующий механизм конкретной ОС;
- концепция Deferred, которая позволяет реализовать в простой форме обслуживание запроса: асинхронные сетевые обращения (например, к БД, memcached), обработку ошибочных ситуаций; Deferred является аналогом обычных конструкций последовательного программирования для асинхронной модели программирования;
- обширный набор уже реализованных сетевых протоколов: HTTP, DNS, SMTP, IMAP, memcached, Jabber, ICQ и т.д.; еще большее количество протоколов доступно в виде дополнительных модулей;
- дополнительная инфраструктура: unit-testы с поддержкой Deferred, пулы нитей, процессов и т.д.;
- качественная концепция разработки — полное покрытие unit-testами, строгий review любого изменения.
Основная часть доклада будет посвящена конкретным примерам приложений, реализованными с помощью Twisted — с архитектурой, конкретными параметрами производительности, приемами оптимизации, преимуществами и недостатками Twisted для решения данной задачи:
- RTMP-сервер pyFMS, сервер вещаний сервиса Smotri.Com (сотни трансляций, десятки тысяч зрителей);
- backend-сервер проекта MDC – хранение и обработка истории общения пользователей, хранение настроек и т.п.;
- Qik Push Engine – сервер немедленной доставки изменений информации о видео, созданных пользователями сервиса, в том числе push-нотификация о появившихся live-стримах, масштабирование, обработка больших объемов информации.
Дополнительная информация:
- Документация Twisted
- О Python, а также о Twisted
- Блог на Хабрахабре про Twisted
- Александр Бурцев о Twisted
- Deferred в Twisted и не только .
Здравствуйте, Андрей! С большим интересом прослушал сегодня ваш доклад. Но вот отвлекся на моменте, когда вы обсуждали уже что то с коллегой из HH, что то сказали про postgresql, то ли вы, то ли он – очень мне интересен истинно асинхронный подход к работе с ним. Известно, что в psycopg2 асинхронный режим есть, а вот возможно ли его скрестить с твистед?
Стоит отметить, что poll()/select() это не асинхронный, а не блокирующий ввод/вывод. Разница может показаться незначительной, но она есть. Довольно хорошо ее видно по структуре паттернов reactor(nbio) и proactor(aio).
Deepwalker, насчет psycopg2 & Twisted – это не сработает, насколько я знаю. Есть реализация postgresql для twisted: http://www.jamwt.com/pgasync/. Она немного кривоватая, ее надо «подкрутить» (я это делал когда-то), и она вполне себе работает.
Denis, неблокирующий ввод-вывод – это fcntl с O_NONBLOCK. Это обязательно делается при любом виде работы с select()/poll(). В нашей и западной терминологии как чистый AIO (который редко где работает нормально), так и комбинация select+nbio называется async IO
Огромное спасибо за доклад, получил невероятное удовольствие!
В чате online трансляции несколько человек очень интересовались вопросом: «Можно ли скрестить Django с Twisted?» Другие же утверждали, что вебовская часть твистеда по меньшей мере странна и недоделана. Что вы думаете по этому поводу?
Alexander, django + twisted есть (google: http://www.google.ru/search?sourceid=chrome&ie=UTF-8&q=django+twisted). Я не пробовал, не могу ничего сказать.
По поводу twisted и web. Обычно, это просто запутанность ситуации в самом Twisted. Есть две реализации web-протоколов: twisted.web & twisted.web2. Когда-то предполагалось, что web2 заменит web, но, как оказалось, web2 получился еще хуже
Сегодня web2 не входит в стандартный пакет twisted вообще (его можно поставить отдельно). Активным разрабатываемым является twisted.web – туда переносятся недостающие фичи из twisted.web2.
По поводу написания http-сервера на twisted порекомендовал бы цикл постов в блоге одного из разработчиков twisted: http://jcalderone.livejournal.com/50562.html
Это я спрашивал : ) Но вот эта схема, с WSGI, никаких плюшек не дает. Для получения какой то выгоды от скрещивания, надо как минимум переделать доступ к БД на асинхронный. То есть по хорошему надо лезть джанге под капот.
Можно сделать свой драйвер для джанговского орма, чтобы, как минмум, ожидание результатов запроса к БД не мешало работе.
А еще стоит наверное запросы тоже принимать самому Twisted и через django.conf.urls.defaults.RegexURLResolver выбирать вьюхи и передавать им параметры. Вьюхи должны будут возвращать HTTPResponse уже не джанговский, что понятно. Вьюхи можно будет заворачивать в декоратор inlineCallbacks.
И после всего этого это уже явно будет не джанго : ) Но скорости работы это все должно добавить, оставив при этом богатство шаблонов, форм, ну и возможно ОРМа. По моему мнению twisted.web на фоне джанго смотрится весьма бледно, так что связка получилась бы неплохой.
P.S. Я находил этот самый pgasync, но давность последнего коммита меня насторожила – обычно все мы проходим мимо мертвых проектов.
Deepwalker, насчет django + twisted я согласен полностью, лучше уж просто на twisted. Но и для twisted есть большое количество «плюшек», например:
P.S. Я сам это все не пробовал
Спасибо, за доклад, он один из лучших.
и про Деферред тоже было интересно узнать, будем учиться
А есть где-нибудь видео конференции за прошлый год?
async sqlalchemy – не плохо работает, но когда нужно реально много данных гонять и обрабатывать при помощи сценариев на Twisted, то прослойка ORM только мешает. Так же с Django. Не нужен там Twisted, если это не внешний сервис с которым можно общаться другими нативными средствами без включения одного проекта в другой.
Асинхронный доступ к базе попросту не нужен. Работает psycopg глючно и на больших нагрузках любит валиться, как 1 так и 2 версия. Смесь Web Framework + Twisted = Zope3 – Ужасный мертворожденный монстр. Спасает Django, AJAX и хорошее проектирование с доступом к данным в обход ORM, через custom manager + memcached, который сохраняет уже готовый результат рендеринга в шаблон на уровне маленьких блоков. Не путайте с templatetag cache.
Андрей, спасибо что развиваете аудиторию Twisted. Думаю что рано или поздно мы познакомимся очно.
Alex, на сайте самого HighLoad (http://highload.ru/) или на smotri.com можно поискать.
Burus, могу добавить из опыта. Очень классно работает система на key-value storage – хранятся объекты, индексы создаются и т.п., надо будет об этом как-нибудь рассказать.
Да, именно через простоту к совершенству. Чаще всего проблемы из-за макаронного кода, в котором разработчики попутно реализации задачи решили сделать универсальный фреймворк =)
Здравствуйте Андрей, из существующих решений key-value storage (CoachDb, MemcacheDB, Redis, MangoDB и т.п.), что бы вы посоветовали?
M0sTH8, сегодня key-value систем очень много. Когда я думал о создании приложения, использующего только key-value хранилище, был доступен только MemcacheDB. Его я и решил попробовать в Qik Push Engine.
С точки зрения доступных фич (мастер-слейв) и производительности он меня совершенно удовлетворяет. MemcacheDB прост, очень прост. Я не могу сказать ничего про другие решения, так как я их сам не пробовал.
@Deepwalker, я использую txpostgres. Никаких тредов, всё работает именно в идеологии twisted. API 1:1 twisted adbapi. Но джанго я не использую, поэтому не в курсе, поможет ли тебе эта инфа.
Насчёт key-value – сейчас их огого, не говоря про документ-ориентированные БД. Меня устраивает связка redis+mongodb+postgres. Каждая применяется в своей части проекта в связи с его спецификой.