Использование Nginx+Tornado для приложений на Django
Не так давно был анонсирован новый веб-сервер Tornado, написанный на Python, отличительной чертой которого являлась скорость работы и то что это не просто веб-сервер, а целый фреймворк.
Впервые я с ним познакомился на хабре, а вскоре после этого прочел заметку Django + Tornado и решил попробовать торнадо в действии!
На самом деле конфигурация "Nginx+Tornado+Django" позаимствована у заметки, ссылку на которую я привел выше, однако у меня та конфигурация не заработала, поэтому я выкладываю для себя и друзей свой конфиг.
Установка
Устанавливаем Tornado:
cd /usr/ports/www/py-tornado make install clean
Устанавливаем Nginx, если он у вас не установлен:
cd /usr/ports/www/nginx-devel make install clean
Настройка
Теперь настроим Nginx для проксирования на Tornado:
server { listen 80; server_name liburg.ru; location ^~ /admin-media { alias /usr/local/lib/python2.6/site-packages/django/contrib/admin/media; } location = /robots.txt { root /www/liburg/static/; } location ~* \.(jpg|jpeg|gif|png|ico|css|zip|js|swf)$ { root /www/liburg/static/; expires 7d; } location / { proxy_pass proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
В корень проекта (рядом с manage.py, settings.py и т.д.) кладем файл "tornading.py":
#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import signal import fcntl import time from subprocess import Popen import logging import logging.handlers import tornado.httpserver import tornado.ioloop import tornado.wsgi PORT = 8001 HOST = 'liburg.ru' # видно в top, htop, ps, etc LOG_FILE = '/var/log/tornado.log' # '' for not write log LOG_LEVEL = 'INFO' # INFO (все статусы), WARNING (>=404), ERROR (>=500) # настраиваем Джанго sys.path.insert(0, '/'.join(os.path.dirname(__file__).split('/')[:-1])) os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' import django.core.handlers.wsgi application = django.core.handlers.wsgi.WSGIHandler() # если сайт не на Джанго, просто импортируем wsgi-объект как aplication #~ from wsgi_app import application # настраиваем логирование в файл os.chdir(os.path.dirname(os.path.abspath(__file__))) if LOG_FILE: try: os.makedirs(os.path.split(LOG_FILE)[0]) except OSError: pass file_handler = logging.handlers.RotatingFileHandler( filename = LOG_FILE, mode='a+', # имя файла maxBytes = 1000000, # максимально байт в файле backupCount = 2) # максимум файлов file_handler.setLevel(getattr(logging, LOG_LEVEL)) file_handler.setFormatter( logging.Formatter('%(asctime)s\t%(levelname)-8s %(message)s', datefmt = '%d-%m-%Y %H:%M:%S')) logging.getLogger('').setLevel(logging.NOTSET) logging.getLogger('').addHandler(file_handler) # блокируемый файл для проверки активности сервера PID_FNAME = '/tmp/' + '_'.join((os.path.abspath(__file__).strip('/').split('/'))) + '.pid' COMMANDS = ['start', 'stop', 'restart'] def daemon(): logging.critical('--- SERVER (RE)STARTED') f = open(PID_FNAME, 'w') fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) f.write('%-12i' % os.getpid()) f.flush() container = tornado.wsgi.WSGIContainer(application) http_server = tornado.httpserver.HTTPServer(container) http_server.listen(PORT) tornado.ioloop.IOLoop.instance().start() def start(): started = alegry_started() if not started: pid = Popen([HOST, os.path.abspath(__file__), 'daemon'], executable='python').pid print 'Server started at port %s (pid: %i)...' % (PORT, pid) else: print 'Server alegry started (pid: %i)' % started def stop(): started = alegry_started() if started: os.kill(started, signal.SIGKILL) print 'Server stoped (pid %i)' % started else: print 'Server not started' def restart(): stop() time.sleep(1) start() def alegry_started(): ''' Если сервер запущен, возвращает pid, иначе 0 ''' if not os.path.exists(PID_FNAME): f = open(PID_FNAME, "w") f.write('0') f.flush() f.close() f = open(PID_FNAME, 'r+') try: fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: started = int(f.read()) else: started = 0 f.close() return started if len(sys.argv) == 2 and sys.argv[1] in (COMMANDS + ['daemon']): cmd = sys.argv[1] globals()[cmd]() else: print 'Error: invalid command' print 'Usage: python tornading.py {%s}.' % '|'.join(COMMANDS)
Стартуем Nginx и Tornado:
/usr/local/etc/rc.d/nginx start cd /www/liburg/ && python tornading.py start
На случай падения Tornado - добавьте в крон задание на запуск сервера:
* * * * * cd /www/liburg/ && /usr/local/bin/python tornading.py start
PS. Проект Либург.ру работает на Tornado+Django

Комментарии
Ложим = кладем.
Исправьте пожалуйста.
Спасибо, поправил
Я че-то не понял, а можно что-то на нем, кроме питона запустить?
Теоретически можно, но я не проверял... А для чего хотите использовать?
Спасибо за заметку.
Но вопрос, настораживает "на случай падения". Это как же? Он настолько нестабилен? По крону start'ить его все время как-то аж коробит от такой идеи.
Ну, у меня парой падает, также как и mysql, sphinx. А то что коробит - согласен, если есть время я разбираюсь с проблемами падения, а текущее решение всего лишь "горячее" :)
А по какой причине падает, не разбирались? Ну, там segmentation fault или что нибудь не такое страшное?
Не, не разбирался, еще времени не нашел...
а как добавить скрипт торнадинг.пу в автозапуск при старте? rc.d пишет ошибку!!!
http://www.freebsd.org/doc/handbook/configtuning-starting-services.html
2 Viacheslav
Как же вы достали, зануды ебанные. Без тебя мудака знаем, как правильно писать. Вечно найдется уебан, который в коментах заявит о своей грамотности. Тьфу!
Почему не использовать Supervisord для демонизации tornado?
Действительно, почему бы и нет ;)
FreeBSD. Установка и настройка supervisor
Оставьте свой комментарий