15 ноября 2009 г. Django Nginx Python Tornado

Использование Nginx+Tornado для приложений на Django

tornado

Не так давно был анонсирован новый веб-сервер 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 http://127.0.0.1:8001/;
        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

upd: Документация к веб-серверу Tornado

Комментарии

Ложим = кладем.
Исправьте пожалуйста.

Спасибо, поправил

Я че-то не понял, а можно что-то на нем, кроме питона запустить?

Теоретически можно, но я не проверял... А для чего хотите использовать?

Александр 18 ноября 2009 г. 17:00

Спасибо за заметку.
Но вопрос, настораживает "на случай падения". Это как же? Он настолько нестабилен? По крону start'ить его все время как-то аж коробит от такой идеи.

Ну, у меня парой падает, также как и mysql, sphinx. А то что коробит - согласен, если есть время я разбираюсь с проблемами падения, а текущее решение всего лишь "горячее" :)

Александр 18 ноября 2009 г. 23:32

А по какой причине падает, не разбирались? Ну, там segmentation fault или что нибудь не такое страшное?

Не, не разбирался, еще времени не нашел...

а как добавить скрипт торнадинг.пу в автозапуск при старте? rc.d пишет ошибку!!!

http://www.freebsd.org/doc/handbook/configtuning-starting-services.html

2 Viacheslav
Как же вы достали, зануды ебанные. Без тебя мудака знаем, как правильно писать. Вечно найдется уебан, который в коментах заявит о своей грамотности. Тьфу!

Владимир 13 июня 2012 г. 20:51

Почему не использовать Supervisord для демонизации tornado?

Действительно, почему бы и нет ;)

Оставьте свой комментарий

Markdown