Проекты Python в рамках Google Summer of Code — gevent

    Уже весна, а это значит… что скоро лето и очередное Google Summer of Code — возможность получить кучу опыта и даже какое-то материальное вознаграждение. Хочу рассказать об одном интересном проекте, которому вы сможете помочь во время летних каникул — gevent.

    gevent — это библиотека для Python, которая позволяет вам писать асинхронные сетевые приложение использую синхронный API. Как вы уже догадались, она использует для этого greenlet'ы. Поясню вышесказанное на следующем примере кода (взятого из официального репозитория проекта на bitbucket):

    """Spawn multiple workers and wait for them to complete"""
    
    urls = ['http://www.google.com', 'http://www.yandex.ru', 'http://www.python.org']
    
    import gevent
    from gevent import monkey
    monkey.patch_all()
    
    import urllib2
    
    def print_head(url):
        print 'Starting %s' % url
        data = urllib2.urlopen(url).read()
        print '%s: %s bytes: %r' % (url, len(data), data[:50])
    
    jobs = [gevent.spawn(print_head, url) for url in urls]
    gevent.joinall(jobs)
    

    Основная прелесть этого кусочка кода в том, что все запросы на получение страниц с помощью вызова urllib2.urlopen выполняются параллельно. Аналогичный трюк можно проделать с любой библиотекой, которая использует модуль socket. Заинтригованы? У меня даже получалось использовать в приложениях, написанных с использоваием gevent, такие мощные библиотеки как SQLAlchemy (конечно необходимо использовать DB-API 2.0 адаптеры с python-сокетами). Резюме — читаемый код, который выполняет асинхронный I/O и никаких коллбэков (вы помните Twisted?), который к тому же выполняется быстро. Почему я утверждаю, что быстро? Потому что «под капотом» у gevent — Libevent — быстрая и проверенная временем библиотека, предоставляющая цикл обработки событий (event loop), который находится полностью в С коде. А в добавок в Libevent есть DNS-резольвер и HTTP-сервер, что gevent использует наполную (модули gevent.dns, gevent.http). Убедиться в скорости gevent можно, например, пройдя по ссылке и посмотрев результаты бенчмарков среди WSGI-серверов, написанных на Python.

    А теперь конкретно об идеях на GSoC для gevent, а их пока только две, что впрочем не мешает вам предлагать свои:
    • Портирование gevent для использования со Stackless Python.
    • Реализация в gevent циклов обработки событий (event loops) отличных от libevent.

    Как вы знаете, модуль greenlet, который используется в gevent, это всего лишь «выжимка» из Stackless Python. И почему бы gevent не заставить работать вместе со Stackless Python. Во-первых, для последнего ещё нету качественной библиотеки, реализующей неблокирующие сокеты, и если она появится, пользователи Stackless Python будут вам очень благодарны. Во-вторых, «микропотоки» (tasklets) Stackless Python работают быстрее чем гринлеты, что даст немалый прирост скорости в приложениях, написанных с gevent.

    Насчёт реализации циклов обработки событий просто приведу свои мысли:
    • Интеграция цикла gevent с циклами GUI-библиотек (pygtk, PyQt4).
    • Nginx mod_wsgi предоставляет API к своему poll-интерфейсу, который можно использовать для реализации цикла gevent — только представьте — nginx как сервер асинхронных python-приложений. По-моему, звучит классно.

    Если вам стало интересно, подписывайтесь на официальный список рассылки gevent, там как раз только начинается обсуждение проектов GSoC.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 24
    • +1
      Поправьте код (используйте тег pre или какой-нибудь раскрашиватель кода) — это же python, а без отступов вообще не читается.
      • +1
        Основная прелесть этого кусочка кода в том, что все запросы на получение страниц с помощью вызова urllib2.urlopen выполняются параллельно.


        Вообще-то gevent однопоточный, а grennlets — это не native threads — это всего лишь одна из техник асинхронного программирования (наряду с callbacks). Они дают псевдомногопоточность, так же как и tasklets в Stackless. Они выполняются не параллельно. Асинхронные вызовы все равно будут отправляться по очереди, а не в один момент времени, так же как и обработка ответов будет по очереди.
        • 0
          Не вижу противоречия между отквоченым и комментарием. Причем тут потоки? Запросы будут исполняться параллельно, т.е. следующий стартует не после завершения предыдущего, а во время ожидания его результата. Асинхронно=параллельно.
          Потоки тут неэффективны, на них затрачиваются лишние ресурсы, а они все время спят, ожидая ввода-вывода и результатов запроса. Яркий пример — шустрый и легкий nginx.
          • 0
            А я юзал threading.Thread run, join для таких целей.

            Думал, gevent — это хттп-сервер для питон.
            • 0
              А я юзал threading.Thread run, join для таких целей.

              Думал, gevent — это хттп-сервер для питон.
              • +1
                >следующий стартует не после завершения предыдущего

                А я и не утверждал обратное. Я говорил о самом запросе и разборе ответа, а не о ожидании ответа. Т.е. скажем, мы послали первый запрос, затем послали второй и т.д., а потом собираем по очереди ответы. Мы не можем в один и тот же момент времени послать 2 запроса. Мы можем послать второй запрос не дожидаясь ответа первого, но НЕ одновременно с ним.

                >Асинхронно=параллельно
                Не во всех случаях. Вот смотрите, пока будет парсится http-response одного запроса разве можно в это время сделать другой? Нет! Эта операция заблокирует поток. Я понимаю что на очень-очень короткое время, но все же за это время мы ничего другого сделать не сможем, поэтому тут слово параллельно не очень хорошо подходит.
                • 0
                  > Мы не можем в один и тот же момент времени послать 2 запроса. Мы можем послать второй запрос не дожидаясь ответа первого, но НЕ одновременно с ним.

                  в асинхронном режиме запрос посылается моментально

                  разбирать ответы было бы разумно в нескольких процессах через multiprocessing
                  или передавая их через mq в другое приложение
                  • +1
                    >в асинхронном режиме запрос посылается моментально

                    Что такое моментально? :-) 1 сутки — это моментально? НЕт? А в масштабах вселенной — это очень даже моментально :-)

                    >или передавая их через mq в другое приложение

                    Вот это на мой взгляд самый хороший подход.

                    • 0
                      > Что такое моментально? :-)
                      это значит что можно послать гораздо больше запросов в асинхронном режиме(особенно, если ещё и делать это из нескольких процессов), чем создав кучу тредов
                  • 0
                    Мы с вами не договорились о термине «параллельно». Вы ставите знак равенства со словом «многопоточно», я считаю это не совсем верным. По-моему параллельно — это «в одно и то же время».

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

                    Если говорить о более мелких частях запроса, конкретно в это библиотеке не знаю, но в классической асинхронной программе парсинг http-response будет также идти одновременно, готовы 10 байтов того респонза (вытащены из сокета) — парсим их, а пока подходят байты другого респонза.

                    Либо вы хотите сказать параллельно — это «не отнимает процессорное времени у другого». Но и при таком толковании потоки не панацея — они также отнимают время друг у друга на одноядерном процессоре, да и на многоядерном блокировки неизбежны для синхронизации доступа к ресурсам. «Отнимание процессорного времени» здесь дешево, обработка запроса несоизмерима с тактами процессора.

                    Стоит также заметить, что по проводу данные идут не «мультитредно» а однопоточно, для «многозадачности» используется именно асинхронность, поэтому она естественна для сетевых приложений (правда здорово запутывает код).
                    • 0
                      >Вы ставите знак равенства со словом «многопоточно», я считаю это не совсем верным.

                      Где вы такое прочитали? O_o Параллельно для меня — э то значит в один и тот же момент времени, а не по очереди.

                      • 0
                        >Вы ставите знак равенства со словом «многопоточно», я считаю это не совсем верным.

                        Где вы такое прочитали? O_o Параллельно для меня — э то значит в один и тот же момент времени, а не по очереди.

                        >да и на многоядерном блокировки неизбежны для синхронизации доступа к ресурсам.

                        Все же скажем на 2-х ядерном процессоре возможно именно одновременное (параллельное в моем понимании) выполнение 2-х процессов.

                        Да, ладно вам. Я просто к слову придрался чуть-чуть, не обращайте внимания :-) Я вижу, что мы с вами одинаково это понимаем, только называем по-разному :-(

                        • 0
                          >Стоит также заметить, что по проводу данные идут не «мультитредно» а однопоточно

                          Вот это отличное замечание. Но ведь мы имеем обычно множество вариантов того, как сигнал дойдет до получателя. В смысле, множество маршрутов. Или нет?
                  • 0
                    Спасибо что рассказали о такой либе, у меня как раз есть небольшая программа делающая много сетевых запросов, сегодня же попробую в действии.
                    • 0
                      Если не секрет, а кто собирается менторить проекты со стороны gevent?
                      • +1
                        Создатель gevent, Денис Биленко. Если возьмётесь на портирование gevent на Stackless, там также будет участвовать, как ментор, Richard Tew.
                        • 0
                          За портирование не возьмусь, возрастом не вышел, но за информацию спасибо.
                      • 0
                        Охренеть, monkey.patch_all() и всё сетевое асинхронится. Реальная волшба :)

                        Потестил wsgi-сервер с отключенным spawn, быстрее tornado работает. Со включенным, чуточку медленнее последнего. Памяти сравнимо кушают.

                        В общем, рад несказанно, респектище :)
                        • 0
                          Nginx mod_wsgi предоставляет API к своему poll-интерфейсу, который можно использовать для реализации цикла gevent — только представьте — nginx как сервер асинхронных python-приложений.
                          Не надо переоценивать роль энджиникса. Это распространенная ошибка, когда хотят в серверную логику запихать логику бэкэнда. Это может привести к сокетному голоданию и тогда наступит полный, сами понимаете что.
                          • 0
                            Надо бы попробовать libev прикрутить. Ещё быстрее будет)
                            • 0
                              Не факт, я давно не видел бенчмарков для актуальных версий libevent и libev.
                              • 0
                                Мы пробовали :-). Libev шустрее.
                                • 0
                                  Интересно посмотреть на графики :-) вобщем, факты в студию.
                                  • 0
                                    дык а чего факты-то.
                                    вон например www.pps.jussieu.fr/~kerneis/software/files/libev-vs-libevent/dat.t1.png.

                                    давно ясно что libevent2 быстрее чем libevent1 и сравним с libev. Но libevent2 до сих пор в альфа-стадии и там много касяков (течёт по памяти => not production ready).

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