Уведомления

Группа в Telegram: @pythonsu

#1 Апрель 3, 2014 11:56:43

buddha
От:
Зарегистрирован: 2012-03-02
Сообщения: 422
Репутация: +  15  -
Профиль   Отправить e-mail  

Помогите разобраться, сделать асинхронную обработку get-запроса на tornado.

Задача простая. Шлём на сервер get запрос с параметрами: имя xml-файла, и xpath-выражение, чтобы им раcпарсить xml. Xml-файл лежит в той же директории , что и сервер, для простоты. Сколько уже перечитал про асинхронность, так и не мог осознать абстракции tornado(что-то очевидно, что-то спрятано). Так же не могу выстроить в голове последовательность работы с версией сопрограмм(coroutines).

Допустим xml:

<?xml version="1.0" encoding="utf-8"?>
<main version="1">
<sub number="1">first</sub>
<sub number="2">second</sub>
</main>

Код сервера с неверным обработчиком:
from io import BytesIO
from xml.etree import ElementTree as ET
from tornado.ioloop import IOLoop
from tornado.httpserver import HTTPServer
from tornado.web import *
from concurrent.futures import ProcessPoolExecutor
  
  
class XpathHandler(RequestHandler):
 
    @asynchronous
    def get(self):
        #http://127.0.0.1:8888/xpath/?file=xml_file.orx&xpath=.//
        file_name = self.get_argument('file')
        xpath = self.get_argument('xpath')
        with ProcessPoolExecutor(max_workers=1) as executor:
            future = executor.submit(XpathHandler.parse_xml, file_name, xpath)
            future.add_done_callback(self.callback)
 
    @staticmethod
    def parse_xml(self, file_name, xpath):
        output = BytesIO()
        tree = ET.parse(file_name)
        root = tree.getroot()
        finded_elements = root.findall(xpath)
        resulted_element = ET.Element('result')
        resulted_element.extend(finded_elements)
        tree._setroot(resulted_element)
        tree.write(output, encoding='utf-8', xml_declaration=True)
        result = output.getvalue().decode()
        output.close()
        return result
 
    def callback(self, future):
        self.write(str(future.result()))
        self.finish()
 
if __name__ == '__main__':
    application = Application([
        (r"/xpath/", XpathHandler)],
        debug=True,
    )
    http_server = HTTPServer(application)
    http_server.listen(8888, 'localhost')
    IOLoop.instance().start()
Пример get-запроса:
http://127.0.0.1:8888/xpath/?file=light_xml.xml&xpath=.//
Метод XpathHandler.get() работать нормально не будет, я так написал только лишь чтобы мою мысль лучше выразить. Что я хочу? Вот получил я параметры из get запроса, через executor запустил задачу на парсинг xml-файла, благодаря декоратору @asynchronous соединение не закрывается и ждёт явного вызова метода self.finish(), который я вызываю в колбэке self.callback(). Этот колбэк я передал объекту future, теперь я жду когда в порождённом процессе рапарсится файл, вернётся результат и этот результат отдаст в ответ на get запрос колбэк, после чего закроет соединение.

Помогите сделать это в абстракциях торнадо , пофиг как, хоть на сопрограммах, на объектах Task, Callback, Wait. Упёрся в то, что в документации очень плохо описана работа с этими объектами, тупо описание объектов. К тому же я в асинхронности начинающий и уже сжег кучу нервов =)

Буду рад любому совету. Принципиально python 3.3, tornado, асинхронность, Windows.

Отредактировано buddha (Апрель 3, 2014 11:59:18)

Офлайн

#2 Апрель 3, 2014 19:43:42

o7412369815963
От:
Зарегистрирован: 2009-06-17
Сообщения: 1986
Репутация: +  32  -
Профиль   Отправить e-mail  

Помогите разобраться, сделать асинхронную обработку get-запроса на tornado.

А в чем конретно проблема?
Запрос в get приходит? Данные правильные (fiel_name…) ? Процесс запускается? Процесс возвращает результат? вызывает callback?

Если проблема с ProcessPoolExecutor, можете попробовать асинхронный запуск через subprocess приделать http://www.py-my.ru/post/501627e2bbddbd261e000000

Офлайн

#3 Апрель 3, 2014 19:48:58

o7412369815963
От:
Зарегистрирован: 2009-06-17
Сообщения: 1986
Репутация: +  32  -
Профиль   Отправить e-mail  

Помогите разобраться, сделать асинхронную обработку get-запроса на tornado.

concurrent.futures из стандартной либы, вроде он не как не связан с торнадо, и в теории должен блокировать торнадо при запуске.

Офлайн

#4 Апрель 4, 2014 10:45:28

buddha
От:
Зарегистрирован: 2012-03-02
Сообщения: 422
Репутация: +  15  -
Профиль   Отправить e-mail  

Помогите разобраться, сделать асинхронную обработку get-запроса на tornado.

o7412369815963
А в чем конретно проблема?Запрос в get приходит? Данные правильные (fiel_name…) ? Процесс запускается? Процесс возвращает результат? вызывает callback?Если проблема с ProcessPoolExecutor, можете попробовать асинхронный запуск через subprocess приделать http://www.py-my.ru/post/501627e2bbddbd261e000000

В целом проблема в том, что я не могу понять абстракций tornado для написания асинхронного кода.
-запрос get конечно приходит
-данные верные, проверено(есть подобный алгоритм на джанге, он синхронный, всё работает на ура)
-процесс запускается и висит(не завершается). Ну это как бы и понятно, код неверный, чисто для примера, выражения мысли.
-трэйсбэк при поступлении запроса на сервер
Traceback (most recent call last):
  File "C:\Python33\lib\multiprocessing\queues.py", line 245, in _feed
    send(obj)
  File "C:\Python33\lib\multiprocessing\connection.py", line 206, in send
    ForkingPickler(buf, pickle.HIGHEST_PROTOCOL).dump(obj)
_pickle.PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
-колбэк не вызывает

o7412369815963
concurrent.futures из стандартной либы, вроде он не как не связан с торнадо, и в теории должен блокировать торнадо при запуске.
Это не верно. Уже нырнул в исходный код, всё увидел, вот что документация говорит…
http://www.tornadoweb.org/en/stable/concurrent.html?highlight=run_on#module-tornado.concurrent

Futures are a pattern for concurrent programming introduced in Python 3.2 in the concurrent.futures package (this package has also been backported to older versions of Python and can be installed with pip install futures). Tornado will use concurrent.futures.Future if it is available; otherwise it will use a compatible class defined in this module.

Офлайн

#5 Апрель 4, 2014 12:29:05

o7412369815963
От:
Зарегистрирован: 2009-06-17
Сообщения: 1986
Репутация: +  32  -
Профиль   Отправить e-mail  

Помогите разобраться, сделать асинхронную обработку get-запроса на tornado.

buddha
В целом проблема в том, что я не могу понять абстракций tornado для написания асинхронного кода.
По идее там все просто, на пальцах так:
1) Вместо вызова функций, они помещаются в список “fn_list”
2) есть “бесконечный цикл”/eventloop, который идет по этому списку и вызывает ф-ии. for cb in fn_list: cb()
3) ну и для полноты картины, в этом цикле есть работа с io, где при некоторых событиях помещаются ф-ии в fn_list
Можете сделать свой асинхронный движок, что-б “прочувствовать”.

buddha
Это не верно. Уже нырнул в исходный код, всё увидел, вот что документация говорит…
ок, я вот даже, якобы, рабочий пример нашел http://lbolla.info/blog/2013/01/22/blocking-tornado

В вашем пример ещё ошибка с перемешиванием потоков:
1) торнадо крутится в потоке №1, у него в event-loop свой список (fn_list) функций для вызова.
2) Когда вы вызываете ProcessPoolExecutor, он создает свой поток №2 (№3, №4 и др)
3) Когда ProcessPoolExecutor завершает работу он вызывает callback: future.add_done_callback(self.callback)
Но этот callback будет запущен в потоке №2, это не правильно, он может не только не выполнить свою работу, но и нагадить потоку №1.
Поэтому нужно запускать callback в потоке №1, это можно сделать поместив ф-ию в fn_list и поток №1 в event_loop сам вызовет эту ф-ю как только доберется до неё. Поместить в “fn_list” можно с помощью: IOLoop.instance().add_callback(callback)
Вот, примерно так, осталось разобраться что-б ProcessPoolExecutor не падал.

Но я бы его вообще не использовал бы, т.к. похоже он создает свои потоки, чем снижает общую производительность приложения. Что-б получить максимальную производительность, нужно использовать только 1 поток, и все вызовы должны быть не блокирующие.

Офлайн

#6 Апрель 4, 2014 12:32:05

o7412369815963
От:
Зарегистрирован: 2009-06-17
Сообщения: 1986
Репутация: +  32  -
Профиль   Отправить e-mail  

Помогите разобраться, сделать асинхронную обработку get-запроса на tornado.

buddha
В целом проблема в том, что я не могу понять абстракций tornado для написания асинхронного кода.
Пробуйте ещё упрощать приложений до минимума пока не станет понятно что где и почему, при этом логируя на каждом углу.

Офлайн

#7 Апрель 4, 2014 15:37:25

buddha
От:
Зарегистрирован: 2012-03-02
Сообщения: 422
Репутация: +  15  -
Профиль   Отправить e-mail  

Помогите разобраться, сделать асинхронную обработку get-запроса на tornado.

Как асинхронщика работает представление имею, писал примеры для клиента и сервера, основанные на select(). Вчера модель работы tornado смотрел тут http://www.slideshare.net/
За пример, спасибо. Но, как в нём и видно, он не соответствует tornado flow, по моему, хотя очень показательный!

Я то вот смотрел в код декораторов @asyncronous @gen.coroutine. Смотрел реализацию метода tornado.httpclient.AsyncHTTPClient.fetch(). Какой то блин колбэк колбэков.

Буду дальше страдать=( авось получиться разложить по полочкам. Хотя плохо это, когда дока не учит использовать внешнее API фреймворка.

Офлайн

Board footer

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

Powered by DjangoBB

Lo-Fi Version