Найти - Пользователи
Полная версия: gevent и разработка на нем
Начало » Web » gevent и разработка на нем
1 2
Voltt
Добрый день.

Мне необходимо написать приложение на python, которое должно в ответ на http запрос (POST) обращаться к другим серверам, брать с них инфу и отдавать клиенту (по типу web-прокси). Я решил разрабатывть это на базе библиотеки gevent, хотя вначале думал использовать низкоуровневые вызовы системы (epoll).
Уже второй день мучаю документацию по gevent, но пока никак не могу понять как мне подступить к задаче. Дело в том что я не совсем корректно представляю как работает gevent. Я так понимаю (используя доку по eventlet) мне надо использовать dispatch паттерн http://eventlet.net/doc/design_patterns.html#dispatch-pattern.

На примерах вроде бы все ясно, но тогда не понятно зачем нужны Event, AsyncResult, Queue, JoinableQueue, Pool и прочие классы.
Или это высокоуровневая абстракция для удобной работы? Чтоб не вручную спавнить гринлеты и ожидать от них выполнения, а использовать очереди и пулы?
Но по сути будет работать именно тот механизм, который был описан в доке http://www.gevent.org/intro.html#event-loop?

Поясните если кому не сложно. Спасибо.
Voltt
Натолкнулся в gevent'e на такой пример

#!/usr/bin/env python
"""A web.py application powered by gevent"""

from gevent import monkey, pool; monkey.patch_all()
from gevent.wsgi import WSGIServer
import gevent
import web
import urllib2

urls = ('/long', 'long_polling')

class long_polling:
# Since gevent.wsgi executes each incoming connection in a separate greenlet
# long running requests such as this one don't block one another;
# and thanks to "monkey.patch_all()" statement at the top, thread-local storage used by web.ctx
# becomes greenlet-local storage thus making requests isolated as they should be.
def GET(self):
print 'handling GET context id = %s' % (id(web.ctx._getd()), )
urls = ['http://www.yandex.ru', 'http://www.google.ru', 'http://www.python.su']
jobs = [gevent.spawn(self._print_head, url) for url in urls]
gevent.joinall(jobs, 10)
return "".join([job.value for job in jobs])

def _print_head(self,url):
data = urllib2.urlopen(url).read()
return '%s: %s bytes: %r' % (url, len(data), data[:10])

if __name__ == "__main__":
application = web.application(urls, globals()).wsgifunc()
pool = pool.Pool(10)

print 'Serving on 8088...'
WSGIServer(('', 8088), application, spawn=pool).serve_forever()
Добавил в него пул гринлетов (10 штук) чтобы ограничить количество одновременных соединений с сервером.
Насколько я понимаю каждый запрос порождает гринлет. Могу ли я внутри этого гринлета запустить еще гринлетов (как я сделал в функции GET)?
Или я неправильно понимаю принцип его работы?
Ed
Почитайте здесь разбор аналогичной задачи: http://www.python.su/forum/viewtopic.php?id=9423
Задавайте вопросы.
Voltt
Спасибо, как раз читаю эту задачу.
Впринципе в той задаче мне кажется все понятно, но с другой стороны посмотришь - какая-то каша в голове.

Когда мы делаем wpool.spawn(worker) у нас появляется новый гринлет который выполняет worker-функцию.
Внутри этого воркера мы можем еще раз сделать wpool.spawn(other_worker)? При этом блокировки не произойдет?

В доке по geventу у многих функций написано что они приводят к блокировке. Это понимать так, что блокируется текущий гринлет и происходит switch в основной? Или я не прав?
Андрей Светлов
Изучаем, как работает неблокирующий IO в теории.
Применяем знания к gevent.
Voltt
Изучил, знаю как работает.
Как к geventy это прикрутить - не понимаю. Потому что в gevenе помимо event-loopа еще и гринлеты появляются.
В обычном случае:
1) Открыть сокеты
2) Послать запрос через них
3) Дескрипторы засунуть в epoll
4) Ждать когда epoll вернет нам сокет где есть результат
5) Считать данные без блокировки

В gevent'e :
1) Создать функцию
2) greenlet.spawn(функция, параметр)
3) Создается Hub гринлет который диспатчит другие гринлеты.
А дальше что?
greenlet.get()? Так это вроде блокирующий вызов.
Ed
Voltt
Когда мы делаем wpool.spawn(worker) у нас появляется новый гринлет который выполняет worker-функцию.
Внутри этого воркера мы можем еще раз сделать wpool.spawn(other_worker)? При этом блокировки не произойдет?
Честно говоря не знаю, но я бы просто не стал бы так делать. Зачем вам эта вложенность? Вам же нужен конечный результат - вот и создавайте все задачи на верхнем уровне. Flat is better than nested. Кстати, именно для того, чтобы уйти от вложенности в примере используется очередь.

В доке по geventу у многих функций написано что они приводят к блокировке. Это понимать так, что блокируется текущий гринлет и происходит switch в основной? Или я не прав?
Скорее блокируется текущий greenthread и происходит переключение в остальные. Хотя лучше дайте ссылку на описание конкретной функции, где это сказано.
Voltt
Ed
Честно говоря не знаю, но я бы просто не стал бы так делать. Зачем вам эта вложенность? Вам же нужен конечный результат - вот и создавайте все задачи на верхнем уровне. Flat is better than nested. Кстати, именно для того, чтобы уйти от вложенности в примере используется очередь.
Я бы и сам рад уйти от такой вложенности, но
gevent
any new connection accepted on 127.0.0.1:1234 will result in a new Greenlet spawned using handle function

http://www.gevent.org/servers.html
Как раз мой случай, т.к. мне надо принять входящее соединение, а потом создать внутри сервера исходящие
Хотя я же при старте сервера передаю spawn=pool. Внутри пула у меня как раз лежат все гринлеты.
Значит можно сделать pool.spawn(another_task) и тогда внутри одного пула будет крутиться и коннекты входящие и исходящие.

Скорее блокируется текущий greenthread и происходит переключение в остальные. Хотя лучше дайте ссылку на описание конкретной функции, где это сказано.
http://www.gevent.org/gevent.html#gevent.Greenlet.get
Видимо: If block is True, unschedule the current greenlet until the result is available or the timeout expires. Происходит переключение в другой гринлет.
Где-то еще видел эти функции, пока не могу найти…
Ed
Voltt
Значит можно сделать pool.spawn(another_task) и тогда внутри одного пула будет крутиться и коннекты входящие и исходящие.
верно.

Видимо: If block is True, unschedule the current greenlet until the result is available or the timeout expires. Происходит переключение в другой гринлет.
Ну да, просто ваш заблокированый гринлет исключается из диспетчеризации. Все остальные диспетчеризируются нормально. По-моему все логично.
Voltt
Я вот что подумал. Внутри handle функции (та что вызывается когда происходит коннект) мне надо будет ждать результат от pool.spawn(another_task). Если я напишу pool.joinall() то получится что я жду результат и исходящих и входящих коннектов.
Как быть?
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB