Использование nginx для кэширования статического контента на frontend-сервере

()

Как известно веб-проекты с большой посещаемостью "живут" не на одном сервера, а на целых комплексах серверов, которые порой имеют весьма сложную структуру и в любом случае являются "штучными" решениями, "заточенными" под задачи конкретного проекта. Однако некоторые общие черты и заложенные идеи в большинстве случае можно проследить.

Автор этих строк принимал участие в разработке и развёртывании комплекса серверов для большого веб-проект (одним из разработчиков которого он является) и далее вниманию читателей предлагается конфигурация веб-сервера nginx с одного из серверов проекта, которую можно в ряде случаев рекомендовать другим проектам, которые "переросли" простые и популярные конфигурации.

Немного подробнее о комплексе серверов: Схема стоит из трёх серверов: front, back1 и back2. front имеет два сетевых интерфейса. Один смотрит в интернет, а второй - на локальную сеть, в которой находятс back1 и back2. Это иллюстрирует схема:

Схема комплекса сереров

В реальности всё немного сложнее, как минимум есть ещё сервер базы данных и ещё кое-какие вспомогательные сервера, но рассмотрение этих серверов выходит за рамки данной статьи.

Основная задача фронтенда (сервер front) пересылать запросы на бэкенды (back1 и back2) разделяя нагрузку поровну, а в случае сбоя одного из бэкендов перенести всю нагрузку на оставшийся.

Фронтенд работает под управлением операционной системы FreeBSD 8.1 и на нём запущена тестовая версия nginx (в портах FreeBSD доступна как www/nginx-devel). Nginx собран со следующими опциями:

WITHOUT_DEBUG=true
WITHOUT_DEBUGLOG=true
WITHOUT_FILE_AIO=true
WITHOUT_IPV6=true
WITHOUT_GOOGLE_PERFTOOLS=true
WITH_HTTP_MODULE=true
WITHOUT_HTTP_ADDITION_MODULE=true
WITH_HTTP_CACHE_MODULE=true
WITHOUT_HTTP_DAV_MODULE=true
WITHOUT_HTTP_FLV_MODULE=true
WITHOUT_HTTP_GEOIP_MODULE=true
WITHOUT_HTTP_GZIP_STATIC_MODULE=true
WITHOUT_HTTP_IMAGE_FILTER_MODULE=true
WITHOUT_HTTP_PERL_MODULE=true
WITHOUT_HTTP_RANDOM_INDEX_MODULE=true
WITHOUT_HTTP_REALIP_MODULE=true
WITH_HTTP_REWRITE_MODULE=true
WITHOUT_HTTP_SECURE_LINK_MODULE=true
WITHOUT_HTTP_SSL_MODULE=true
WITH_HTTP_STATUS_MODULE=true
WITHOUT_HTTP_SUB_MODULE=true
WITHOUT_HTTP_XSLT_MODULE=true
WITHOUT_MAIL_MODULE=true
WITHOUT_MAIL_IMAP_MODULE=true
WITHOUT_MAIL_POP3_MODULE=true
WITHOUT_MAIL_SMTP_MODULE=true
WITHOUT_MAIL_SSL_MODULE=true
WITH_WWW=true
WITHOUT_CACHE_PURGE_MODULE=true
WITHOUT_ECHO_MODULE=true
WITHOUT_HEADERS_MORE_MODULE=true
WITHOUT_HTTP_ACCEPT_LANGUAGE=true
WITHOUT_HTTP_ACCESSKEY_MODULE=true
WITHOUT_HTTP_AUTH_PAM_MODULE=true
WITHOUT_HTTP_AUTH_REQ_MODULE=true
WITHOUT_HTTP_EVAL_MODULE=true
WITHOUT_HTTP_FANCYINDEX_MODULE=true
WITHOUT_HTTP_GUNZIP_FILTER=true
WITHOUT_HTTP_MOGILEFS_MODULE=true
WITHOUT_HTTP_MP4_H264_MODULE=true
WITHOUT_HTTP_NOTICE_MODULE=true
WITHOUT_HTTP_PUSH_MODULE=true
WITHOUT_HTTP_REDIS_MODULE=true
WITHOUT_HTTP_RESPONSE_MODULE=true
WITHOUT_HTTP_UPLOAD_MODULE=true
WITHOUT_HTTP_UPLOAD_PROGRESS=true
WITHOUT_HTTP_UPSTREAM_FAIR=true
WITHOUT_HTTP_UPSTREAM_HASH=true
WITHOUT_HTTP_UPSTREAM_KEEPALIVE=true
WITHOUT_HTTP_ZIP_MODULE=true
WITHOUT_MEMC_MODULE=true
WITHOUT_SLOWFS_CACHE_MODULE=true
WITHOUT_SUPERVISORD_MODULE=true
WITHOUT_UDPLOG_MODULE=true

Приведём сразу готовую конфигурацию nginx (файл /usr/local/etc/nginx/nginx.conf) опустив специфичные для проекта моменты:

# Количество форков основого процесса. Рекомендуется по одному форку на каждое процессорное ядрое
worker_processes  4;

# Для обработки очереди используем механизм kqueue
# На каждый форк устанавливаем ограничение в 1024 tcp-соединения
events {
    worker_connections  1024;
    use kqueue;
}


http {
    # Не будем включать в http-заголовки информацию о сервере. Это немного повысит производительность.
    server_tokens off;
    # Описание MIME-типов
    include       mime.types;
    # Неизвестные документы получат этот MIME-тип
    default_type  application/octet-stream;

    # Без нужды в логи мусорить не будем
    access_log /dev/null;

    # Тонкие настройки обработки запросов
    sendfile           on;
    aio                off;
    tcp_nodelay        on;
    tcp_nopush         on;
    
    # Именно столько и не больше будут жить устойчивые соединения
    # Слишком большое значение приведёт к большому количеству полуоткрытых сокетов
    # Слишком маленькое - не позволит клиентам насладиться всеми прелестями HTTP/1.1
    keepalive_timeout  3;
    
    # Настройки проксирования
    client_body_buffer_size     128K;
    client_header_buffer_size   128K;
    client_max_body_size          1M;
    large_client_header_buffers 1 1k;
    proxy_temp_path    /usr/home/nginx/proxy_temp;
    proxy_cache_path   /usr/home/nginx/proxy_cache  levels=1:2   keys_zone=one:30m max_size=1g;
    client_body_temp_path /usr/home/nginx/client_body_temp;
    fastcgi_temp_path /usr/home/nginx/fastcgi_temp;
    proxy_pass_header  Cookie;
    proxy_pass_header  Set-Cookie;
    proxy_cache_key    $scheme$proxy_host$request_uri$http_cookie$http_set-cookie;
    
    # Сжатие позволит отдавать контент клиентам быстрее
    gzip on;
    gzip_types application/x-javascript application/javascript text/css text/plain application/xml application/rss+xml text/vnd.wap.wml;
    gzip_vary on;
    gzip_comp_level 9;


    # Описание апстрима - место, куда мы будем пересылать http-запросы
    # Количество бэков может быть любым
    # Параметр weight описывает приоритет бэка
    # Параметр max_fails описывает количество сбоев подряд прежде чем бэк считаемся мёртвым
    # Параметр fail_timeout указывает время, через которое можно попробовать снова использовать мёртвые бэки
    upstream project_backends {
        server back1 weight=10 max_fails=3 fail_timeout=30s;
        server back2 weight=10 max_fails=3 fail_timeout=30s;
    }
    
    # Собственно описание сервера
    server {
        # Слушаем 80-й порт
        listen *:80;
        
        # Имя севера
        server_name front;
        # При пересылке запросов не будем менять значение заголовка Host
        server_name_in_redirect  off;
        
        # Имя файла журнала
        access_log /usr/home/logs/nginx-access.log;
        
        # Все запросы
        location / {
            # Пересылаем запросы на бэкенды
            proxy_pass         http://project_backends;
            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;
            client_max_body_size       10m;
            client_body_buffer_size    128k;
            proxy_connect_timeout      30;
            proxy_send_timeout         90;
            proxy_read_timeout         90;
            proxy_buffer_size          8k;
            proxy_buffers              4 32k;
            proxy_busy_buffers_size    64k;
            proxy_temp_file_write_size 10m;
        }
        # Запросы к статике
        location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico)$ {
            # Принудительно указываем заголовок Expire
            expires            30d;
            # Запросы по прежнему пересылаем на бэкенды
            proxy_pass         http://project_backends;
            # Однако здесь мы их кэшируем на один час
            # Это позволяет немного разгрузить бэкенды
            proxy_cache_valid  60m;
            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;
            proxy_cache_key    $scheme$proxy_host$request_uri$host;
            proxy_cache one;
        }
    }
}

Основная изюминка этой конфигурации - кэширование статического контента на фронтенде, что позволяет с одной стороны ускорить обработку запросов к статическому контенту, а с другой - немного разгрузить бэкенды. Статистика за один час работы всего комплекса выглядит так:

Сервер: front back1 back2
Количестно HTTP-запросов: 1010426 149716 149566

Видно что количество запросов, пересылаемых бэкендам сократилось на 70%! Не менее интересны графики трафика на сетевых интерфейсах. Трафик на внешнем интерфейсе фронтенда:

Трафик NIC1 на front

В то время как трафик на втором интерфейсе фронтенда:

Трафик NIC2 на front

То есть отдавая клиентам поток до девяти мегабайт в секунду фронтенд сам создаёт поток трафика с бэкендов не более четырёх мегабайт в секунду. Можно говорить о том что бэкенды избавлены от солидной части работы по обработке статического контента и могут спокойно заниматься обработкой динамического контента:)

На этом всё. Приятной работы!

Ключевые слова: freebsd, nginx, frontend, cache, кэш.

Комментарии:

Anonymous 2010-11-29 21:49:09 (#)

Вы наверное имели в виду мегабит в секунду? Это хорошо видно на графиках :)

MooSE 2010-11-29 22:07:26 (#)

Цитата:

Вы наверное имели в виду мегабит в секунду? Это хорошо видно на графиках :)


На графиках именно мегабайты. Проект весьма и весьма не маленький.

Anonymous 2010-11-29 22:18:53 (#)

А почему написано bits,

MooSE 2010-11-30 11:03:34 (#)

Цитата:

А почему написано bits,

Кривая настройка MRTG

Anonymous 2011-04-03 21:08:23 (#)

А что есть синий, и что есть зеленый цвет на графике? Никак не пойму(

MooSE 2011-04-04 09:27:34 (#)

Цитата:

А что есть синий, и что есть зеленый цвет на графике? Никак не пойму(

Это стандартный график MRTG. Синий - исходщяий трафик. Зелёный - входящий.
Новый комментарий



© 2006-2016 Вадим Калинников aka MooSE

Стоимость монтажа труб Рехау