Соединения, сессии, запросы, логирование#

Механизмы обработки соединений#

Angie поддерживает различные методы обработки соединений. Доступность конкретного метода зависит от используемой платформы. На платформах, поддерживающих несколько методов, Angie обычно автоматически выбирает наиболее эффективный метод. Однако, при необходимости, метод обработки соединений можно явно выбрать с помощью директивы use.

Доступны следующие методы обработки соединений:

Метод

Описание

select

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

poll

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

kqueue

Эффективный метод, доступный на FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 и macOS.

epoll

Эффективный метод, доступный на Linux 2.6+.

/dev/poll

Эффективный метод, доступный на Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+ и Tru64 UNIX 5.1A+.

eventport

Метод event ports доступен на Solaris 10+. (Из-за известных проблем рекомендуется использовать метод /dev/poll.)

Обработка HTTP-сессий#

Каждый HTTP-запрос проходит через ряд фаз, на каждой из которых выполняется определенный тип обработки.

Post-read

Начальная фаза. Модуль RealIP вызывается на этой фазе.

Server-rewrite

Фаза, на которой обрабатываются директивы из модуля Rewrite, определенные в блоке server (но вне блока location).

Find-config

Специальная фаза, на которой выбирается location на основе URI запроса.

Rewrite

Похожа на фазу Server-rewrite, но применяется к правилам rewrite, определенным в блоке location, выбранном на предыдущей фазе.

Post-rewrite

Специальная фаза, на которой запрос перенаправляется в новое место, как на фазе Find-config, если его URI был изменен во время фазы Rewrite.

Preaccess

На этой фазе стандартные модули Angie, такие как Limit Req, регистрируют свои обработчики.

Access

Фаза, на которой проверяется право клиента на выполнение запроса, обычно с помощью стандартных модулей Angie, таких как Auth Basic.

Post-access

Специальная фаза, на которой обрабатывается директива satisfy any.

Precontent

На этой фазе стандартные модули, такие как директивы try_files и mirror, регистрируют свои обработчики.

Content

Фаза, на которой обычно генерируется ответ. На этом этапе несколько стандартных модулей Angie регистрируют свои обработчики, включая Index. Обработчики вызываются последовательно, пока один из них не произведет вывод.

Log

Финальная фаза, на которой выполняется логирование запросов. В настоящее время только модуль Log регистрирует свой обработчик на этом этапе для ведения журнала доступа.

Обработка TCP/UDP-сессий#

TCP/UDP-сессия от клиента проходит через ряд фаз, на каждой из которых выполняется определенный тип обработки:

Post-accept

Начальная фаза после принятия соединения от клиента. На этой фазе вызывается модуль RealIP.

Pre-access

Предварительная фаза для проверки доступа. Модули Set вызываются на этой фазе.

Access

Фаза для ограничения доступа клиента перед фактической обработкой данных. На этом этапе вызывается модуль Access.

SSL

Фаза, на которой происходит завершение TLS/SSL. На этой фазе вызывается модуль SSL.

Preread

Фаза для чтения начальных байт данных в preread buffer, чтобы позволить таким модулям, как SSL Preread, проанализировать данные до их обработки.

Content

Обязательная фаза, на которой данные фактически обрабатываются, обычно с участием модуля Return, который отправляет ответ клиенту.

Log

Финальная фаза, на которой фиксируется результат обработки сессии клиента. На этой фазе вызывается модуль Log.

Обработка запросов#

Выбор виртуального сервера#

Изначально соединение создается в контексте сервера по умолчанию. Имя сервера может быть определено на следующих этапах обработки запроса, каждый из которых участвует в выборе конфигурации сервера:

  • Во время SSL-рукопожатия, заранее, в соответствии с SNI.

  • После обработки строки запроса.

  • После обработки поля заголовка Host.

Если имя сервера не определено после обработки строки запроса или поля заголовка Host, Angie использует пустое имя в качестве имени сервера.

На каждом из этих этапов могут применяться различные конфигурации сервера. Поэтому некоторые директивы следует указывать с осторожностью:

  • В случае директивы ssl_protocols список протоколов устанавливается библиотекой OpenSSL до применения конфигурации сервера в соответствии с именем, запрошенным через SNI. В результате протоколы следует указывать только для сервера по умолчанию.

  • Директивы client_header_buffer_size и merge_slashes применяются до чтения строки запроса. Поэтому эти директивы используют либо конфигурацию сервера по умолчанию, либо конфигурацию сервера, выбранную через SNI.

  • В случае директив ignore_invalid_headers, large_client_header_buffers и underscores_in_headers, которые участвуют в обработке полей заголовков запроса, конфигурация сервера дополнительно зависит от того, была ли она обновлена в соответствии со строкой запроса или полем заголовка Host.

  • Ответ с ошибкой обрабатывается с использованием директивы error_page на сервере, который в данный момент обрабатывает запрос.

Виртуальные серверы на основе имен#

Сначала Angie определяет, какой сервер должен обрабатывать запрос. Рассмотрим простую конфигурацию, где все три виртуальных сервера слушают на порту 80:

server {

    listen 80;
    server_name example.org www.example.org;
    # ...
}

server {

    listen 80;
    server_name example.net www.example.net;
    #  ...
}

server {

    listen 80;
    server_name example.com www.example.com;
    #  ...
}

В этой конфигурации Angie определяет, какой сервер должен обработать запрос, основываясь исключительно на поле заголовка Host. Если значение этого заголовка не совпадает ни с одним из имен серверов или если запрос не содержит этого заголовка, Angie направит запрос к серверу по умолчанию для этого порта. В приведенной выше конфигурации сервером по умолчанию является первый — что является стандартным поведением Angie. Также можно явно указать, какой сервер должен быть сервером по умолчанию, используя параметр default_server в директиве listen:

server {

    listen 80 default_server;
    server_name example.net www.example.net;
    #  ...
}

Примечание

Обратите внимание, что сервер по умолчанию является свойством слушающего сокета, а не имени сервера.

Международные имена#

Международные доменные имена (IDNs) должны указываться в директиве server_name с использованием представления ASCII (Punycode):

server {

    listen 80;
    server_name xn--e1afmkfd.xn--80akhbyknj4f; # пример.испытание
    #    ...
}

Запрет запросов с неопределенными именами серверов#

Если запросы без заголовка Host нежелательны, можно определить сервер, который просто отклоняет такие запросы:

server {

    listen 80;
    server_name "";
    return 444;
}

В этой конфигурации имя сервера задано пустой строкой, что соответствует запросам без заголовка Host. Затем возвращается специальный нестандартный код 444, который закрывает соединение.

Сочетание виртуальных серверов, основанных на именах и IP-адресах#

Рассмотрим более сложную конфигурацию, где некоторые виртуальные серверы слушают на разных адресах:

server {

    listen 192.168.1.1:80;
    server_name example.org www.example.org;
    #  ...
}

server {

    listen 192.168.1.1:80;
    server_name example.net www.example.net;
    #  ...
}

server {

    listen 192.168.1.2:80;
    server_name example.com www.example.com;
    #  ...
}

В этой конфигурации Angie сначала проверяет IP-адрес и порт запроса по директивам listen блоков server. Затем он проверяет поле заголовка Host запроса по записям server_name блоков server, которые совпали с IP-адресом и портом. Если имя сервера не найдено, запрос будет обработан сервером по умолчанию. Например, запрос к www.example.com, полученный на порту 192.168.1.1:80, будет обработан сервером по умолчанию для этого порта — т.е. первым сервером — поскольку www.example.com не определен для этого порта.

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

server {

    listen 192.168.1.1:80;
    server_name example.org www.example.org;
    #  ...
}

server {

    listen 192.168.1.1:80 default_server;
    server_name example.net www.example.net;
    #  ...
}

server {

    listen 192.168.1.2:80 default_server;
    server_name example.com www.example.com;
    #  ...
}

Выбор локаций#

Рассмотрим простую конфигурацию PHP-сайта:

server {

    listen 80;
    server_name example.org www.example.org;
    root /data/www;

    location / {

        index index.html index.php;
    }

    location ~* \.(gif|jpg|png)$ {

        expires 30d;
    }

    location ~ \.php$ {

        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME
        $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Angie сначала ищет наиболее конкретный префикс location, заданный буквальными строками, независимо от их порядка в списке. В приведенной выше конфигурации единственный префикс локации — это location /, который соответствует любому запросу и будет использоваться в качестве последнего средства. Затем Angie проверяет локации, определенные с помощью регулярных выражений, в порядке их появления в конфигурационном файле. Первое совпадающее выражение завершает поиск, и Angie использует эту location. Если ни одно регулярное выражение не совпадает с запросом, Angie использует наиболее конкретную префиксную location, найденную ранее.

Примечание

Локации всех типов проверяют только URI часть строки запроса, исключая аргументы. Это связано с тем, что аргументы в строке запроса могут быть указаны разными способами, например:

  • /index.php?user=john&page=1

  • /index.php?page=1&user=john

Кроме того, строка запроса может содержать любое количество параметров:

  • /index.php?page=1&something+else&user=john

Теперь давайте рассмотрим, как запросы будут обрабатываться в приведенной выше конфигурации:

  • Запрос /logo.gif сначала соответствует префиксу location /, а затем регулярному выражению .(gif|jpg|png)$. Поэтому он обрабатывается последней локацией. Используя директиву root /data/www, запрос сопоставляется с файлом /data/www/logo.gif, и файл отправляется клиенту.

  • Запрос /index.php также изначально соответствует префиксу location /, а затем регулярному выражению .(php)$. Следовательно, он обрабатывается последней локацией, и запрос передается серверу FastCGI, слушающему на localhost:9000. Директива fastcgi_param устанавливает параметр FastCGI SCRIPT_FILENAME в /data/www/index.php, и сервер FastCGI выполняет файл. Переменная $document_root устанавливается в значение директивы root, а переменная $fastcgi_script_name устанавливается в URI запроса, т.е. /index.php.

  • Запрос /about.html соответствует только префиксу location /, поэтому он обрабатывается в этой локации. Используя директиву root /data/www, запрос сопоставляется с файлом /data/www/about.html, и файл отправляется клиенту.

Обработка запроса / более сложна. Он соответствует только префиксу location /, поэтому он обрабатывается в этой локации. Директива index затем проверяет наличие индексных файлов в соответствии с её параметрами и директивой root /data/www. Если файл /data/www/index.html не существует, но файл /data/www/index.php существует, директива выполняет внутреннюю переадресацию на /index.php, и Angie снова ищет локации, как если бы запрос был отправлен клиентом. Как упоминалось ранее, переадресованный запрос в конечном итоге будет обработан сервером FastCGI.

Проксирование и балансировка нагрузки#

Одним из распространенных способов использования Angie является настройка в качестве прокси-сервера. В этой роли Angie принимает запросы, перенаправляет их на проксируемые серверы, получает ответы от этих серверов и отправляет ответы обратно клиентам.

Простой прокси-сервер:

server {

    location / {

        proxy_pass http://backend:8080;
    }

Директива proxy_pass заставит Angie передавать запросы клиентов на сервер backend:8080 (прокси-сервер). Существует множество дополнительных директив, которые можно использовать для дальнейшей настройки прокси-соединения.

Проксирование FastCGI#

Angie может использоваться для маршрутизации запросов на FastCGI-серверы, которые выполняют приложения, построенные с использованием различных фреймворков и языков программирования, таких как PHP.

Простейшая конфигурация Angie для работы с FastCGI-сервером включает использование директивы fastcgi_pass вместо директивы proxy_pass, а также директив fastcgi_param для установки параметров, передаваемых FastCGI-серверу. Предположим, что FastCGI-сервер доступен по адресу localhost:9000. В PHP параметр SCRIPT_FILENAME используется для определения имени скрипта, а параметр QUERY_STRING используется для передачи параметров запроса. Результирующая конфигурация будет следующей:

server {

    location / {

        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param QUERY_STRING $query_string;
    }

    location ~ \.(gif|jpg|png)$ {

        root /data/images;
    }
}

Эта конфигурация настраивает сервер, который направляет все запросы, кроме запросов к статическим изображениям, на проксируемый сервер, работающий на localhost:9000 через протокол FastCGI.

Проксирование WebSocket#

Для обновления соединения с HTTP/1.1 до WebSocket используется механизм протокольного переключения <https://datatracker.ietf.org/doc/html/rfc2616#section-14.42>, доступный в HTTP/1.1.

Однако есть тонкость: поскольку заголовок Upgrade является заголовком перехода <https://datatracker.ietf.org/doc/html/rfc2616#section-13.5.1>, он не передается от клиента к проксируемому серверу. При использовании прямого проксирования клиенты могут использовать метод CONNECT для обхода этой проблемы. Этот подход не работает при обратном проксировании, так как клиенты не осведомлены о каких-либо прокси-серверах, и требуется специальная обработка на прокси-сервере.

Angie реализует специальный режим работы, который позволяет установить туннель между клиентом и проксируемым сервером, если проксируемый сервер возвращает ответ с кодом 101 (Switching Protocols), а клиент запрашивает переключение протокола через заголовок Upgrade в запросе.

Как уже упоминалось, заголовки перехода, включая Upgrade и Connection, не передаются от клиента к проксируемому серверу. Поэтому, чтобы проксируемый сервер был осведомлен о намерении клиента переключиться на протокол WebSocket, эти заголовки должны быть переданы явно:

location /chat/ {

    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Более сложный пример демонстрирует, как значение поля заголовка Connection в запросе к проксируемому серверу зависит от наличия поля Upgrade в заголовке запроса клиента:

http {

    map $http_upgrade $connection_upgrade {

        default upgrade;
        '' close;
    }

    server {

        ...

        location /chat/ {

            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }
}

По умолчанию соединение будет закрыто, если проксируемый сервер не передает никаких данных в течение 60 секунд. Этот тайм-аут можно увеличить с помощью директивы proxy_read_timeout. В качестве альтернативы: на проксируемом сервере можно настроить периодическую отправку кадров WebSocket ping для сброса тайм-аута и проверки того, активно ли соединение.

Балансировка нагрузки#

Балансировка нагрузки между несколькими экземплярами приложения — это широко используемая техника для оптимизации использования ресурсов, максимизации пропускной способности, уменьшения задержек и обеспечения отказоустойчивых конфигураций.

Angie можно использовать в качестве высокоэффективного HTTP-балансировщика нагрузки для распределения трафика между несколькими серверами приложений, тем самым улучшая производительность, масштабируемость и надежность веб-приложений.

Самая простая конфигурация для балансировки нагрузки с помощью Angie может выглядеть следующим образом:

http {

    upstream myapp1 {

        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {

        listen 80;

        location / {

            proxy_pass http://myapp1;
        }
    }
}

В приведенном примере три экземпляра одного и того же приложения работают на серверах с srv1 по srv3. Когда метод балансировки нагрузки не настроен явно, по умолчанию используется круговой метод (round-robin). Другие поддерживаемые механизмы балансировки нагрузки включают: weight, least_conn и ip_hash. Реализация обратного прокси в Angie также поддерживает встроенные (или пассивные) проверки состояния серверов. Эти проверки настраиваются с помощью директив max_fails и fail_timeout внутри блока server в контексте upstream.

Логирование#

Примечание

В дополнение к перечисленным здесь опциям, вы также можете включить отладочный лог.

Syslog#

Директивы error_log и access_log поддерживают логирование в syslog. Для настройки логирования в syslog используются следующие параметры:

server=address

Указывает адрес сервера syslog. Адрес может быть доменным именем или IP-адресом с необязательным портом, либо путем к UNIX-доменному сокету, указанным после префикса "unix:". Если порт не указан, используется UDP-порт 514. Если доменное имя разрешается в несколько IP-адресов, используется первый разрешенный адрес.

facility=string

Устанавливает уровень для сообщений syslog, как определено в RFC 3164. Возможные уровни включают: "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp", "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0".."local7". По умолчанию используется "local7".

severity=string

Определяет уровень серьезности сообщений syslog для access_log, как указано в RFC 3164. Возможные значения те же, что и для второго параметра (уровень) директивы error_log. По умолчанию используется "info". Серьезность сообщений об ошибках определяется Angie, поэтому этот параметр игнорируется в директиве error_log.

tag=string

Устанавливает тег для сообщений syslog. По умолчанию используется тег "angie".

nohostname

Отключает добавление поля hostname в заголовок сообщения syslog.

Пример конфигурации syslog:

error_log syslog:server=192.168.1.1 debug;

access_log syslog:server=unix:/var/log/angie.sock,nohostname;
access_log syslog:server=[2001:db8::1]:12345,facility=local7,tag=angie,severity=info combined;

Примечание

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