Назад в блог

Алгоритмы выбора блоков server и location в Nginx: Обзор

Алгоритмы выбора блоков server и location в Nginx: Обзор

Введение

Nginx — один из самых популярных веб-серверов в мире. Он способен успешно справляться с множеством одновременных клиентских подключений. В то же время он функционирует как почтовый, веб- или обратный прокси-сервер.

Цель этого руководства — описать внутренние механизмы, определяющие, как Nginx обрабатывает запросы клиентов. Мы разберем структуру блоков server и location, а также объясним, как лучше всего снизить кажущуюся непредсказуемость обработки запросов.

Прежде всего, вот подробное руководство по теме как установить Nginx на сервер Ubuntu. А теперь начнем!

Конфигурация блоков в Nginx

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

Основными блоками, которые мы обсудим, будут блоки server и location. Блоки server представляют собой подмножество конфигураций Nginx, которые определяют, какой виртуальный сервер будет отвечать за обработку определенного типа запроса. Чаще всего они основаны на IP-адресе, доменном имени или порте входящего запроса. Администраторы настраивают несколько блоков server. Затем им нужно решить, какое из подключений должно обрабатывать запрос.

Блоки location находятся внутри блоков server. Именно они определяют, как и какие ресурсы вы можете использовать для обработки входящих запросов к их конкретному родительскому серверу. Эта модель очень гибкая. Пространство URI может быть настроено на использование этих блоков любым способом, который администратор сочтет наилучшим.

Определение того, какой блок будет обрабатывать конкретный запрос в Nginx

Nginx позволяет определять несколько блоков server. Все они функционируют как разные виртуальные веб-серверы. Следовательно, необходим метод, определяющий, какой сервер будет обрабатывать конкретные входящие запросы. Это делается путем поиска наиболее подходящего варианта для обработки запроса с помощью системы определенных проверок.

Nginx в основном работает с двумя главными директивами блока server: listen и server_name.

Поиск возможных совпадений с помощью директивы «Listen»

Первое, что оценивает Nginx, — это порт и IP-адрес запроса. Затем он сопоставляет их с директивой listen каждого сервера. Этот анализ списка серверов помогает выделить только те блоки server, которые могут обработать данный запрос.

Обычно директива listen определяет порт и IP-адрес, на запросы к которым должен отвечать конкретный блок server. Блок server, в котором отсутствует директива listen, по умолчанию получает параметры прослушивания 0.0.0.0:80. Если Nginx запущен обычным пользователем без прав root, параметр listen определяется как 0.0.0.0:8080. Это означает, что независимо от интерфейса, если запросы поступают на порт 80, блоки, определенные таким образом, будут отвечать на них. Однако это значение по умолчанию не имеет большого веса в процессе выбора сервера.

Вы можете настроить директиву listen на:

  • Одиночный IP-адрес, который прослушивает запросы на порту по умолчанию (80).
  • Одиночный порт, который прослушивает любой интерфейс на этом порту.
  • Комбинацию порта и IP-адреса.
  • Определенный путь к сокету Unix (этот параметр имеет значение только тогда, когда запросы передаются между разными серверами).

Nginx применяет набор правил при принятии решения о том, в какой блок server будет отправлен запрос. Правила зависят от конкретной конфигурации директивы listen. Они заключаются в следующем:

  • Если директива listen указана не полностью, недостающие части получают значения по умолчанию. Это означает, что IP-адрес и порт будут принудительно дополнены значениями по умолчанию для обработки запроса.
    • В этом случае блок, не содержащий директивы listen, будет использовать значение по умолчанию 0.0.0.0:80.
    • Блок, у которого отсутствует порт, но указан IP-адрес 111.111.111.111, превратится в 111.111.111.111:80.
    • Если IP-адрес отсутствует, блок с портом 8888 получит IP-адрес по умолчанию, который будет добавлен для создания 0.0.0.0:8888.
  • Определив IP-адрес и порт, Nginx затем будет искать блоки server, предлагаемые в качестве соответствия этому порту.
  • Если будет найдено только одно конкретное соответствие, это и будет нужный блок server. Если подходящих блоков несколько, Nginx обратится к директиве server_name, чтобы точнее определить конкретный блок server.

Nginx прибегнет к оценке директивы server_name только в том случае, если он не нашел блок server с точным уровнем специфичности из директивы listen. Если example.com находится на порту 80 с IP-адресом 192.168.1.10, первый блок в этом примере всегда будет тем, который обрабатывает запрос. Это происходит независимо от того, что указано в директиве server_name:

Nginx Server

Если существует более одного подходящего блока server с совпадающей специфичностью, то будет учитываться директива server_name.

Поиск возможных соответствий с помощью директивы ‘Server_Name’

Если директивы listen одинаково специфичны, Nginx проверит заголовок запроса’s ‘Host.’ Это значение, которое изначально содержит IP-адрес или домен, к которому пытался обратиться клиент. Nginx будет использовать директиву server_name внутри каждого из оставшихся подходящих блоков-кандидатов server. Он выполняет эти оценки на основе формулы. Она выглядит следующим образом:

  • Первой попыткой Nginx будет поиск блока с server_name, точно соответствующим значению заголовка ‘Host’ в запросе. Если он его найдет, блок с точным соответствием будет обслуживать запрос. В случае обнаружения нескольких блоков будет выбран первый в списке.
  • Если точных совпадений нет, Nginx попытается использовать server_name для поиска блока server, соответствующего маске с использованием символа *, расположенного в начале имени блока server в конфигурации. Нахождение блока этим методом означает, что блок server определен. Если найдено более одного совпадения, запрос будет обработан наиболее длинным совпадением.
  • Если совпадений с маской в начале нет, Nginx попытается найти блок server с соответствующей маской в конце. Другими словами, это будет имя сервера с символом * в конфигурации. Если такой блок найден, он используется для запроса. Если же найдено несколько таких блоков, Nginx снова выберет наиболее длинное совпадение.
  • Если после обеих попыток с масками совпадений по-прежнему нет, Nginx оценит те блоки server, которые определяют server_name с использованием регулярных выражений (обозначаемых символом ~ перед именем). Первый экземпляр server_name с выражением, соответствующим заголовку ‘Host’, будет считаться блоком server для обработки запроса.
  • Если и на этом этапе совпадений не обнаружено, Nginx будет использовать блок server по умолчанию для этой комбинации порта и IP-адреса.

Каждая комбинация порта и IP-адреса будет иметь назначенный блок server. Он будет использоваться, если правила определения подходящего блока server для обработки запроса не принесли результатов. Это будет первый блок в конфигурации, содержащий параметр default_server в директиве listen (он переопределит изначально найденный алгоритм). Каждая комбинация IP-адреса и порта может иметь не более одной настройки default_server.

Примеры выбора блока server

Если определенный server_name точно соответствует значению заголовка ‘Host’, для обработки запроса будет выбран этот блок server. В следующем примере заголовок ‘Host’ запроса указан как “host1.example.com”. В этом случае будет выбран второй сервер:

Nginx Server

При отсутствии точного соответствия Nginx проверит, существует ли server_name с маской. Если нет, будет выбрано самое длинное совпадение, начинающееся с маски. В следующем примере “www.example.org” находится в заголовке “Host”. Это означает, что будет выбран второй блок:

server screenshot

Если совпадение, начинающееся с подстановочного знака, не найдено, Nginx переходит к проверке подстановочного знака в конце. Для обработки запроса будет выбрано самое длинное совпадение, заканчивающееся подстановочным знаком. В данном случае заголовок «Host» равен «www.example.com», поэтому будет выбран третий блок server:

Nginx Server

Если совпадений по-прежнему нет, Nginx попытается сопоставить директивы server_name с помощью регулярных выражений. Первое из этих выражений выбирается для обработки запроса. Если «Host» равен «www.example.com», для обслуживания запроса будет выбран второй блок server:

Nginx Server

Если совпадений всё еще нет, запрос будет направлен на комбинацию IP-адреса и порта с настроенным соответствующим сервером по умолчанию.

Сопоставление блоков Location

Nginx также необходимо определить алгоритм, с помощью которого он будет решать, какой блок location на сервере будет отвечать за обработку запроса.

Синтаксис блоков Location

Прежде чем объяснять, как Nginx выбирает блок location для обработки запросов, мы рассмотрим синтаксис определений блоков location. Как упоминалось ранее, блоки location находятся внутри блоков server (и других блоков location). Их цель — принимать решения о том, как обрабатывать URI запроса. URI — это часть запроса, которая идет после IP-адреса и порта или доменного имени в запросе.

Блоки location обычно выглядят следующим образом:

Syntax for Location Blocks

Nginx будет сопоставлять URI запроса с location_match. Наличие или отсутствие вышеуказанного модификатора определяет способ, которым Nginx будет пытаться сопоставить блоки. В зависимости от модификатора блоки location будут интерпретироваться по следующим правилам:

  • Без модификаторов: Без модификаторов location будет интерпретироваться как совпадение по префиксу. Это означает, что указанный location будет сопоставляться с началом URI запроса для определения правильного совпадения.
  • =: Знак равенства означает, что этот блок будет считаться совпадением только в том случае, если URI запроса точно соответствует указанному location.
  • ~: Модификатор тильда означает, что сопоставление блока location будет чувствительным к регистру.
  • ~*: Сочетание тильды и звездочки означает, что при поиске совпадения блок location не будет чувствителен к регистру.
  • ^~: Если перед модификатором тильды стоит символ каретки, сопоставление регулярных выражений не будет выполняться, если этот блок выбран как наиболее подходящее совпадение без регулярных выражений.

Примеры синтаксиса блоков Location

В качестве примера сопоставления по префиксу, данный блок location будет выбран для ответа на URI запроса в виде /site, /site/page1/index.html или /site/index/html:

location site

Для целей этой демонстрации точного сопоставления URI, блок всегда будет использоваться для ответа на запросы URI в виде /page1, а не /page1/index.html. Если выбран этот блок и он выполняет запрос с использованием индексной страницы, фактический обработчик запроса будет перенаправлен внутри системы на другой location:

Nginx Server

Например, для location, который должен интерпретироваться с учетом регистра, следующий блок не сможет обрабатывать запросы для /FLOWER.PNG. Однако он будет обрабатывать запросы для /tortoise.jpg:

Nginx Server and Location Block Selection Algorithms: Overview

Далее рассмотрим блок, который разрешает сопоставление без учета регистра, аналогичный приведенному выше. В этом случае блок может обрабатывать как //tortoise.jpg и /FLOWER.PNG:

location

Последний вариант — это блок, который предотвращает сопоставление регулярных выражений, если он определен как оптимальное совпадение без регулярных выражений. Он может обрабатывать запросы для /costumes/ninja.html:

Nginx Server and Location Block Selection Algorithms: Overview

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

Выбор location, который будет обрабатывать запросы в Nginx

Метод, с помощью которого Nginx выбирает location для обработки запроса, аналогичен тому, как выбираются блоки server. Другими словами, он определяет оптимальный location для каждого запроса, выполняя определенный процесс. Чтобы настроить Nginx точно и правильно, крайне важно понимать этот процесс.

Принимая во внимание рассмотренные ранее объявления location, Nginx аналогичным образом использует потенциальные контексты location, проверяя соответствие для каждого location путем сравнения с ним URI из данного запроса. При этом применяется следующий алгоритм:

  • Сначала Nginx проверяет все типы location, которые не содержат регулярных выражений. Для этого он ищет все совпадения по префиксу location. Чтобы сделать это, он сопоставляет location с полным URI запроса.
  • Nginx начинает с поиска точного совпадения. Как только обнаруживается блок location, использующий модификатор =, он сравнивается с URI запроса. Если они полностью совпадают, этот блок location выбирается для обработки запроса прямо на месте.
  • Если нет блоков location, точно соответствующих сравнению с модификатором =, Nginx переходит к оценке неточных префиксов. Как только он определяет наиболее длинный префиксный location, соответствующий URI запроса, он выполняет следующие проверки:
    • Если location с наиболее длинным префиксным совпадением использует модификатор ^~, этот location будет выбран незамедлительно.
    • Если location с наиболее длинным префиксом не использует модификатор ^~, совпадение временно сохраняется Nginx, чтобы позволить переключить фокус поиска.
  • Как только наиболее длинное префиксное совпадение location найдено и сохранено, Nginx переходит к оценке location с регулярными выражениями. Они включают совпадения как с учетом регистра, так и без него. Если наиболее длинный совпадающий префиксный location содержит внутри себя какие-либо регулярные выражения, Nginx перестроит список, чтобы поместить их ближе к началу списка location. Первое выражение из перестроенного списка, которое соответствует URI запроса, будет выбрано в качестве location для обслуживания запроса.
  • Если не найдено регулярных выражений, удовлетворяющих URI запроса, для обработки запроса будет выбран ранее сохраненный location.

По умолчанию Nginx отдает приоритет совпадениям по регулярным выражениям перед предпочтительными префиксными совпадениями. Однако он сначала оценивает префиксные location, чтобы администратор мог переопределить эту тенденцию с помощью модификаторов = и ^~.

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

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

Когда происходит переход к другим location при оценке блоков location?

Как правило, после того как запрос оценен и выбран блок location для его обработки, он будет полностью обрабатываться в этом контексте. Это означает, что только унаследованные директивы и выбранные location определяют обработку запроса, без какого-либо участия соседних блоков location.

Хотя это общее правило, позволяющее предсказуемо проектировать блоки location, иногда определенные директивы внутри location также могут инициировать новый поиск. Другими словами, из правила «только один блок location» есть несколько исключений. Эти исключения могут не соответствовать ожиданиям от блоков location. Следовательно, они могут обрабатывать запрос не так, как ожидалось.

Эти внутренние перенаправления могут возникать из-за некоторых директив, включая:

  • index
  • rewrite
  • error_page
  • try_files

Если вы используете директиву index, это всегда будет приводить к внутреннему перенаправлению во время обработки запроса. Хотя поиск совпадений location обычно завершает выполнение алгоритма для ускорения процесса выбора, если найденное совпадение location является каталогом, запрос, скорее всего, будет перенаправлен в другой location для формальной обработки.

Например, следующий первый location соответствует URI запроса /exact. Однако для обработки запроса директива index, которую наследует блок location, перенаправляет запрос во вторичный блок:

index

Для такого сценария, если выполнение должно оставаться в пределах первичного блока, потребуется другая схема для обработки запроса к каталогу. Один из способов сделать это — настроить недействительный index для рассматриваемого блока и вместо этого активировать auto index:

location exact

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

Еще одна директива, которую можно использовать для переоценки обрабатываемого location, — это директива try_files. Она указывает Nginx специально проверять существование определенного набора файлов или каталогов, при этом последним критерием поиска является URI, на который Nginx должен выполнить внутреннее перенаправление.

Давайте рассмотрим следующую конфигурацию:

root var

Если поступит запрос на /blahblah, его получит первый location. Если файл blahblah не будет найден в каталоге /var/www/main, это запустит последующий поиск blahblah.html. Затем он будет искать подкаталог с именем blahblah в каталоге /var/www/main. Если все эти проверки завершатся неудачно, он перенаправит запрос на /fallback/index.html. Это вызовет еще один поиск location, который подхватит другой блок location. Затем он обработает файл /var/www/another/fallback/index.html.

Еще одна директива, которая приводит к перенаправлению в другой блок location, — это директива rewrite. Nginx будет искать новый соответствующий location на основе результата директивы rewrite, когда используется параметр last. Если изменить последний пример, включив в него эту директиву rewrite, станет очевидно, что запрос может быть перенаправлен в другой location без использования директивы try_files:

root var ww main

В этом примере запрос к /rewrite/hello изначально будет обработан первым location. После того как он будет перезаписан в /hello, запустится вторичный поиск location. Он совпадет с первым location. Он будет обработан директивой try_file, с возможным возвратом к /fallback/index.html, если совпадений не будет найдено.

Однако, если будет сделан запрос к /rewrite/fallback/hello, будет найдено совпадение с первым блоком. Таким образом, rewrite будет обработан снова, но на этот раз результатом будет /fallback/hello. Запрос будет обработан в другом блоке location.

Подобные ситуации возникают, когда вы используете директиву return для отправки кодов состояния 301 или 302. Единственная разница в том, что в результате создается новый запрос, что проявляется в виде очень явного перенаправления. Аналогично, это может произойти с директивой rewrite, когда вы применяете флаги permanent или redirect.

Еще одна директива, которая может привести к внутренним перенаправлениям, аналогичным try_again, — это директива error_page. Вы можете использовать ее при возникновении определенных кодов ошибок во время обработки. Если установлена директива try_files, директива error_page, скорее всего, никогда не будет выполнена. Это связано с тем, что данная директива будет обрабатывать весь жизненный цикл запроса.

Давайте рассмотрим следующий пример:

root screenshot

В этом случае каждый запрос будет обрабатываться первым блоком, обслуживающим файлы из /var/www/main. Это не относится к тем запросам, которые начинаются с /another. Но если файл не будет найден, будет инициировано внутреннее перенаправление на /another/whoops/html. Это приведет к еще одному поиску location. В свою очередь, это направит запрос во вторичный блок, и этот файл будет извлечен из /var/www/another/whoops.html.

Очевидно, что понимание ситуаций, в которых Nginx инициирует новый поиск location, помогает лучше прогнозировать поведение системы при обработке запросов.

Заключение

Работа администраторов становится значительно проще, когда они понимают методы, с помощью которых Nginx обрабатывает клиентские запросы. Это позволяет администраторам определить, в какой блок server будет направлен запрос. Они также могут определить, какой блок location будет выбран на основе URI запроса. В целом, это также дает администраторам возможность отслеживать контексты, применяемые Nginx при обработке каждого запроса.

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

Приятной работы!

author

Manpreet Singh

Автор · CloudSigma

Preslav Dobrev — креативный дизайнер в CloudSigma, сосредоточенный на формировании последовательного корпоративного образа с помощью традиционных и инновационных маркетинговых каналов. Он умело сочетает художественное видение со стратегическим маркетингом, создавая убедительные истории бренда.

Комментарии

Комментариев пока нет. Будьте первым.