Уведомления

Группа в Telegram: @pythonsu

#1 Фев. 12, 2011 08:15:36

Voltt
От:
Зарегистрирован: 2010-09-20
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

gevent и разработка на нем

Добрый день.

Мне необходимо написать приложение на 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?

Поясните если кому не сложно. Спасибо.



Офлайн

#2 Фев. 12, 2011 14:55:40

Voltt
От:
Зарегистрирован: 2010-09-20
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

gevent и разработка на нем

Натолкнулся в 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)?
Или я неправильно понимаю принцип его работы?



Офлайн

#3 Фев. 12, 2011 15:09:23

Ed
От:
Зарегистрирован: 2008-12-13
Сообщения: 1032
Репутация: +  13  -
Профиль   Отправить e-mail  

gevent и разработка на нем

Почитайте здесь разбор аналогичной задачи: http://www.python.su/forum/viewtopic.php?id=9423
Задавайте вопросы.



Офлайн

#4 Фев. 12, 2011 15:45:00

Voltt
От:
Зарегистрирован: 2010-09-20
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

gevent и разработка на нем

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

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

В доке по geventу у многих функций написано что они приводят к блокировке. Это понимать так, что блокируется текущий гринлет и происходит switch в основной? Или я не прав?



Офлайн

#5 Фев. 12, 2011 15:58:05

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

gevent и разработка на нем

Изучаем, как работает неблокирующий IO в теории.
Применяем знания к gevent.



Офлайн

#6 Фев. 12, 2011 16:25:23

Voltt
От:
Зарегистрирован: 2010-09-20
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

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)

Офлайн

#7 Фев. 12, 2011 16:38:50

Ed
От:
Зарегистрирован: 2008-12-13
Сообщения: 1032
Репутация: +  13  -
Профиль   Отправить e-mail  

gevent и разработка на нем

Voltt
Когда мы делаем wpool.spawn(worker) у нас появляется новый гринлет который выполняет worker-функцию.
Внутри этого воркера мы можем еще раз сделать wpool.spawn(other_worker)? При этом блокировки не произойдет?
Честно говоря не знаю, но я бы просто не стал бы так делать. Зачем вам эта вложенность? Вам же нужен конечный результат - вот и создавайте все задачи на верхнем уровне. Flat is better than nested. Кстати, именно для того, чтобы уйти от вложенности в примере используется очередь.

В доке по geventу у многих функций написано что они приводят к блокировке. Это понимать так, что блокируется текущий гринлет и происходит switch в основной? Или я не прав?
Скорее блокируется текущий greenthread и происходит переключение в остальные. Хотя лучше дайте ссылку на описание конкретной функции, где это сказано.



Офлайн

#8 Фев. 12, 2011 17:02:08

Voltt
От:
Зарегистрирован: 2010-09-20
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

gevent и разработка на нем

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. Происходит переключение в другой гринлет.
Где-то еще видел эти функции, пока не могу найти…



Офлайн

#9 Фев. 12, 2011 18:08:13

Ed
От:
Зарегистрирован: 2008-12-13
Сообщения: 1032
Репутация: +  13  -
Профиль   Отправить e-mail  

gevent и разработка на нем

Voltt
Значит можно сделать pool.spawn(another_task) и тогда внутри одного пула будет крутиться и коннекты входящие и исходящие.
верно.

Видимо: If block is True, unschedule the current greenlet until the result is available or the timeout expires. Происходит переключение в другой гринлет.
Ну да, просто ваш заблокированый гринлет исключается из диспетчеризации. Все остальные диспетчеризируются нормально. По-моему все логично.



Офлайн

#10 Фев. 13, 2011 09:03:11

Voltt
От:
Зарегистрирован: 2010-09-20
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

gevent и разработка на нем

Я вот что подумал. Внутри handle функции (та что вызывается когда происходит коннект) мне надо будет ждать результат от pool.spawn(another_task). Если я напишу pool.joinall() то получится что я жду результат и исходящих и входящих коннектов.
Как быть?



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version