Доброго времени суток, господа.
Сегодня я хочу поговорить о своём опыте освоения асинхронного сервера Tornado, написанного на языке Python. Этот пост не будет подробным мануалом, но несколько основных моментов я покажу. Фреймворк Tornado заинтересовал меня прежде всего своими результатами в тесте, опубликованном на официальнои сайте проекта. Заинтересовавшись, я нашёл ещё вот этот и этот тесты. В них Торнадо показал себя достаточно неплохо. Ну и то, что Торнадо используется (и разработан) командой FriendFeed, тоже кое о чём говорит. Как оказалось, фреймворк так же имеет достаточно удобную структуру. Итак, начнём.
Получить исходный код Торнадо можно из репозитория на GitHub. Распаковываем архив и делаем:
Торнадо так же можно поставить через easy_install:
При этом важно чтобы были установлены пакеты python-dev, python-pycurl и python-simplejson. Будем считать что с установкой вы справились и продолжим дальше.
Мы не будем делать что-то более сложное чем «хелловорлд», но ключевые моменты я постараюсь передать. Каждое приложение на Торнадо, как правило, состоит из, собственно, класса приложения и классов хэндлеров (обработчиков) запросов. Класс простого приложения содержит в себе привязку хэндлеров к путям приложения и ряд настроек. Мы же можем определить в нём, например, подключение к базе данных, чтобы не создавать его при обработке каждого запроса, и затем передать его в классы хендлеров. Для этого мы объявим некий базовый хендлер и будем наследовать от него остальные.
Так как приложение на Торнадо, это, по сути, сервер, он может запускаться с разнообразными параметрами, определёнными разработчиками. Для упрощения их получения используется модуль Торнадо options.
Класс приложения наследуется от класса tornado.web.Application. Он может выглядеть например так:
Здесь в словаре настроек присутствуют пути до каталогов с шаблонами и статикой (контент, который не изменяется, например графика или файлы таблиц стилей). Они необходиы для движка шаблонов фреймворка и для статической отдачи файлов. Параметр xsrf_cookies включает генерацию токенов для форм для защиты XSRF. cookie_secret, как несложно догадаться, строка, используемая для генерации cookies. Здесь мы покачто не воспользуемся этим функционалом, но в дальнейших постах я опишу эти вещи. Следующая после настроек строка инициализирует класс приложения с обозначенными выше параметрами. Дальше я объявил подключение к базе данных MongoDB, чуть ниже мы передадим его в хендлеры.
Хендлеры это классы, обрабатывающие входящие запросы в соответствии с правилами роутинга для приложения. Думаю, вы знаете что такое роутинг, так что объяснять не буду. В общем виде класс хендлера выглядит вот так:
Этот класс будет обрабатывать GET запросы, пришедшие на корневой адрес, то есть на sitename.org. В данном случае мы просто возвращаем строку «Hello, world!». А вот так может выглядеть наследование хэндлеров:
Мы объявляем класс базового хэндлера и проксируем в нём инициализированное выше подключение к базе данных. Затем объявляем корневой контроллер, наследуемый от базового и выводим по запросу Id первой записи в коллекции. Вы же слышали о MongoDB, правда?
Теперь осталось написать функцию main, которая получит управление после запуска сервера и инициализирует его.
Мы парсим командную строку на наличие переданных параметров (например, номер порта, который равен 8888 по умолчанию), создаём сервер, обслуживающий наше приложение и стартуем его.
Вот полный код нашего приложения, благо он небольшой по объёму:
define определяет аргументы, передаваемые серверу и устанавливает их дефолтные значения, а так же строку подсказки. Неиспользуемые настройки я опустил за ненадобностью. Вот, собственно, и всё для начала, надеюсь, кто-то тоже заинтересуется Торнадо.
Сегодня я хочу поговорить о своём опыте освоения асинхронного сервера 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=8888, help="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 определяет аргументы, передаваемые серверу и устанавливает их дефолтные значения, а так же строку подсказки. Неиспользуемые настройки я опустил за ненадобностью. Вот, собственно, и всё для начала, надеюсь, кто-то тоже заинтересуется Торнадо.