Tornado - Введение в асинхронность
Primary tabs
Доброго времени суток, сегодня поговорим о Tornado - это асинхронный (неблокирующий) веб-фреймворк. О самих асинхронных серверах и об архитектуре в общем мы говорили тут.
Также об Async Web есть запись видеовстречи iff: https://www.youtube.com/watch?feature=pl...
Итак, Tornado.
Документация к фреймворку основанна на встроенной системе документации python. На момент написания статьи актуальной версией Tornado является 3.2, ну и конечно мы используем python3.
Из-за скудной документации приходится часто рыться в исходниках, чтобы понять как работать с данным чудом, поэтому я буду ждать вопросов от тех, кто выберет данный фреймворк и сервер для своих проектов. В этой статье, я постараюсь рассказать о самых основных проблемных вопросах в Tornado.
Для того чтобы было понятно тем, кто не знаком с Tornado, я приведу типичный код из документации, в котором будет создан самый простой http-сервер, обрабатывающий http запросы.
class MainHandler(RequestHandler): # класс-обработчик запроса, он будет обрабатывать поступивший url
def get(self): # переобределяем метод GET
self.write("ololo") # записываем в ответ "ololo"
# на этом обработка запроса завершается
class TestHandler(RequestHandler):
def initialize(self, database): # здест мы передаём при связке с url параметр
self.database = database # см. ниже
def get(self):
...
class HiamHandler(RequestHandler):
def post(self, username): # параметр в url присутсвует в параметрах функции обработки запроса
...
app_settings = { # здесь популярные настройки, не нужно обращать на них внимания
'autoreload': True,
'debug': True,
'gzip': False,
'serve_traceback': True, # True, if Debug
#'log_function': SOME_LOG_Function,
#'default_handler_class': SOME_VALUE,
#'default_handler_args': SOME_VALUE,
'cookie_secret': "DebuG)",
#'login_url': "SOME_LOGIN_URL",
'xsrf_cookies': True,
#'autoescape': None,
'compiled_template_cache': False, # False, if Debug
'template_path': "SOME PATH",
#'template_loader': SOME_VALUE,
'static_hash_cache': False, # False, if Debug
'static_path': "SOME PATH",
'static_url_prefix': "/static/",
}
if __name__ == "__main__": # далее код инициализации и запуска сервера
application = tornado.web.Application([ # создаём наше приложение - сервер
(r"/", MainHandler), # тут ставим в соответсвие класс-обработчик запроса и url
(r"/test/", TestHandler, dict(database=database)), # возможно также
# передавать дополнительный параметр
(r"/hwoiam/(.*)", HiamHandler), # параметры url определяются регулярным
# выражением как в других фреймворках
], **app_settings) # словарь с настройками
application.listen(8888) # указываем порт, который будем слушать
tornado.ioloop.IOLoop.instance().start() # запускаем основную петлю задач,
# что необходимо при асинхроной архитектуре
Это почти стандартный скелет для реализации собственного http сервера, более подробно смотрите документацию, для самых элементарных вещей она достаточно понятна: http://www.tornadoweb.org/en/stable/docu...
--- Грабля 1: Первое что нужно понять, что тут почти всё надо делать ручками. Для того чтобы использовать встроенную систему аутентификации нужно переопределить функцию get_current_user,а для защиты от XSRF достаточно следовать инструкциям, но у меня не всегда работала их система с JSON поэтому я переопределил функцию проверки так:
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self): # реализуем свою систему аутентификации
#return None
def check_xsrf_cookie(self): # эта переопределённая функция проверки xsrf токена
token = (self.get_argument("_xsrf", None) or
self.request.headers.get("X-Xsrftoken") or
self.request.headers.get("X-Csrftoken"))
if not token:
raise tornado.web.HTTPError(403, "'_xsrf' argument missing from POST")
else:
try:
token = tornado.escape.json_decode(token)
except ValueError:
token = token
if self.xsrf_token != token:
raise tornado.web.HTTPError(403, "XSRF cookie does not match POST argument")
После, для тех обработчиков для которых нужна аутентификация и проверка xsrf токена я применял наследование от BaseHandler.
---Грабля 2, она же главная. Это работа с асинхронностью, собственно понимание вопросав, связанных с асинхронной архитектурой и необходимо чтобы дальше продолжить читать материал. А проблема с пониманием того, как с помощью Tornado использовать фикши асинхронной архитектуры. Это будет цикл отдельных заметок, но сегодня я расскажу о самом простом способе - это переадресация задачи на другой сервер))) об этом говорилось на встрече iff. посвящённой асинхроному вебу. Почему так? - Это самый простой способ работы с блокирующими вызовами, и он прекрасно реализован в фраемворке.
Итак:
- К нам приходит запрос
- Что-то делаем, блокирующие вызовы и длительные вычисления реализовываем на другом сервере
- Делаем средствами фреймворка асинхронный запрос
Пример (прям из документации):
class GenAsyncHandler(RequestHandler):
@gen.coroutine # с помощью декоратора объявляем
# метод асинхронным
def get(self):
http_client = AsyncHTTPClient() # создаём асинхронный клиент
response = yield http_client.fetch("http://example.com") # делаем неблокирующий запрос
#после этого обработка запроса прерывается и выполняются
#другие задачи из очереди
#когда сервер, выполняющий наши долгие вычисления или обращение к диску
#ответит, и торнадо получит результат
#обработка продолжится в порядке очереди
do_something_with_response(response)
self.render("template.html") # завершаем обработку
Итак, теперь в принципе понятна архитектура Tornado и далее поговорим о том, как создавать собственные асинхронные обработчики средствами Tornado. Но это потом....
- Log in to post comments
- 9745 reads