Ускоряем Nginx за 5 минут

79833b13218bd4dba7669c980d239fe4

Попытайтесь повторить это сами

Как правило, настроенный должным образом сервер Nginx на Linux, может обрабатывать 500,000 — 600,000 запросов в секунду. Мне удалось довести этот показатель до 904,000 запросов в секунду. Хотел бы обратить внимание на тот факт, что настройки описанные ниже, применялись в тестовой среде и, возможно, для ваших боевых серверов они не подойдут.

Минутка банальности.

yum -y install nginx

 

На всякий пожарный, создадим бэкап исходного конфига.

cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.orig
vim /etc/nginx/nginx.conf

 

А теперь можно и похимичить!

Начнём с директивы worker_processes. Если Nginx выполняет работу нагружающую процессор (например SSL или gzipping), то оптимально установить эту директиву в значение, равное количеству ядер процессора. Выигрыш при большем значении вы получите только в случае обработки очень большого количества статики.

# This number should be, at maximum, the number of CPU cores on your system. 
worker_processes 24;

 

Также, директива worker_processes, умноженная на worker_connections из секции event, даст максимально возможное количество клиентов. Однако это количество не получится сделать сколь угодно большим, т.к. оно ограничено числом сокетов, доступных в системе (это порядка 64 * 103).

# Determines how many clients will be served by each worker process.
worker_connections 4000;

 

Последняя пролетарская директива, которую я хочу затронуть — это worker_rlimit_nofile. Данная директива указывает сколько файловых дескрипторов будет использовать Nginx. На каждое соединение надо выделять по два дексриптора, даже для статических файлов (картинки/JS/CSS): один для соединения с клиентом, а второй — для открытия статического файла. Таким образом, значение worker_rlimit_nofile должно быть равным удвоенному значению worker_connections. В системе это значение можно установить из командной строки ulimit -n 200000 или используя /etc/security/limits.conf.

# Number of file descriptors used for Nginx.
worker_rlimit_nofile 200000;

 

Теперь разберёмся с логированием. Во-первых, оставим логирование только критических ошибок.

# Only log critical errors.
error_log /var/log/nginx/error.log crit

 

Если вы совсем бесстрашны и хотите отключить логирование ошибок целиком, то помните, что error_log off вам не поможет. Вы просто получите весь лог в файле off. Для отключения логирования ошибок надо делать так:

# Fully disable log errors.
error_log /dev/null crit;

 

А вот логи доступа не так страшно отключить полностью.

# Disable access log altogether.
access_log off;

 

Или, хотя бы, включить буфер чтения / записи.

# Buffer log writes to speed up IO.
access_log /var/log/nginx/access.log main buffer=16k;

 

Для обработки подключений Nginx поддерживает ряд методов. Наиболее эффективным для Linux является метод epoll.

# The effective method, used on Linux 2.6+, optmized to serve many clients with each thread.
use epoll;

 

Для того, чтобы Nginx пытался принять максимальное количество подключений, необходимо включить директивуmulti_accept. Однако при слишком маленьком значении worker_connections, их лимит может быть очень быстро исчерпан.

# Accept as many connections as possible, after nginx gets notification about a new connection.
multi_accept on;

 

Конечно же, нам не обойтись без кеширования информации о:

  • дескрипторах недавно открытых файлов: их размера и даты модификации;
  • существовании директорий;
  • ошибках при поиске файлов: отсутствие самого файла, отсутствие прав на чтение и т.д.

Советую не копировать значения директив кеширования, а поиграть с ними, подобрав оптимальные для вашего окружения.

# Caches information about open FDs, freqently accessed files.
open_file_cache max=200000 inactive=20s; 
open_file_cache_valid 30s; 
open_file_cache_min_uses 2;
open_file_cache_errors on;

 

Директива sendfile активирует копирование данных между файловыми дескрипторами средствами ядра, что намного эффективнее связки read() + write(), которая требует обмена данными с пользовательским пространством.

# Sendfile copies data between one FD and other from within the kernel.
sendfile on;

 

После включения sendfile, можно заставить Nginx отправлять заголовки HTTP-ответов одним пакетом, а не отдельным частями.

# Causes nginx to attempt to send its HTTP response head in one packet,  instead of using partial frames.
tcp_nopush on;

 

Для keep-alive подключений можно выключить буферизацию (алгоритм Нейгла). Это будет полезно при частом запросе маленьких объёмов данных в режиме реального времени, без получения немедленного ответа, когда важна своевременная доставка данных. Классический пример — события наведения мышкой.

# Don't buffer data-sends (disable Nagle algorithm).
tcp_nodelay on;

 

Стоит обратить внимание на ещё две директивы для keep-alive подключений. Их назначение выглядит очевидным.

# Timeout for keep-alive connections. Server will close connections after this time.
keepalive_timeout 30;
# Number of requests a client can make over the keep-alive connection.
keepalive_requests 1000;

 

Чтобы высвободить дополнительную память, выделенную под сокеты, включите директиву reset_timedout_connection. Она разрешит серверу закрывать подключение тех клиентов, которые перестали отвечать.

# Allow the server to close the connection after a client stops responding. 
reset_timedout_connection on;

 

Ещё можно существенно уменьшить тайм-ауты для директив client_body_timeout и send_timeout (дефолтное значение обеих — 60 секунд). Первая — ограничивает время на чтение тела запроса от клиента. Вторая — время ответа клиенту. Таким образом, если клиент не начнёт читать данные в указанный промежуток времени, то Nginx закроет подключение.

# Send the client a "request timed out" if the body is not loaded by this time.
client_body_timeout 10;
# If the client stops reading data, free up the stale client connection after this much time.
send_timeout 2;

 

И, конечно же, сжатие данных. Плюс — единственный и очевидный: уменьшение размера пересылаемого трафика. Минус — единственный и очевидный: не работает для MSIE 6 и ниже.

# Compression.
gzip on;
gzip_min_length 10240;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
gzip_disable "MSIE [1-6]\.";

 

Пожалуй, это всё, о чём я хотел рассказать. Скажу лишь ещё раз, что не стоит копировать приведенные настройки один в один. Я советую применять их по одной, каждый раз запуская какую-нибудь утилиту для нагрузочного тестирования (например, Tsung). Это весьма важно для понимания, какие настройки реально ускоряют ваш веб-сервер. Методичность в тестировании сэкономит вам уйму времени.

P.S. Все настройки одним куском для бесстрашных лентяев

# This number should be, at maximum, the number of CPU cores on your system. 
worker_processes 24;

# Number of file descriptors used for Nginx.
worker_rlimit_nofile 200000;

# Only log critical errors.
error_log /var/log/nginx/error.log crit

events {

    # Determines how many clients will be served by each worker process.
    worker_connections 4000;

    # The effective method, used on Linux 2.6+, optmized to serve many clients with each thread.
    use epoll;

    # Accept as many connections as possible, after nginx gets notification about a new connection.
    multi_accept on;

}

http {

    # Caches information about open FDs, freqently accessed files.
    open_file_cache max=200000 inactive=20s; 
    open_file_cache_valid 30s; 
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    # Disable access log altogether.
    access_log off;

    # Sendfile copies data between one FD and other from within the kernel.
    sendfile on; 

    # Causes nginx to attempt to send its HTTP response head in one packet,  instead of using partial frames.
    tcp_nopush on;

    # Don't buffer data-sends (disable Nagle algorithm).
    tcp_nodelay on; 

    # Timeout for keep-alive connections. Server will close connections after this time.
    keepalive_timeout 30;

    # Number of requests a client can make over the keep-alive connection.
    keepalive_requests 1000;

    # Allow the server to close the connection after a client stops responding. 
    reset_timedout_connection on;

    # Send the client a "request timed out" if the body is not loaded by this time.
    client_body_timeout 10;

    # If the client stops reading data, free up the stale client connection after this much time.
    send_timeout 2;

    # Compression.
    gzip on;
    gzip_min_length 10240;
    gzip_proxied expired no-cache no-store private auth;
    gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
    gzip_disable "MSIE [1-6]\.";

}