Pull to refresh

Введение в Tornado

Доброго времени суток, господа.
Сегодня я хочу поговорить о своём опыте освоения асинхронного сервера Tornado, написанного на языке Python. Этот пост не будет подробным мануалом, но несколько основных моментов я покажу. Фреймворк Tornado заинтересовал меня прежде всего своими результатами в тесте, опубликованном на официальнои сайте проекта. Заинтересовавшись, я нашёл ещё вот этот и этот тесты. В них Торнадо показал себя достаточно неплохо. Ну и то, что Торнадо используется (и разработан) командой FriendFeed, тоже кое о чём говорит. Как оказалось, фреймворк так же имеет достаточно удобную структуру. Итак, начнём.

Установка

Получить исходный код Торнадо можно из репозитория на GitHub. Распаковываем архив и делаем:
python setup.py build
sudo python setup.py install

Торнадо так же можно поставить через easy_install:
sudo easy_install tornado

При этом важно чтобы были установлены пакеты python-dev, python-pycurl и python-simplejson. Будем считать что с установкой вы справились и продолжим дальше.

Структура приложения

Мы не будем делать что-то более сложное чем «хелловорлд», но ключевые моменты я постараюсь передать. Каждое приложение на Торнадо, как правило, состоит из, собственно, класса приложения и классов хэндлеров (обработчиков) запросов. Класс простого приложения содержит в себе привязку хэндлеров к путям приложения и ряд настроек. Мы же можем определить в нём, например, подключение к базе данных, чтобы не создавать его при обработке каждого запроса, и затем передать его в классы хендлеров. Для этого мы объявим некий базовый хендлер и будем наследовать от него остальные.

Так как приложение на Торнадо, это, по сути, сервер, он может запускаться с разнообразными параметрами, определёнными разработчиками. Для упрощения их получения используется модуль Торнадо options.

Класс приложения наследуется от класса tornado.web.Application. Он может выглядеть например так:
class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r"/", RootHandler),
        ]
        settings = dict(
            template_path=os.path.join(os.path.dirname(__file__)"templates"),
            static_path=os.path.join(os.path.dirname(__file__)"static"),
            xsrf_cookies=True,
            cookie_secret="11oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
        )
        tornado.web.Application.__init__(self, handlers, **settings)
 
        # MongoDB
        self.connection = pymongo.Connection(MONGODB_HOST, MONGODB_PORT)
        self.db = self.connection["tornado"]

Здесь в словаре настроек присутствуют пути до каталогов с шаблонами и статикой (контент, который не изменяется, например графика или файлы таблиц стилей). Они необходиы для движка шаблонов фреймворка и для статической отдачи файлов. Параметр xsrf_cookies включает генерацию токенов для форм для защиты XSRF. cookie_secret, как несложно догадаться, строка, используемая для генерации cookies. Здесь мы покачто не воспользуемся этим функционалом, но в дальнейших постах я опишу эти вещи. Следующая после настроек строка инициализирует класс приложения с обозначенными выше параметрами. Дальше я объявил подключение к базе данных MongoDB, чуть ниже мы передадим его в хендлеры.

Хендлеры

Хендлеры это классы, обрабатывающие входящие запросы в соответствии с правилами роутинга для приложения. Думаю, вы знаете что такое роутинг, так что объяснять не буду. В общем виде класс хендлера выглядит вот так:
class RootHandler(tornado.web.RequestHandler):
    def get(self):
# do something here
        self.write("Hello, world")

Этот класс будет обрабатывать GET запросы, пришедшие на корневой адрес, то есть на sitename.org. В данном случае мы просто возвращаем строку «Hello, world!». А вот так может выглядеть наследование хэндлеров:
class BaseHandler(tornado.web.RequestHandler):
    @property
    def db(self):
        return self.application.db
 
class RootHandler(BaseHandler):
    def get(self):
        result = list(self.db.contents.find({}, limit=1))[0]["_id"]
        self.write(str(result))

Мы объявляем класс базового хэндлера и проксируем в нём инициализированное выше подключение к базе данных. Затем объявляем корневой контроллер, наследуемый от базового и выводим по запросу Id первой записи в коллекции. Вы же слышали о MongoDB, правда?

Теперь осталось написать функцию main, которая получит управление после запуска сервера и инициализирует его.
def main():
    tornado.options.parse_command_line()
    http_server = tornado.httpserver.HTTPServer(Application())
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()
 
 
if __name__ == "__main__":
    main()

Мы парсим командную строку на наличие переданных параметров (например, номер порта, который равен 8888 по умолчанию), создаём сервер, обслуживающий наше приложение и стартуем его.

Вот полный код нашего приложения, благо он небольшой по объёму:
# -*- coding: utf-8 -*-
 
import os
 
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
 
from tornado.options import define, options
 
import pymongo
 
MONGODB_HOST = "192.168.1.2"
MONGODB_PORT = 27017
 
define("port", default=8888help="run on the given port"type=int)
 
class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r"/", RootHandler),
        ]
        settings = dict()
        tornado.web.Application.__init__(self, handlers, **settings)
 
        # MongoDB
        self.connection = pymongo.Connection(MONGODB_HOST, MONGODB_PORT)
        self.db = self.connection["mallertv"]
 
class BaseHandler(tornado.web.RequestHandler):
    @property
    def db(self):
        return self.application.db
 
class RootHandler(BaseHandler):
    def get(self):
        result = list(self.db.contents.find({}, limit=1))[0]["_id"]
        self.write(str(result))
 
 
def main():
    tornado.options.parse_command_line()
    http_server = tornado.httpserver.HTTPServer(Application())
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()
 
 
if __name__ == "__main__":
    main()

define определяет аргументы, передаваемые серверу и устанавливает их дефолтные значения, а так же строку подсказки. Неиспользуемые настройки я опустил за ненадобностью. Вот, собственно, и всё для начала, надеюсь, кто-то тоже заинтересуется Торнадо.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.