Форум сайта python.su
Добрый день.
Мне необходимо написать приложение на 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?
Поясните если кому не сложно. Спасибо.
Офлайн
Натолкнулся в 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()
Офлайн
Почитайте здесь разбор аналогичной задачи: http://www.python.su/forum/viewtopic.php?id=9423
Задавайте вопросы.
Офлайн
Спасибо, как раз читаю эту задачу.
Впринципе в той задаче мне кажется все понятно, но с другой стороны посмотришь - какая-то каша в голове.
Когда мы делаем wpool.spawn(worker) у нас появляется новый гринлет который выполняет worker-функцию.
Внутри этого воркера мы можем еще раз сделать wpool.spawn(other_worker)? При этом блокировки не произойдет?
В доке по geventу у многих функций написано что они приводят к блокировке. Это понимать так, что блокируется текущий гринлет и происходит switch в основной? Или я не прав?
Офлайн
Изучаем, как работает неблокирующий IO в теории.
Применяем знания к gevent.
Офлайн
Изучил, знаю как работает.
Как к geventy это прикрутить - не понимаю. Потому что в gevenе помимо event-loopа еще и гринлеты появляются.
В обычном случае:
1) Открыть сокеты
2) Послать запрос через них
3) Дескрипторы засунуть в epoll
4) Ждать когда epoll вернет нам сокет где есть результат
5) Считать данные без блокировки
В gevent'e :
1) Создать функцию
2) greenlet.spawn(функция, параметр)
3) Создается Hub гринлет который диспатчит другие гринлеты.
А дальше что?
greenlet.get()? Так это вроде блокирующий вызов.
Отредактировано (Фев. 12, 2011 16:31:37)
Офлайн
VolttЧестно говоря не знаю, но я бы просто не стал бы так делать. Зачем вам эта вложенность? Вам же нужен конечный результат - вот и создавайте все задачи на верхнем уровне. Flat is better than nested. Кстати, именно для того, чтобы уйти от вложенности в примере используется очередь.
Когда мы делаем wpool.spawn(worker) у нас появляется новый гринлет который выполняет worker-функцию.
Внутри этого воркера мы можем еще раз сделать wpool.spawn(other_worker)? При этом блокировки не произойдет?
В доке по geventу у многих функций написано что они приводят к блокировке. Это понимать так, что блокируется текущий гринлет и происходит switch в основной? Или я не прав?Скорее блокируется текущий greenthread и происходит переключение в остальные. Хотя лучше дайте ссылку на описание конкретной функции, где это сказано.
Офлайн
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
Скорее блокируется текущий greenthread и происходит переключение в остальные. Хотя лучше дайте ссылку на описание конкретной функции, где это сказано.http://www.gevent.org/gevent.html#gevent.Greenlet.get
Офлайн
Volttверно.
Значит можно сделать pool.spawn(another_task) и тогда внутри одного пула будет крутиться и коннекты входящие и исходящие.
Видимо: If block is True, unschedule the current greenlet until the result is available or the timeout expires. Происходит переключение в другой гринлет.Ну да, просто ваш заблокированый гринлет исключается из диспетчеризации. Все остальные диспетчеризируются нормально. По-моему все логично.
Офлайн
Я вот что подумал. Внутри handle функции (та что вызывается когда происходит коннект) мне надо будет ждать результат от pool.spawn(another_task). Если я напишу pool.joinall() то получится что я жду результат и исходящих и входящих коннектов.
Как быть?
Офлайн