Озеленение Twisted

    Как обычно, в праздники, в свободное время от затирки плитки и прочих прибиваний плинтуса, меня захватила очередная идея из цикла «попробовать». На хабре проскочила статья о Pyrant. Первой итерацией я взял и переделал основную часть протокола на Twisted — github.com/Deepwalker/tx-tokyo. И все было хорошо, наступление шло по всем фронтам, но тут я перешел к питоничной части pyrant, и понял что сделать yield a[megakey]='mega data string', вообще говоря невозможно. Это было очень печально, ведь в статье меня зацепило именно легкое обращение с данными в питоничной форме. Что же делать, Пух, спросил я себя? И вспомнил о greenlet-ах.



    Помучавшись некоторое время, я переписал defer.inlineCallbacks на использование greenlet-ов и смог написать такой код:
    from twisted.internet import defer, protocol, reactor
    from tx_green import inlineCallbacks
    from greentokyo import Tyrant
    
    @inlineCallbacks
    def test_proto():
        t = Tyrant()
        print t.get_stats()
    
        t['kuku'] = 'Green Tyrant!'
        print t['kuku']
    
        reactor.stop()
    
    if __name__=='__main__':
        test_proto()
        reactor.run()
    


    Удобство в том, что greenlet-ы имеют важное отличие от генераторов — они не ограничены одной функцией. То есть при вызове t['kuku'], на самом деле вызывается t.__getitem__, внутри которого мы и делаем вызов switch() для возврата управления основному циклу на время, пока мы ожидаем данные от сервера. Если бы мы попробовали сделать внутри t.__getitem__ yield, то мы бы просто сделали из него генератор, не получив никакой пользы.

    Как видно, достаточно обернуть одну функцию, и все что внутри нее начнет работать в неблокирующем режиме. Причем если какие то модули будут использовать уже модифицированные версии нижележащих модулей, то их синтаксис не поменяется. То есть совсем. Пример — я допишу свой форк pyrant на twisted с использование greenlet-ов, и смогу использовать models без изменений.

    Неудобство состоит в том, что надо помнить обо всей этой начинке — будет плохо если вы будете ждать данные, а вместо этого вам упадет просто Deferred. И будет еще хуже, если вы забудете вернуть управление основному потоку — остановите все приложение. И еще в этом варианте не светит «not twist your brain», потому что надо помнить и использовать возможности Twisted.

    Стоит обратить внимание, что в приведенном коде нет ничего, явно указывающего на возврат управления — вызовы greenlet.switch() скрываются в реализации питоничного Tyrant. То есть он заточен под эту реализацию. Если бы это было не так, то нам бы пришлось явно вызывать функцию wait из tx_green. Именно этот момент меня беспокоит — можно создать чуть ли не джангу, и обращения к БД через ORM будут работать также скрытно вызывая wait, но если забыться, то потенциально можно породить трудноуловимую ошибку, или новичок, как обычно не прочитав толком документацию, начнет запрашивать с какого-либо сервера странички через urllib и будет долго удивляться, почему сервер подтормаживает.

    Реализация расположена в моей песочнице.

    Вот такой небольшой пост. Как всегда ожидаю приятного обсуждения, и надеюсь, что будут хорошие предложения по коду, как было в прошлой нашей встрече, со мной в амплуа постописателя. Спасибо за внимание.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 34

      0
      Меня больше всего интересует в данном моменте создание или обертка вокруг ORM, для получения асинхронных обращений к БД. Ты пробовал уже что-нибудь подобное реализовать? Интересует, конечно же, работа в контексте psycopg2.
        0
        Ну в общем тема обсуждалась и обсуждается — сопрограммы у нас сейчас на пике моды конечно, а асинхронностью бредят все, и особенно в приложении к Django: )

        Посмотри вот тут — code.google.com/p/coev/source/browse/#hg/examples/django/hwcoev, хотя как я понимаю код в альфе. Я бы попробовал на самом деле влезть джанге в душу, то есть в глубины кода, но сейчас захвачен идеей попробовать собрать свой фреймворк с jinja и с pyrant-models, поверх вот этой вот моей обертки.
          0
          есть готовый проект, схожий с тем, что ты пытаешься собрать. Никак не могу найти источник, там речь шла об обвязке werkzeug+jinja2+sqlalchemy+migrate+command (хотя побродив по просторам интернетов услышал много негатива в адрес jinja2 с планами по переходу на mako). Т.е. всё, что делает джанга, но не велосипедами её создателей, а готовыми питоньими модулями. На эту тему есть пост на pyobject.ru. Много еще пишет Александр Соловьев.
            0
            Svarga, только он, как я понял, тоже не затронут асинхронностью. Я его изучаю сейчас, думаю у Александра есть чему поучится.
              0
              В точку, спасибо за подсказку. Я на него еще не смотрел как следует. В общем-то меня пока еще пугают фреймворки с отсутствием внятного тестирования. UnitTest не справится с тестированием за пределами ядра приложения, особенно, когда нужны behavior-тесты. Смотрю, кстати, в сторону pycukes, но до работы cukes с джангой мне еще не достает тестового окружения.
                +1
                Не, про асинхронность мы даже и не думали, sqlalchemy — синхронная, да и стиль написания асинхронных программ в питоне в большинстве случаев не радует абсолютно… Гринлеты еще туда-сюда, но твистед и торнадо — точно не радуют. :)
                +1
                можно раскрыть тему негатива в адрес jinja2?
                  0
                  Присоединяюсь к вопросу.
                    0
                    тема негатива на самом деле наиграна, раз уж пошла речь об отрыве от стандартного (к слову, довольно медленного) шаблонизатора django — jinja — наиболее близка к нему по синтаксису, чего не скажешь о Mako, есть несколько примеров сравнения синтаксиса, и еще отдельно есть небольшой обзор, c которого я и начинал вспоминать о sarga. В последнем и всплыл этот момент.
                      +2
                      Ну синтаксис как раз можно и сменить (хотя джинджа привлекает тем, что на неë с джанги перейти легко), а Jinja из поста Юры — это Jinja1, и она действительно была несколько странной. В общем и целом Jinja2 очень клëвая, на мой взгляд. :)
                        +1
                        Jinja из поста Юры — это Jinja1, и она действительно была несколько странной. В общем и целом Jinja2 очень клëвая, на мой взгляд.

                        Всё так :) Jinja версий 0.X, 1.X и 2.X — три разные вещи. И Jinja2 хороша, особенно как покопаешься во внутренностях Django templates.
                          0
                          хорошо бы сделать обзор актуальных на сегодняшний день шаблонизаторов в мире python.
                            0
                            Ну, даже если вкратце перечислить просто, то по-моему актуальны всего три — Jinja2, Mako и Genshi. Ну и GHRML, если хочется поиграться с Haml-like синтаксисом. Больше ничего особенно интересного и нету…
                  0
                  В общем у многих идея витает асинхронность джанге привить, но я пока ни одной истории успеха не встречал.
                    +1
                    И в итоге — я сейчас пробую перейти на pylons, уж очень он захватил меня своей модульной структурой, так что не спеши со svarga, хотя бы в том плане, что коммьюнити pylons всё-таки есть, и реактивность вопросов будет выше. Плюс — я же брежу production-ами. Так что svarga пока явно не мой выбор. А если с pylons всё получится, то очередной проект сделаю именно на нём и будет чем поделиться, в плане опыта.
                      0
                      Очень схожие аргументы и у меня насчет pylons.
                      Только куда там прививать асинхронность?
                        0
                        Для асинхронности во всех фреймворках фактически одно главное место, куда ее надо вкручивать — БД. Я сейчас не готов сказать, возможно ли куда то в pylons вкрутить асинхронность, не знакомился с этим фреймворком.
                      +1
                      Прививать асинхронность Django — пустая затея. Более того, я не понимаю цели. Проще говоря, чего желаете добиться, прикрутив асинхронный I/O к веб-фреймворку?

                      Ну т.е. вы можете конечно использовать WSGI-интерфейс twisted или tornado, но только Django/Pylons то остаются синхронным и толку ноль от того, что http-сервер асинхронный.

                      Гораздо продуктивнее (во всех смыслах), IMHO, пытаться «облагородить» twisted или tornado, чтобы можно было писать проще.
                        0
                        они как-раз и хотят сделать Django/Pylons асинхронными.

                        чисто теоретически, оборачивая все блокирующие вызовы в tx_green.wait, можно делать их асинхронными, не меняя код который их использует.
                          0
                          Если быть точным, то wait оборачивает асинхронный код, позволяя мимикрировать коду высшего порядка под синхронный. Или по другому — код высшего порядка может быть написан в обычном синхронном стиле, если использует для обращений в БД и тп, код обернутый в wait, а сам вызов этой библиотеки обернут в наш декоратор.
                            0
                            согласен, но я о том, что в контексте «асинхронизации джанги» магия этого подхода в том, что его можно воткнуть в небольшом количестве мест в самом низу, почти не меняя сам фреймворк
                    0
                    Любопытно. В коде ничего не понял, но это, скорее всего, проблема чтеца. Особенно если учесть, что в репозитории не обучающий пример и не заранее продуманный проект, а потенциально полезный эксперимент (как и сами Pyrant и Models).

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

                      А баги — ну была мысль ломать модуль socket или что то в этом духе — поймаешь программу, которая socket пытается использовать, и сразу предупреждение. Хотя как я понимаю с драйвером mysql вот не сработает — он, наверное, питоновский сокет и не использует. В общем по хорошему как то надо универсально заблокировать сетевые операции, идущие мимо twisted, и тогда потенциальных проколов будет меньше.

                      С другой стороны подкладывание соломки это, как мне кажется, не python way. В питоне же нет приватных методов у классов и тп — рассчитано, что программист знает, что он делает.
                      0
                      помойму очень круто.

                      только как минимум декоратор не inlineCallbacks всё-же назвать стоит, чтобы не путать, и предупредить что внутри магия.

                      а сам подход очень интересный.
                        0
                        поправил, назвал make_it_green.
                      • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          А что за методы? Никакие гринлеты не помогут сделать синхронное асинхронным — гринлеты это средство скрыть асинхронность. Поэтому если блокирующие методы это какие то вычислительные задачи, то треды или мультипроцессинг, или что-нибудь готовое для этих целей.
                          • НЛО прилетело и опубликовало эту надпись здесь
                            • НЛО прилетело и опубликовало эту надпись здесь
                                0
                                Смотря какая БД. Если postgres, то есть pgasync для Twisted. Да, не для торнадо, но я в нем смысла не вижу. Вообще советую ознакомится именно с Twisted — он не заточен на HTTP only. Реализации mysql на твистед не встречал, но вообще стоит осмотреться — есть много чего готового.

                                А особенно меня просто убил параметр у функции callback в tornado: ) до такого уродства надо было додуматься.
                                • НЛО прилетело и опубликовало эту надпись здесь
                                    0
                                    Возможно, что у торнадо меньше накладных расходов на абстракциях, так как он гораздо менее универсален, но на приложении не hello world эта разница быстро съедается реальными задачами.
                                    Ну и есть ли у торнадо средства для работы с memcache и тп? Возможно ли на торнадо легко и просто реализовать какой то протокол?
                                    В общем в этом вопросе я заодно с авторами twisted — торнадо это обделенный функциональностью велосипед. Я после вашего комментария специально пошел и посмотрел в его код — мон блин! Они даже в пул тредов не удосужились скинуть работу с БД!
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                      • НЛО прилетело и опубликовало эту надпись здесь

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

                            Самое читаемое