Разработка#
Angie — проект с открытым исходным кодом,
поучаствовать в котором могут все. Клонировать исходный код Angie можно из наших открытых репозиториев:
Mercurial,
Git. Вносимые изменения должны сочетаться с остальным кодом Angie;
хорошей отправной точкой будут
правила оформления кода. Совет При сомнениях посмотрите на окружающий код и следуйте его примеру
либо запустите grep по базе кода в поисках вдохновения. Исторически коммит-лог ведется на английском языке. Сообщение начинается с однострочной сводки о проделанной работе.
Она может иметь префикс, уже использованный в коммит-логе для этой части кода.
Сводка имеет длину до 67 символов включительно;
за ней может следовать пустая строка, после которой идут подробности. В хорошем сообщении указаны причины изменения, что было сделано,
и каково положение дел сейчас: Детали, которые могли остаться незамеченными: Резюме кончается точкой и начинается с прописной (большой) буквы. Если использован префикс, то за ним следует строчная (маленькая) буква. Предложения в одной строке разделяются двойным пробелом. Проверьте, что ваши изменения работают на всех целевых платформах. Запустите тесты на всех платформах, чтобы устранить возможность регрессии: Подробности см. в файле Убедитесь, что вас устраивают юридические условия. Чтобы отправить патч, создайте PR-запрос в нашем
зеркале на GitHub. С вопросами и предложениями обращайтесь к разработчикам через
GitHub Issues. Исходный код следует следующей структуре и соглашениям. Следующие два оператора В дополнение к этому, HTTP код должен включать Почтовый код должен включать Потоковый код должен включать Для общих целей код Angie использует два целочисленных типа,
Большинство функций в Angie возвращают следующие коды: Макрос Значения Пример использования Для C строк Angie использует указатель на беззнаковый символьный тип
Строковый тип Angie Поле Операции со строками в Angie объявлены в
Другие строковые функции специфичны для Angie: Следующие функции выполняют преобразование регистра и сравнение: Следующие макросы упрощают инициализацию строк: Следующие функции форматирования поддерживают специфичные для Angie типы: Полный список опций форматирования, поддерживаемых этими функциями, находится
в Вы можете добавить префикс В Angie реализовано несколько функций для преобразования чисел.
Первые четыре преобразуют строку заданной длины в положительное целое число
указанного типа.
Они возвращают Есть две дополнительные функции преобразования чисел.
Как и первые четыре, они возвращают Интерфейс регулярных выражений в Angie является оберткой вокруг
библиотеки PCRE.
Соответствующий заголовочный файл — Чтобы использовать регулярное выражение для сопоставления строк, его сначала нужно
скомпилировать, что обычно делается на этапе конфигурации.
Обратите внимание, что поскольку поддержка PCRE опциональна, весь код, использующий интерфейс, должен
быть защищен окружающим макросом После успешной компиляции поля Скомпилированное регулярное выражение затем может использоваться для сопоставления со строками: Аргументы Если есть совпадения, к захватам можно получить доступ следующим образом: Функция Структура Структура Для получения текущего времени обычно достаточно обратиться к одной из
доступных глобальных переменных, представляющих кэшированное значение времени в желаемом
формате. Доступные строковые представления: Макросы Для явного получения времени используйте Следующие функции преобразуют Функция Тип массива Angie Элементы массива доступны в поле Используйте вызов Используйте следующие функции для добавления элементов в массив: Если текущий выделенный объем памяти недостаточно велик для размещения
новых элементов, выделяется новый блок памяти и существующие элементы
копируются в него.
Новый блок памяти обычно в два раза больше существующего. В Angie список представляет собой последовательность массивов, оптимизированную для вставки потенциально
большого количества элементов.
Тип списка Фактические элементы хранятся в частях списка, которые определяются следующим образом: Перед использованием список должен быть инициализирован вызовом
Списки в основном используются для входящих и исходящих HTTP-заголовков. Списки не поддерживают удаление элементов.
Однако при необходимости элементы могут быть внутренне помечены как отсутствующие без фактического
удаления из списка.
Например, чтобы пометить исходящие HTTP-заголовки (которые хранятся как
объекты В Angie очередь представляет собой интрузивный двусвязный список, где каждый узел определяется как
следует: Головной узел очереди не связан с какими-либо данными.
Используйте вызов Пример: Заголовочный файл Для работы с деревом в целом необходимы два узла: корневой и сторожевой.
Обычно они добавляются в пользовательскую структуру, позволяя
организовать данные в дерево, в котором листья содержат ссылку на ваши
данные или встраивают их. Для инициализации дерева: Для обхода дерева и вставки новых значений используются функции
Обход довольно прост и может быть продемонстрирован следующим
шаблоном функции поиска: Функция Для добавления узла в дерево выделите новый узел, инициализируйте его и вызовите
Для удаления узла вызовите функцию Функции хеш-таблиц объявлены в Перед инициализацией хеша необходимо знать количество элементов, которые он будет
содержать, чтобы Angie мог построить его оптимально.
Два параметра, которые необходимо настроить: Ключи хеша хранятся в Для вставки ключей в массив ключей хеша используйте функцию
Для построения хеш-таблицы вызовите функцию
Функция завершается неудачей, если параметры Когда хеш построен, используйте функцию
Для создания хеша, работающего с шаблонами, используйте тип
Можно добавлять шаблонные ключи, используя флаг
Функция распознает шаблоны и добавляет ключи в соответствующие массивы.
Обратитесь к документации модуля
Map для описания синтаксиса шаблонов и
алгоритма сопоставления. В зависимости от содержимого добавленных ключей может потребоваться инициализировать до трех
массивов ключей: один для точного сопоставления (описанный выше) и два дополнительных для включения
сопоставления, начинающегося с начала или конца строки: Массив ключей необходимо отсортировать, а результаты инициализации должны быть добавлены
в комбинированный хеш.
Инициализация массива Поиск в комбинированном хеше обрабатывается функцией
Для выделения памяти из системной кучи используйте следующие функции: Большинство выделений памяти в Angie выполняется в пулах.
Память, выделенная в пуле Angie, автоматически освобождается при уничтожении пула.
Это обеспечивает хорошую производительность выделения памяти и упрощает контроль памяти. Пул внутренне выделяет объекты в непрерывных блоках памяти.
Когда блок заполняется, выделяется новый блок и добавляется в список блоков памяти пула.
Когда запрашиваемое выделение слишком велико для размещения в блоке, запрос
перенаправляется системному аллокатору, а
возвращенный указатель сохраняется в пуле для последующего освобождения. Тип для пулов Angie — Звенья цепочек ( В пуле могут быть зарегистрированы обработчики очистки.
Обработчик очистки — это обратный вызов с аргументом, который вызывается при уничтожении пула.
Пул обычно связан с конкретным объектом Angie (например, HTTP-запросом) и
уничтожается, когда объект достигает конца своего жизненного цикла.
Регистрация очистки пула — это удобный способ освобождения ресурсов, закрытия
файловых дескрипторов или внесения окончательных изменений в общие данные, связанные с
основным объектом. Для регистрации очистки пула вызовите
Разделяемая память используется Angie для совместного использования общих данных между процессами.
Функция Структура записи разделяемой памяти Записи разделяемых зон отображаются на фактическую память в
Для выделения памяти в разделяемой памяти Angie предоставляет тип slab-пула
Slab-пул разделяет всю разделяемую зону на страницы.
Каждая страница используется для выделения объектов одинакового размера.
Указанный размер должен быть степенью двойки и больше минимального размера в
8 байт.
Несоответствующие значения округляются вверх.
Битовая маска для каждой страницы отслеживает, какие блоки используются, а какие свободны для
выделения.
Для размеров больше половины страницы (что обычно составляет 2048 байт) выделение
выполняется целыми страницами за раз. Для защиты данных в разделяемой памяти от одновременного доступа используйте мьютекс,
доступный в поле Для логирования Angie использует объекты stderr — логирование в стандартный поток ошибок (stderr) file — логирование в файл syslog — логирование в syslog memory — логирование во внутреннее хранилище памяти для целей разработки; к памяти
можно получить доступ позже с помощью отладчика Экземпляр логгера может быть цепочкой логгеров, связанных друг с другом через
поле Для каждого логгера уровень серьезности контролирует, какие сообщения записываются в
лог (логируются только события, назначенные этому уровню или выше).
Поддерживаются следующие уровни серьезности: Для отладочного логирования также проверяется маска отладки.
Маски отладки: Обычно логгеры создаются существующим кодом Angie из
директив Nginx предоставляет следующие макросы логирования: Сообщение лога форматируется в буфере размером
Приведенный выше пример приводит к записям лога, подобным этим: Объект цикла хранит контекст выполнения Angie, созданный из конкретной
конфигурации.
Его тип — Цикл создается функцией Члены цикла включают: загрузчик пути — выполняется только один раз в 60 секунд после запуска или перезагрузки
Angie.
Обычно загрузчик читает каталог и сохраняет данные в разделяемой памяти Angie.
Обработчик вызывается из выделенного процесса Angie "cache loader". менеджер пути — выполняется периодически.
Обычно менеджер удаляет старые файлы из каталога и обновляет память Angie
для отражения изменений.
Обработчик вызывается из выделенного процесса "cache manager". Для операций ввода/вывода Angie предоставляет тип буфера
Структура Для операций ввода и вывода буферы связываются в цепочки.
Цепочка — это последовательность звеньев цепи типа Каждое звено цепи хранит ссылку на свой буфер и ссылку на следующее
звено цепи. Пример использования буферов и цепочек: Тип соединения Соединение Angie может прозрачно инкапсулировать SSL-слой.
В этом случае поле Директива Поскольку количество соединений на рабочий процесс ограничено, Angie предоставляет
способ захватывать соединения, которые в данный момент используются.
Для включения или отключения повторного использования соединения вызовите функцию
Объект события Поля в Каждое соединение, полученное путем вызова функции Событие может быть настроено для отправки уведомления при истечении тайм-аута.
Таймер, используемый событиями, считает миллисекунды с некоторой неопределенной
точки в прошлом, усеченные до типа Функция Событие может быть отложено, что означает, что его обработчик будет вызван
в какой-то момент позже в рамках текущей итерации цикла событий.
Откладывание событий является хорошей практикой для упрощения кода и избежания
переполнения стека.
Отложенные события хранятся в очереди отложенных событий.
Макрос Пример: За исключением главного процесса Angie, все процессы Angie выполняют операции ввода-вывода и поэтому имеют цикл событий.
(Главный процесс Angie вместо этого проводит большую часть времени в вызове
Цикл событий имеет следующие этапы: Найти таймаут, который ближе всего к истечению, вызвав
Обработать события ввода-вывода, вызвав обработчик, специфичный для механизма уведомления о событиях,
выбранного конфигурацией Angie.
Этот обработчик ожидает возникновения по крайней мере одного события ввода-вывода, но только до истечения следующего
таймаута.
Когда происходит событие чтения или записи, устанавливается флаг Истечь таймеры, вызвав Обработать отложенные события, вызвав Все процессы Angie также обрабатывают сигналы.
Обработчики сигналов только устанавливают глобальные переменные, которые проверяются после
вызова В Angie существует несколько типов процессов.
Тип процесса хранится в глобальной переменной Процессы Angie обрабатывают следующие сигналы: Хотя все рабочие процессы Angie способны получать и правильно обрабатывать POSIX
сигналы, главный процесс не использует стандартный системный вызов Возможно выгрузить в отдельный поток задачи, которые иначе
заблокировали бы рабочий процесс Angie.
Например, Angie можно настроить на использование потоков для выполнения
файлового ввода-вывода.
Другой случай использования — библиотека, которая не имеет асинхронного интерфейса
и поэтому не может быть нормально использована с Angie.
Имейте в виду, что интерфейс потоков является помощником для существующего
асинхронного подхода к обработке клиентских соединений, и ни в коем случае
не предназначен в качестве замены. Для работы с синхронизацией доступны следующие обертки над
примитивами Вместо создания нового потока для каждой задачи Angie реализует
стратегию пула потоков.
Несколько пулов потоков могут быть настроены для разных целей
(например, для выполнения ввода-вывода на разных наборах дисков).
Каждый пул потоков создается при запуске и содержит ограниченное количество потоков,
которые обрабатывают очередь задач.
Когда задача завершена, вызывается предопределенный обработчик завершения. Заголовочный файл Во время конфигурации модуль, желающий использовать потоки, должен получить
ссылку на пул потоков, вызвав
Чтобы добавить Чтобы выполнить функцию в потоке, передайте параметры и настройте обработчик
завершения, используя структуру Каждый отдельный модуль Angie находится в отдельном каталоге, который содержит
как минимум два файла:
Файл Следующие модули обычно используются в качестве ориентиров.
По умолчанию фильтрующие модули размещаются перед
Для компиляции модуля в Angie статически используйте аргумент
Модули являются строительными блоками Angie, и большая часть его функциональности
реализована в виде модулей.
Исходный файл модуля должен содержать глобальную переменную типа
Опущенная приватная часть включает версию модуля и подпись и
заполняется с использованием предопределенного макроса Каждый модуль хранит свои приватные данные в поле Обработчики директив конфигурации вызываются по мере их появления
в файлах конфигурации в контексте главного процесса. После успешного разбора конфигурации вызывается обработчик Главный процесс создает один или несколько рабочих процессов и
обработчик Когда рабочий процесс получает команду завершения или остановки от
главного процесса, он вызывает обработчик Главный процесс вызывает обработчик Поскольку потоки используются в Angie только как вспомогательное средство ввода-вывода со своим
собственным API, обработчики Поле Набор основных модулей включает модули где Например, упрощенный модуль под названием Тип Завершите массив специальным значением Флаги для типов директив: Контекст директивы определяет, где она может появляться в конфигурации: Парсер конфигурации использует эти флаги для выдачи ошибки в случае
неправильно размещенной директивы и вызывает обработчики директив, снабженные соответствующим
указателем конфигурации, так что одни и те же директивы в разных местах могут
сохранять свои значения в отдельных местах. Поле Поле Поле Поле Аргумент Каждое HTTP-соединение клиента проходит через следующие этапы: Для каждого HTTP-запроса клиента создается объект Обратите внимание, что для HTTP-соединений поле Запрос обычно помещается в очередь вызовом
Каждый HTTP-модуль может иметь три типа конфигурации: Основная конфигурация — Применяется ко всему блоку Конфигурация сервера — Применяется к одному блоку Конфигурация местоположения — Применяется к одному блоку Структуры конфигурации создаются на этапе конфигурации Angie путем
вызова функций, которые выделяют структуры, инициализируют их
и объединяют их.
Следующий пример показывает, как создать простую конфигурацию местоположения
для модуля.
Конфигурация имеет одну настройку, Как видно из примера, функция Следующие макросы доступны
для доступа к конфигурации HTTP-модулей во время конфигурирования.
Все они принимают ссылку на Следующий пример получает указатель на конфигурацию Следующие макросы доступны для доступа к конфигурации HTTP-модулей
во время выполнения. Эти макросы получают ссылку на HTTP-запрос
Каждый HTTP-запрос проходит через последовательность фаз.
В каждой фазе выполняется определенный тип обработки запроса.
Специфичные для модуля обработчики могут быть зарегистрированы в большинстве фаз,
и многие стандартные модули Angie регистрируют свои обработчики фаз как способ
быть вызванными на определенной стадии обработки запроса.
Фазы обрабатываются последовательно, и обработчики фаз вызываются,
как только запрос достигает фазы.
Ниже приведен список фаз Angie HTTP. Ниже приведен пример обработчика фазы preaccess. Ожидается, что обработчики фаз возвращают определенные коды: Любое другое значение, возвращаемое обработчиком фазы, рассматривается как код
завершения запроса, в частности, как код HTTP-ответа.
Запрос завершается с предоставленным кодом. Для некоторых фаз коды возврата обрабатываются несколько по-другому.
На фазе содержимого любой код возврата, отличный от
Репозиторий
nginx-dev-examples
предоставляет примеры модулей nginx, подходящие и для Angie. максимальная ширина текста составляет 80 символов отступ составляет 4 пробела никаких табуляций, никаких завершающих пробелов элементы списка на одной строке разделяются пробелами шестнадцатеричные литералы записываются строчными буквами имена файлов, функций и типов, а также глобальные переменные имеют
префикс Типичный исходный файл может содержать следующие разделы, разделенные
двумя пустыми строками: заявления об авторских правах включения определения препроцессора определения типов прототипы функций определения переменных определения функций Заявления об авторских правах выглядят следующим образом: Если файл значительно изменяется, список авторов должен быть обновлен,
новый автор добавляется в начало. Файлы Заголовочные файлы должны включать так называемую "защиту заголовка": комментарии текст написан на английском языке, предпочтительна американская орфография многострочные комментарии форматируются следующим образом: Имена макросов начинаются с префикса Условия заключаются в скобки, отрицание находится снаружи: Имена типов заканчиваются суффиксом Типы структур определяются с использованием Сохраняйте одинаковое выравнивание среди различных структур в файле.
Структура, которая указывает на себя, имеет имя, заканчивающееся на
Каждый член структуры объявляется на отдельной строке: Указатели на функции внутри структур имеют определенные типы, заканчивающиеся
на Перечисления имеют типы, заканчивающиеся на Переменные объявляются отсортированными по длине базового типа, затем в алфавитном порядке.
Имена типов и имена переменных выравниваются.
"Столбцы" типа и имени разделяются двумя пробелами.
Большие массивы помещаются в конец блока объявлений: Статические и глобальные переменные могут быть инициализированы при объявлении: Существует множество часто используемых комбинаций тип/имя: Все функции (даже статические) должны иметь прототипы.
Прототипы включают имена аргументов.
Длинные прототипы переносятся с одним отступом на строках продолжения: Имя функции в определении начинается с новой строки.
Открывающая и закрывающая скобки тела функции находятся на отдельных строках.
Тело функции имеет отступ.
Между функциями есть две пустые строки: Нет пробела после имени функции и открывающей скобки.
Длинные вызовы функций переносятся таким образом, что строки продолжения начинаются
с позиции первого аргумента функции.
Если это невозможно, отформатируйте первую строку продолжения так, чтобы она
заканчивалась на позиции 79: Макрос Бинарные операторы, кроме Приведения типов отделяются одним пробелом от приводимых выражений.
Звездочка внутри приведения типа отделяется пробелом от имени типа: Если выражение не помещается в одну строку, оно переносится.
Предпочтительное место для разрыва строки — бинарный оператор.
Строка продолжения выравнивается с началом выражения: В крайнем случае можно перенести выражение так, чтобы
строка продолжения заканчивалась на позиции 79: Вышеуказанные правила также применяются к подвыражениям,
где каждое подвыражение имеет свой собственный уровень отступа: Иногда удобно перенести выражение после приведения типа.
В этом случае строка продолжения имеет отступ: Указатели явно сравниваются с
Ключевое слово Аналогичные правила форматирования применяются к циклам Ключевое слово Большинство циклов Если какая-то часть оператора Цикл с пустым телом также обозначается комментарием
Бесконечный цикл выглядит следующим образом: Метки окружаются пустыми строками и имеют отступ на предыдущем уровне: Для отладки проблем с памятью, таких как переполнение буферов или использование освобожденной памяти, вы
можете использовать AddressSanitizer
(ASan), поддерживаемый некоторыми современными компиляторами.
Для включения ASan с Поскольку большинство выделений памяти в Angie производится из внутреннего
пула Angie, включение ASan не всегда может быть достаточным для отладки
проблем с памятью.
Внутренний пул выделяет большой блок памяти из системы и вырезает
из него меньшие выделения.
Однако этот механизм можно отключить, установив
макрос Следующая строка конфигурации обобщает информацию, представленную выше.
Она рекомендуется при разработке сторонних модулей и тестировании Angie на
различных платформах. Наиболее распространенная ошибка — это попытка написать полноценный C-модуль,
когда этого можно избежать.
В большинстве случаев ваша задача может быть решена созданием правильной конфигурации.
Если написание модуля неизбежно, постарайтесь сделать его
максимально маленьким и простым.
Например, модуль может только экспортировать некоторые
Перед началом создания модуля рассмотрите следующие вопросы: Возможно ли реализовать желаемую функциональность, используя уже
Встроенные модули? Возможно ли решить проблему, используя встроенные языки сценариев,
такие как perl или Сторонние модули? Наиболее используемый тип строк в Angie,
Избегайте использования глобальных переменных в ваших модулях.
Скорее всего, наличие глобальной переменной является ошибкой.
Любые глобальные данные должны быть привязаны к Вместо работы с подходом malloc/free, который подвержен ошибкам,
изучите, как использовать Angie Рекомендуется избегать использования потоков в Angie, поскольку это
определенно нарушит работу: большинство функций Angie не являются потокобезопасными.
Ожидается, что поток будет выполнять только системные вызовы и
потокобезопасные функции библиотек.
Если вам нужно выполнить код, не связанный с обработкой клиентских запросов,
правильный способ — запланировать таймер в обработчике модуля Распространенная ошибка — использование библиотек, которые внутренне блокируют выполнение.
Большинство существующих библиотек являются синхронными и блокирующими по своей природе.
Другими словами, они выполняют одну операцию за раз и тратят
время на ожидание ответа от другой стороны.
В результате, когда запрос обрабатывается с помощью такой библиотеки, весь
рабочий процесс Angie блокируется, что разрушает производительность.
Используйте только библиотеки, которые предоставляют асинхронный интерфейс и не
блокируют весь процесс. Часто модулям необходимо выполнить HTTP-вызов к какому-либо внешнему сервису.
Распространенная ошибка — использование какой-либо внешней библиотеки, такой как libcurl,
для выполнения HTTP-запроса.
Совершенно не нужно привносить огромное количество внешнего
(вероятно, Существует два основных сценария использования, когда необходим внешний запрос: в контексте обработки клиентского запроса (например, в обработчике содержимого) в контексте рабочего процесса (например, обработчик таймера) В первом случае лучше всего использовать
Для второго случая можно использовать базовую функциональность HTTP-клиента,
доступную в Angie.
Например,
модуль OCSP
реализует простой HTTP-клиент.Исходный код#
Стиль кода#
Коммит-сообщения#
API: bad things removed, good things added.
As explained elsewhere[1], the original API was bad because stuff;
this change was introduced to improve that aspect locally.
Levels of goodness have been implemented to mitigate the badness;
this is now the preferred way to work. Also, the badness is gone.
[1] https://example.com
Финальные проверки#
$ cd tests
$ prove .
tests/README
.Куда отправлять патчи#
Правила оформления кода#
Структура кода#
auto
— Скрипты сборкиsrc
core
— Базовые типы и функции — строки, массивы, журнал,
пул и т.д.event
— Ядро событийmodules
— Модули уведомления о событиях:
epoll
, kqueue
, select
и т.д.http
— Основной HTTP модуль и общий кодmodules
— Другие HTTP модулиv2
— HTTP/2mail
— Почтовые модулиos
— Платформо-зависимый кодunix
win32
stream
— Потоковые модулиВключаемые файлы#
#include
должны присутствовать в
начале каждого файла Angie:#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_mail.h>
#include <ngx_stream.h>
Целые числа#
ngx_int_t
и ngx_uint_t
, которые являются
typedef'ами для intptr_t
и uintptr_t
соответственно.Общие коды возврата#
NGX_OK
— Операция выполнена успешно.NGX_ERROR
— Операция завершилась неудачей.NGX_AGAIN
— Операция не завершена; вызовите функцию снова.NGX_DECLINED
— Операция отклонена, например, потому что она
отключена в конфигурации. Это никогда не является ошибкой.NGX_BUSY
— Ресурс недоступен.NGX_DONE
— Операция завершена или продолжена в другом месте.
Также используется как альтернативный код успеха.NGX_ABORT
— Функция была прервана.
Также используется как альтернативный код ошибки.Обработка ошибок#
ngx_errno
возвращает последний код системной ошибки.
Он сопоставлен с errno
на POSIX платформах и с
вызовом GetLastError()
в Windows.
Макрос ngx_socket_errno
возвращает последний номер ошибки
сокета.
Как и макрос ngx_errno
, он сопоставлен с
errno
на POSIX платформах.
Он сопоставлен с вызовом WSAGetLastError()
в Windows.
Обращение к значениям ngx_errno
или
ngx_socket_errno
более одного раза подряд может вызвать
проблемы с производительностью.
Если значение ошибки может использоваться несколько раз, сохраните его в локальной переменной
типа ngx_err_t
.
Для установки ошибок используйте макросы ngx_set_errno(errno)
и
ngx_set_socket_errno(errno)
.ngx_errno
и
ngx_socket_errno
могут быть переданы функциям журналирования
ngx_log_error()
и ngx_log_debugX()
, в
этом случае текст системной ошибки добавляется к сообщению журнала.ngx_errno
:ngx_int_t
ngx_my_kill(ngx_pid_t pid, ngx_log_t *log, int signo)
{
ngx_err_t err;
if (kill(pid, signo) == -1) {
err = ngx_errno;
ngx_log_error(NGX_LOG_ALERT, log, err, "kill(%P, %d) failed", pid, signo);
if (err == NGX_ESRCH) {
return 2;
}
return 1;
}
return 0;
}
Строки#
Обзор#
u_char *
.ngx_str_t
определен следующим образом:typedef struct {
size_t len;
u_char *data;
} ngx_str_t;
len
содержит длину строки, а
data
содержит данные строки.
Строка, содержащаяся в ngx_str_t
, может быть завершена
нулевым символом после len
байт, а может и не быть.
В большинстве случаев она не завершена нулевым символом.
Однако в некоторых частях кода (например, при разборе конфигурации)
объекты ngx_str_t
заведомо завершены нулевым символом, что
упрощает сравнение строк и облегчает передачу строк в
системные вызовы.src/core/ngx_string.h
.
Некоторые из них являются обертками вокруг стандартных функций C:ngx_strcmp()
ngx_strncmp()
ngx_strstr()
ngx_strlen()
ngx_strchr()
ngx_memcmp()
ngx_memset()
ngx_memcpy()
ngx_memmove()
ngx_memzero()
— Заполняет память нулями.ngx_explicit_memzero()
— Делает то же самое, что и
ngx_memzero()
, но этот вызов никогда не удаляется
оптимизацией компилятора по устранению мертвых записей.
Эта функция может использоваться для очистки чувствительных данных, таких как пароли и ключи.ngx_cpymem()
— Делает то же самое, что и
ngx_memcpy()
, но возвращает конечный адрес назначения.
Это удобно для последовательного добавления нескольких строк.ngx_movemem()
— Делает то же самое, что и
ngx_memmove()
, но возвращает конечный адрес назначения.ngx_strlchr()
— Ищет символ в строке,
ограниченной двумя указателями.ngx_tolower()
ngx_toupper()
ngx_strlow()
ngx_strcasecmp()
ngx_strncasecmp()
ngx_string(text)
— статический инициализатор для типа
ngx_str_t
из строкового литерала C
text
ngx_null_string
— статический инициализатор пустой строки для типа
ngx_str_t
ngx_str_set(str, text)
— инициализирует строку
str
типа ngx_str_t *
строковым литералом C
text
ngx_str_null(str)
— инициализирует строку str
типа ngx_str_t *
пустой строкойФорматирование#
ngx_sprintf(buf, fmt, ...)
ngx_snprintf(buf, max, fmt, ...)
ngx_slprintf(buf, last, fmt, ...)
ngx_vslprintf(buf, last, fmt, args)
ngx_vsnprintf(buf, max, fmt, args)
src/core/ngx_string.c
. Некоторые из них:%O
— off_t
%T
— time_t
%z
— ssize_t
%i
— ngx_int_t
%p
— void *
%V
— ngx_str_t *
%s
— u_char *
(завершенная нулевым символом)%*s
— size_t + u_char *
u
к большинству типов, чтобы сделать их беззнаковыми.
Для преобразования вывода в шестнадцатеричный формат используйте X
или x
.Преобразование чисел#
NGX_ERROR
при ошибке.ngx_atoi(line, n)
— ngx_int_t
ngx_atosz(line, n)
— ssize_t
ngx_atoof(line, n)
— off_t
ngx_atotm(line, n)
— time_t
NGX_ERROR
при ошибке.ngx_atofp(line, n, point)
— Преобразует число с фиксированной точкой
заданной длины в положительное целое число типа
ngx_int_t
.
Результат сдвигается влево на point
десятичных
позиций.
Ожидается, что строковое представление числа имеет не более
point
дробных цифр.
Например, ngx_atofp("10.5", 4, 2)
возвращает
1050
.ngx_hextoi(line, n)
— Преобразует шестнадцатеричное представление
положительного целого числа в ngx_int_t
.Регулярные выражения#
src/core/ngx_regex.h
.NGX_PCRE
:#if (NGX_PCRE)
ngx_regex_t *re;
ngx_regex_compile_t rc;
u_char errstr[NGX_MAX_CONF_ERRSTR];
ngx_str_t value = ngx_string("message (\\d\\d\\d).*Codeword is '(?<cw>\\w+)'");
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
rc.pattern = value;
rc.pool = cf->pool;
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
/* rc.options can be set to NGX_REGEX_CASELESS */
if (ngx_regex_compile(&rc) != NGX_OK) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
return NGX_CONF_ERROR;
}
re = rc.regex;
#endif
captures
и
named_captures
в структуре
ngx_regex_compile_t
содержат количество всех
захватов и именованных захватов соответственно, найденных в регулярном выражении.ngx_int_t n;
int captures[(1 + rc.captures) * 3];
ngx_str_t input = ngx_string("This is message 123. Codeword is 'foobar'.");
n = ngx_regex_exec(re, &input, captures, (1 + rc.captures) * 3);
if (n >= 0) {
/* строка соответствует выражению */
} else if (n == NGX_REGEX_NO_MATCHED) {
/* совпадение не найдено */
} else {
/* какая-то ошибка */
ngx_log_error(NGX_LOG_ALERT, log, 0, ngx_regex_exec_n " failed: %i", n);
}
ngx_regex_exec()
— это скомпилированное регулярное
выражение re
, строка для сопоставления input
,
опциональный массив целых чисел для хранения любых найденных captures
и size
массива.
Размер массива captures
должен быть кратен трем,
как требует
API PCRE.
В примере размер вычисляется из общего количества захватов плюс
один для самой совпавшей строки.u_char *p;
size_t size;
ngx_str_t name, value;
/* все захваты */
for (i = 0; i < n * 2; i += 2) {
value.data = input.data + captures[i];
value.len = captures[i + 1] - captures[i];
}
/* доступ к именованным захватам */
size = rc.name_size;
p = rc.names;
for (i = 0; i < rc.named_captures; i++, p += size) {
/* имя захвата */
name.data = &p[2];
name.len = ngx_strlen(name.data);
n = 2 * ((p[0] << 8) + p[1]);
/* захваченное значение */
value.data = &input.data[captures[n]];
value.len = captures[n + 1] - captures[n];
}
ngx_regex_exec_array()
принимает массив
элементов ngx_regex_elt_t
(которые являются просто скомпилированными регулярными
выражениями с ассоциированными именами), строку для сопоставления и лог.
Функция применяет выражения из массива к строке до тех пор, пока
либо не найдено совпадение, либо не закончились выражения.
Возвращаемое значение — NGX_OK
при наличии совпадения и
NGX_DECLINED
в противном случае, или NGX_ERROR
в случае ошибки.Время#
ngx_time_t
представляет время с тремя отдельными
типами для секунд, миллисекунд и смещения GMT:typedef struct {
time_t sec;
ngx_uint_t msec;
ngx_int_t gmtoff;
} ngx_time_t;
ngx_tm_t
является псевдонимом для
struct tm
на UNIX-платформах и SYSTEMTIME
в Windows.ngx_cached_err_log_time
— используется в записях журнала ошибок:
"1970/09/28 12:00:00"
ngx_cached_http_log_time
— используется в записях журнала доступа HTTP:
"28/Sep/1970:12:00:00 +0600"
ngx_cached_syslog_time
— используется в записях syslog:
"Sep 28 12:00:00"
ngx_cached_http_time
— используется в HTTP-заголовках:
"Mon, 28 Sep 1970 06:00:00 GMT"
ngx_cached_http_log_iso8601
— стандартный формат ISO 8601:
"1970-09-28T12:00:00+06:00"
ngx_time()
и ngx_timeofday()
возвращают текущее значение времени в секундах и являются предпочтительным способом доступа
к кэшированному значению времени.ngx_gettimeofday()
,
которая обновляет свой аргумент (указатель на
struct timeval
).
Время всегда обновляется, когда Angie возвращается в цикл событий из системных
вызовов.
Для немедленного обновления времени вызовите ngx_time_update()
,
или ngx_time_sigsafe_update()
, если обновляете время в
контексте обработчика сигналов.time_t
в указанное
разложенное представление времени.
Первая функция в каждой паре преобразует time_t
в
ngx_tm_t
, а вторая (с инфиксом _libc_
)
в struct tm
:ngx_gmtime(), ngx_libc_gmtime()
— время, выраженное как UTCngx_localtime(), ngx_libc_localtime()
— время, выраженное
относительно местного часового поясаngx_http_time(buf, time)
возвращает строковое
представление, подходящее для использования в HTTP-заголовках (например,
"Mon, 28 Sep 1970 06:00:00 GMT"
).
Функция ngx_http_cookie_time(buf, time)
возвращает строковое
представление, подходящее для HTTP-куки ("Thu, 31-Dec-37 23:55:55 GMT"
).Контейнеры#
Массив#
ngx_array_t
определяется следующим образомtypedef struct {
void *elts;
ngx_uint_t nelts;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *pool;
} ngx_array_t;
elts
.
Поле nelts
содержит количество элементов.
Поле size
содержит размер одного элемента и устанавливается
при инициализации массива.ngx_array_create(pool, n, size)
для создания
массива в пуле, и вызов ngx_array_init(array, pool, n, size)
для инициализации объекта массива, который уже был выделен.ngx_array_t *a, b;
/* создать массив строк с предварительно выделенной памятью для 10 элементов */
a = ngx_array_create(pool, 10, sizeof(ngx_str_t));
/* инициализировать массив строк для 10 элементов */
ngx_array_init(&b, pool, 10, sizeof(ngx_str_t));
ngx_array_push(a)
добавляет один элемент в конец и возвращает указатель
на негоngx_array_push_n(a, n)
добавляет n
элементов
в конец и возвращает указатель на первый из нихs = ngx_array_push(a);
ss = ngx_array_push_n(&b, 3);
Список#
ngx_list_t
определяется следующим образом:typedef struct {
ngx_list_part_t *last;
ngx_list_part_t part;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *pool;
} ngx_list_t;
typedef struct ngx_list_part_s ngx_list_part_t;
struct ngx_list_part_s {
void *elts;
ngx_uint_t nelts;
ngx_list_part_t *next;
};
ngx_list_init(list, pool, n, size)
или создан вызовом
ngx_list_create(pool, n, size)
.
Обе функции принимают в качестве аргументов размер одного элемента и количество
элементов на часть списка.
Для добавления элемента в список используйте функцию ngx_list_push(list)
.
Для итерации по элементам обращайтесь напрямую к полям списка, как показано в
примере:ngx_str_t *v;
ngx_uint_t i;
ngx_list_t *list;
ngx_list_part_t *part;
list = ngx_list_create(pool, 100, sizeof(ngx_str_t));
if (list == NULL) { /* ошибка */ }
/* добавить элементы в список */
v = ngx_list_push(list);
if (v == NULL) { /* ошибка */ }
ngx_str_set(v, "foo");
v = ngx_list_push(list);
if (v == NULL) { /* ошибка */ }
ngx_str_set(v, "bar");
/* итерация по списку */
part = &list->part;
v = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
v = part->elts;
i = 0;
}
ngx_do_smth(&v[i]);
}
ngx_table_elt_t
) как отсутствующие, установите поле
hash
в ngx_table_elt_t
в
ноль.
Элементы, помеченные таким образом, явно пропускаются при итерации
по заголовкам.Очередь#
typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
ngx_queue_init(q)
для инициализации головы списка
перед использованием.
Очереди поддерживают следующие операции:ngx_queue_insert_head(h, x)
,
ngx_queue_insert_tail(h, x)
— вставить новый узелngx_queue_remove(x)
— удалить узел очередиngx_queue_split(h, q, n)
— разделить очередь в узле,
возвращая хвост очереди в отдельной очередиngx_queue_add(h, n)
— добавить вторую очередь к первой очередиngx_queue_head(h)
,
ngx_queue_last(h)
— получить первый или последний узел очередиngx_queue_sentinel(h)
— получить объект-страж очереди для завершения
итерацииngx_queue_data(q, type, link)
— получить ссылку на
начало структуры данных узла очереди, учитывая смещение поля очереди в
нейtypedef struct {
ngx_str_t value;
ngx_queue_t queue;
} ngx_foo_t;
ngx_foo_t *f;
ngx_queue_t values, *q;
ngx_queue_init(&values);
f = ngx_palloc(pool, sizeof(ngx_foo_t));
if (f == NULL) { /* ошибка */ }
ngx_str_set(&f->value, "foo");
ngx_queue_insert_tail(&values, &f->queue);
/* вставить больше узлов здесь */
for (q = ngx_queue_head(&values);
q != ngx_queue_sentinel(&values);
q = ngx_queue_next(q))
{
f = ngx_queue_data(q, ngx_foo_t, queue);
ngx_do_smth(&f->value);
}
Красно-черное дерево#
src/core/ngx_rbtree.h
предоставляет доступ к
эффективной реализации красно-черных деревьев.typedef struct {
ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel;
/* пользовательские данные для дерева */
} my_tree_t;
typedef struct {
ngx_rbtree_node_t rbnode;
/* пользовательские данные для узла */
foo_t val;
} my_node_t;
my_tree_t root;
ngx_rbtree_init(&root.rbtree, &root.sentinel, insert_value_function);
insert_value
.
Например, функция ngx_str_rbtree_insert_value
работает
с типом ngx_str_t
.
Ее аргументами являются указатели на корневой узел для вставки, вновь созданный
узел для добавления и сторожевой узел дерева.void ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node,
ngx_rbtree_node_t *sentinel)
my_node_t *
my_rbtree_lookup(ngx_rbtree_t *rbtree, foo_t *val, uint32_t hash)
{
ngx_int_t rc;
my_node_t *n;
ngx_rbtree_node_t *node, *sentinel;
node = rbtree->root;
sentinel = rbtree->sentinel;
while (node != sentinel) {
n = (my_node_t *) node;
if (hash != node->key) {
node = (hash < node->key) ? node->left : node->right;
continue;
}
rc = compare(val, node->val);
if (rc < 0) {
node = node->left;
continue;
}
if (rc > 0) {
node = node->right;
continue;
}
return n;
}
return NULL;
}
compare()
является классической функцией сравнения, которая
возвращает значение меньше, равное или больше нуля.
Для ускорения поиска и избежания сравнения пользовательских объектов, которые могут быть большими, используется целочисленное
поле хеша.ngx_rbtree_insert()
:my_node_t *my_node;
ngx_rbtree_node_t *node;
my_node = ngx_palloc(...);
init_custom_data(&my_node->val);
node = &my_node->rbnode;
node->key = create_key(my_node->val);
ngx_rbtree_insert(&root->rbtree, node);
ngx_rbtree_delete()
:ngx_rbtree_delete(&root->rbtree, node);
Хеш#
src/core/ngx_hash.h
.
Поддерживается как точное, так и шаблонное сопоставление.
Последнее требует дополнительной настройки и описано в отдельном разделе ниже.max_size
и bucket_size
, как подробно описано в отдельном
разделе.
Обычно они настраиваются пользователем.
Настройки инициализации хеша хранятся в типе
ngx_hash_init_t
, а сам хеш — в
ngx_hash_t
:ngx_hash_t foo_hash;
ngx_hash_init_t hash;
hash.hash = &foo_hash;
hash.key = ngx_hash_key;
hash.max_size = 512;
hash.bucket_size = ngx_align(64, ngx_cacheline_size);
hash.name = "foo_hash";
hash.pool = cf->pool;
hash.temp_pool = cf->temp_pool;
key
— это указатель на функцию, которая создает целочисленный ключ
хеша из строки.
Существуют две универсальные функции создания ключей:
ngx_hash_key(data, len)
и
ngx_hash_key_lc(data, len)
.
Последняя преобразует строку в символы нижнего регистра, поэтому переданная строка
должна быть доступна для записи.
Если это не так, передайте флаг NGX_HASH_READONLY_KEY
в функцию, инициализирующую массив ключей (см. ниже).ngx_hash_keys_arrays_t
и
инициализируются с помощью ngx_hash_keys_array_init(arr, type)
.
Второй параметр (type
) управляет количеством ресурсов,
предварительно выделенных для хеша, и может быть либо NGX_HASH_SMALL
, либо
NGX_HASH_LARGE
.
Последний подходит, если ожидается, что хеш будет содержать тысячи
элементов.ngx_hash_keys_arrays_t foo_keys;
foo_keys.pool = cf->pool;
foo_keys.temp_pool = cf->temp_pool;
ngx_hash_keys_array_init(&foo_keys, NGX_HASH_SMALL);
ngx_hash_add_key(keys_array, key, value, flags)
:ngx_str_t k1 = ngx_string("key1");
ngx_str_t k2 = ngx_string("key2");
ngx_hash_add_key(&foo_keys, &k1, &my_data_ptr_1, NGX_HASH_READONLY_KEY);
ngx_hash_add_key(&foo_keys, &k2, &my_data_ptr_2, NGX_HASH_READONLY_KEY);
ngx_hash_init(hinit, key_names, nelts)
:ngx_hash_init(&hash, foo_keys.keys.elts, foo_keys.keys.nelts);
max_size
или
bucket_size
недостаточно велики.ngx_hash_find(hash, key, name, len)
для поиска
элементов:my_data_t *data;
ngx_uint_t key;
key = ngx_hash_key(k1.data, k1.len);
data = ngx_hash_find(&foo_hash, key, k1.data, k1.len);
if (data == NULL) {
/* ключ не найден */
}
Шаблонное сопоставление#
ngx_hash_combined_t
.
Он включает описанный выше тип хеша и имеет два дополнительных массива ключей:
dns_wc_head
и dns_wc_tail
.
Инициализация основных свойств аналогична обычному хешу:ngx_hash_init_t hash
ngx_hash_combined_t foo_hash;
hash.hash = &foo_hash.hash;
hash.key = ...;
NGX_HASH_WILDCARD_KEY
:/* k1 = ".example.org"; */
/* k2 = "foo.*"; */
ngx_hash_add_key(&foo_keys, &k1, &data1, NGX_HASH_WILDCARD_KEY);
ngx_hash_add_key(&foo_keys, &k2, &data2, NGX_HASH_WILDCARD_KEY);
if (foo_keys.dns_wc_head.nelts) {
ngx_qsort(foo_keys.dns_wc_head.elts,
(size_t) foo_keys.dns_wc_head.nelts,
sizeof(ngx_hash_key_t),
cmp_dns_wildcards);
hash.hash = NULL;
hash.temp_pool = pool;
if (ngx_hash_wildcard_init(&hash, foo_keys.dns_wc_head.elts,
foo_keys.dns_wc_head.nelts)
!= NGX_OK)
{
return NGX_ERROR;
}
foo_hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
}
dns_wc_tail
выполняется аналогично.ngx_hash_find_combined(chash, key, name, len)
:/* key = "bar.example.org"; - будет соответствовать ".example.org" */
/* key = "foo.example.com"; - будет соответствовать "foo.*" */
hkey = ngx_hash_key(key.data, key.len);
res = ngx_hash_find_combined(&foo_hash, hkey, key.data, key.len);
Управление памятью#
Куча#
ngx_alloc(size, log)
— выделить память из системной кучи.
Это обертка вокруг malloc()
с поддержкой логирования.
Ошибки выделения памяти и отладочная информация записываются в log
.ngx_calloc(size, log)
— выделить память из системной кучи
как ngx_alloc()
, но заполнить память нулями после
выделения.ngx_memalign(alignment, size, log)
— выделить выровненную память
из системной кучи.
Это обертка вокруг posix_memalign()
на тех платформах, которые предоставляют эту функцию.
В противном случае реализация возвращается к ngx_alloc()
, которая
обеспечивает максимальное выравнивание.ngx_free(p)
— освободить выделенную память.
Это обертка вокруг free()
.Пул#
ngx_pool_t
.
Поддерживаются следующие операции:ngx_create_pool(size, log)
— создать пул с указанным
размером блока.
Возвращаемый объект пула также выделяется в пуле.
size
должен быть не менее NGX_MIN_POOL_SIZE
и кратным NGX_POOL_ALIGNMENT
.ngx_destroy_pool(pool)
— освободить всю память пула, включая
сам объект пула.ngx_palloc(pool, size)
— выделить выровненную память из
указанного пула.ngx_pcalloc(pool, size)
— выделить выровненную память
из указанного пула и заполнить ее нулями.ngx_pnalloc(pool, size)
— выделить невыровненную память из
указанного пула.
В основном используется для выделения строк.ngx_pfree(pool, p)
— освободить память, которая была ранее
выделена в указанном пуле.
Могут быть освобождены только выделения, которые являются результатом запросов, перенаправленных системному аллокатору.u_char *p;
ngx_str_t *s;
ngx_pool_t *pool;
pool = ngx_create_pool(1024, log);
if (pool == NULL) { /* error */ }
s = ngx_palloc(pool, sizeof(ngx_str_t));
if (s == NULL) { /* error */ }
ngx_str_set(s, "foo");
p = ngx_pnalloc(pool, 3);
if (p == NULL) { /* error */ }
ngx_memcpy(p, "foo", 3);
ngx_chain_t
) активно используются в Angie,
поэтому реализация пула Angie предоставляет способ их повторного использования.
Поле chain
структуры ngx_pool_t
хранит
список ранее выделенных звеньев, готовых к повторному использованию.
Для эффективного выделения звена цепочки в пуле используйте функцию
ngx_alloc_chain_link(pool)
.
Эта функция ищет свободное звено цепочки в списке пула и выделяет новое
звено цепочки, если список пула пуст.
Для освобождения звена вызовите функцию ngx_free_chain(pool, cl)
.ngx_pool_cleanup_add(pool, size)
, которая возвращает указатель
ngx_pool_cleanup_t
, который должен быть
заполнен вызывающей стороной.
Используйте аргумент size
для выделения контекста для обработчика
очистки.ngx_pool_cleanup_t *cln;
cln = ngx_pool_cleanup_add(pool, 0);
if (cln == NULL) { /* error */ }
cln->handler = ngx_my_cleanup;
cln->data = "foo";
...
static void
ngx_my_cleanup(void *data)
{
u_char *msg = data;
ngx_do_smth(msg);
}
Разделяемая память#
ngx_shared_memory_add(cf, name, size, tag)
добавляет
новую запись разделяемой памяти ngx_shm_zone_t
в цикл.
Функция получает name
и size
зоны.
Каждая разделяемая зона должна иметь уникальное имя.
Если запись разделяемой зоны с предоставленными name
и
tag
уже существует, существующая запись зоны используется повторно.
Функция завершается с ошибкой, если существующая запись с тем же именем имеет
другой тег.
Обычно адрес структуры модуля передается как
tag
, что позволяет повторно использовать разделяемые зоны по имени в рамках
одного модуля Angie.ngx_shm_zone_t
имеет
следующие поля:init
— обратный вызов инициализации, вызываемый после того, как разделяемая зона
отображается на фактическую памятьdata
— контекст данных, используемый для передачи произвольных данных в
обратный вызов init
noreuse
— флаг, который отключает повторное использование разделяемой зоны из
старого циклаtag
— тег разделяемой зоныshm
— платформо-специфичный объект типа
ngx_shm_t
, имеющий как минимум следующие поля:addr
— адрес отображенной разделяемой памяти, изначально NULLsize
— размер разделяемой памятиname
— имя разделяемой памятиlog
— журнал разделяемой памятиexists
— флаг, указывающий, что разделяемая память была унаследована
от главного процесса (специфично для Windows)ngx_init_cycle()
после разбора конфигурации.
В POSIX-системах используется системный вызов mmap()
для создания
разделяемого анонимного отображения.
В Windows используется пара CreateFileMapping()
/
MapViewOfFileEx()
.ngx_slab_pool_t
.
Slab-пул для выделения памяти автоматически создается в каждой разделяемой зоне Angie.
Пул располагается в начале разделяемой зоны и доступен через
выражение (ngx_slab_pool_t *) shm_zone->shm.addr
.
Для выделения памяти в разделяемой зоне вызовите либо
ngx_slab_alloc(pool, size)
, либо
ngx_slab_calloc(pool, size)
.
Для освобождения памяти вызовите ngx_slab_free(pool, p)
.mutex
структуры
ngx_slab_pool_t
.
Мьютекс чаще всего используется slab-пулом при выделении и освобождении
памяти, но он может использоваться для защиты любых других пользовательских структур данных, выделенных
в разделяемой зоне.
Для блокировки или разблокировки мьютекса вызовите
ngx_shmtx_lock(&shpool->mutex)
или
ngx_shmtx_unlock(&shpool->mutex)
соответственно.ngx_str_t name;
ngx_foo_ctx_t *ctx;
ngx_shm_zone_t *shm_zone;
ngx_str_set(&name, "foo");
/* allocate shared zone context */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_foo_ctx_t));
if (ctx == NULL) {
/* error */
}
/* add an entry for 64k shared zone */
shm_zone = ngx_shared_memory_add(cf, &name, 65536, &ngx_foo_module);
if (shm_zone == NULL) {
/* error */
}
/* register init callback and context */
shm_zone->init = ngx_foo_init_zone;
shm_zone->data = ctx;
...
static ngx_int_t
ngx_foo_init_zone(ngx_shm_zone_t *shm_zone, void *data)
{
ngx_foo_ctx_t *octx = data;
size_t len;
ngx_foo_ctx_t *ctx;
ngx_slab_pool_t *shpool;
value = shm_zone->data;
if (octx) {
/* reusing a shared zone from old cycle */
ctx->value = octx->value;
return NGX_OK;
}
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
if (shm_zone->shm.exists) {
/* initialize shared zone context in Windows Angie worker */
ctx->value = shpool->data;
return NGX_OK;
}
/* initialize shared zone */
ctx->value = ngx_slab_alloc(shpool, sizeof(ngx_uint_t));
if (ctx->value == NULL) {
return NGX_ERROR;
}
shpool->data = ctx->value;
return NGX_OK;
}
Логирование#
ngx_log_t
.
Логгер Angie поддерживает несколько типов вывода:next
.
В этом случае каждое сообщение записывается во все логгеры в цепочке.NGX_LOG_EMERG
NGX_LOG_ALERT
NGX_LOG_CRIT
NGX_LOG_ERR
NGX_LOG_WARN
NGX_LOG_NOTICE
NGX_LOG_INFO
NGX_LOG_DEBUG
NGX_LOG_DEBUG_CORE
NGX_LOG_DEBUG_ALLOC
NGX_LOG_DEBUG_MUTEX
NGX_LOG_DEBUG_EVENT
NGX_LOG_DEBUG_HTTP
NGX_LOG_DEBUG_MAIL
NGX_LOG_DEBUG_STREAM
error_log
и доступны практически на каждом этапе
обработки в цикле, конфигурации, клиентском соединении и других объектах.ngx_log_error(level, log, err, fmt, ...)
— логирование ошибокngx_log_debug0(level, log, err, fmt)
,
ngx_log_debug1(level, log, err, fmt, arg1)
и т.д. — отладочное
логирование с поддержкой до восьми аргументов форматированияNGX_MAX_ERROR_STR
(в настоящее время 2048 байт) в стеке.
К сообщению добавляется префикс с уровнем серьезности, идентификатором процесса (PID), идентификатором соединения
(хранится в log->connection
) и текстом системной ошибки.
Для неотладочных сообщений также вызывается log->handler
для
добавления более специфичной информации к сообщению лога.
HTTP модуль устанавливает функцию ngx_http_log_error()
как обработчик лога
для логирования адресов клиента и сервера, текущего действия (хранится в
log->action
), строки запроса клиента, имени сервера и т.д./* specify what is currently done */
log->action = "sending mp4 to client";
/* error and debug log */
ngx_log_error(NGX_LOG_INFO, c->log, 0, "client prematurely
closed connection");
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 start:%ui, length:%ui", mp4->start, mp4->length);
2016/09/16 22:08:52 [info] 17445#0: *1 client prematurely closed connection while
sending mp4 to client, client: 127.0.0.1, server: , request: "GET /file.mp4 HTTP/1.1"
2016/09/16 23:28:33 [debug] 22140#0: *1 mp4 start:0, length:10000
Цикл#
ngx_cycle_t
.
Текущий цикл ссылается на глобальную переменную ngx_cycle
и
наследуется рабочими процессами Angie при их запуске.
Каждый раз при перезагрузке конфигурации Angie создается новый цикл из
новой конфигурации Angie; старый цикл обычно удаляется после успешного
создания нового.ngx_init_cycle()
, которая
принимает предыдущий цикл в качестве аргумента.
Функция находит файл конфигурации предыдущего цикла и наследует как
можно больше ресурсов от предыдущего цикла.
Цикл-заполнитель, называемый "init cycle", создается при запуске Angie, затем
заменяется фактическим циклом, построенным из конфигурации.pool
— пул цикла.
Создается для каждого нового цикла.log
— журнал цикла.
Первоначально наследуется от старого цикла, устанавливается для указания на
new_log
после чтения конфигурации.new_log
— журнал цикла, созданный конфигурацией.
На него влияет директива error_log
корневой области видимости.connections
, connection_n
—
массив соединений типа ngx_connection_t
, созданный
модулем событий при инициализации каждого рабочего процесса Angie.
Директива worker_connections
в конфигурации Angie
устанавливает количество соединений connection_n
.free_connections
,
free_connection_n
— список и количество доступных в данный момент
соединений.
Если соединения недоступны, рабочий процесс Angie отказывается принимать новых клиентов
или подключаться к upstream-серверам.files
, files_n
— массив для сопоставления файловых
дескрипторов с соединениями Angie.
Это сопоставление используется модулями событий, имеющими
флаг NGX_USE_FD_EVENT
(в настоящее время это
poll
и devpoll
).conf_ctx
— массив конфигураций основных модулей.
Конфигурации создаются и заполняются во время чтения файлов конфигурации Angie.modules
, modules_n
— массив модулей
типа ngx_module_t
, как статических, так и динамических, загруженных
текущей конфигурацией.listening
— массив объектов прослушивания типа
ngx_listening_t
.
Объекты прослушивания обычно добавляются директивой listen
различных модулей, которые вызывают функцию
ngx_create_listening()
.
Сокеты прослушивания создаются на основе объектов прослушивания.paths
— массив путей типа ngx_path_t
.
Пути добавляются вызовом функции ngx_add_path()
из
модулей, которые собираются работать с определенными каталогами.
Эти каталоги создаются Angie после чтения конфигурации, если отсутствуют.
Более того, для каждого пути можно добавить два обработчика:open_files
— список объектов открытых файлов типа
ngx_open_file_t
, которые создаются вызовом функции
ngx_conf_open_file()
.
В настоящее время Angie использует такие открытые файлы для журналирования.
После чтения конфигурации Angie открывает все файлы в списке
open_files
и сохраняет каждый файловый дескриптор в поле
fd
объекта.
Файлы открываются в режиме добавления и создаются, если отсутствуют.
Файлы в списке переоткрываются рабочими процессами Angie при получении
сигнала переоткрытия (чаще всего USR1
).
В этом случае дескриптор в поле fd
изменяется на
новое значение.shared_memory
— список зон разделяемой памяти, каждая добавляется
вызовом функции ngx_shared_memory_add()
.
Разделяемые зоны отображаются на один и тот же диапазон адресов во всех процессах Angie и
используются для совместного использования общих данных, например, дерева HTTP-кеша в памяти.Буфер#
ngx_buf_t
.
Обычно он используется для хранения данных, которые должны быть записаны в место назначения или прочитаны из
источника.
Буфер может ссылаться на данные в памяти или в файле, и технически
возможно, чтобы буфер ссылался на оба одновременно.
Память для буфера выделяется отдельно и не связана со структурой
буфера ngx_buf_t
.ngx_buf_t
имеет следующие поля:start
, end
— границы блока памяти,
выделенного для буфера.pos
, last
— границы буфера памяти;
обычно подмножество start
..
end
.file_pos
, file_last
— границы файлового
буфера, выраженные как смещения от начала файла.tag
— уникальное значение, используемое для различения буферов; создается
различными модулями Angie, обычно с целью повторного использования буфера.file
— объект файла.temporary
— флаг, указывающий, что буфер ссылается
на записываемую память.memory
— флаг, указывающий, что буфер ссылается на память
только для чтения.in_file
— флаг, указывающий, что буфер ссылается на данные
в файле.flush
— флаг, указывающий, что все данные перед буфером
должны быть сброшены.recycled
— флаг, указывающий, что буфер может быть повторно использован и
должен быть обработан как можно скорее.sync
— флаг, указывающий, что буфер не несет данных или
специальный сигнал, такой как flush
или last_buf
.
По умолчанию Angie считает такие буферы условием ошибки, но этот флаг говорит
Angie пропустить проверку ошибки.last_buf
— флаг, указывающий, что буфер является последним в
выводе.last_in_chain
— флаг, указывающий, что больше нет буферов данных
в запросе или подзапросе.shadow
— ссылка на другой ("теневой") буфер, связанный с
текущим буфером, обычно в том смысле, что буфер использует данные из
теневого буфера.
Когда буфер обрабатывается, теневой буфер обычно также помечается как
обработанный.last_shadow
— флаг, указывающий, что буфер является последним,
который ссылается на конкретный теневой буфер.temp_file
— флаг, указывающий, что буфер находится во временном
файле.ngx_chain_t
,
определенная следующим образом:typedef struct ngx_chain_s ngx_chain_t;
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
ngx_chain_t *
ngx_get_my_chain(ngx_pool_t *pool)
{
ngx_buf_t *b;
ngx_chain_t *out, *cl, **ll;
/* first buf */
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) { /* error */ }
b = ngx_calloc_buf(pool);
if (b == NULL) { /* error */ }
b->start = (u_char *) "foo";
b->pos = b->start;
b->end = b->start + 3;
b->last = b->end;
b->memory = 1; /* read-only memory */
cl->buf = b;
out = cl;
ll = &cl->next;
/* second buf */
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) { /* error */ }
b = ngx_create_temp_buf(pool, 3);
if (b == NULL) { /* error */ }
b->last = ngx_cpymem(b->last, "foo", 3);
cl->buf = b;
cl->next = NULL;
*ll = cl;
return out;
}
Сетевое взаимодействие#
Соединение#
ngx_connection_t
является оберткой вокруг
дескриптора сокета.
Он включает следующие поля:fd
— дескриптор сокетаdata
— произвольный контекст соединения.
Обычно это указатель на объект более высокого уровня, построенный поверх
соединения, такой как HTTP-запрос или Stream-сессия.read
, write
— события чтения и записи для
соединения.recv
, send
,
recv_chain
, send_chain
— операции ввода-вывода
для соединения.pool
— пул соединения.log
— журнал соединения.sockaddr
, socklen
,
addr_text
— адрес удаленного сокета в двоичной и текстовой формах.local_sockaddr
, local_socklen
— адрес
локального сокета в двоичной форме.
Изначально эти поля пусты.
Используйте функцию ngx_connection_local_sockaddr()
для получения
адреса локального сокета.proxy_protocol_addr
, proxy_protocol_port
— адрес и порт клиента по протоколу PROXY, если протокол PROXY включен для
соединения.ssl
— SSL-контекст для соединения.reusable
— флаг, указывающий, что соединение находится в состоянии,
которое делает его пригодным для повторного использования.close
— флаг, указывающий, что соединение повторно используется
и должно быть закрыто.ssl
соединения содержит указатель на
структуру ngx_ssl_connection_t
, хранящую все SSL-связанные данные
для соединения, включая SSL_CTX
и
SSL
.
Обработчики recv
, send
,
recv_chain
и send_chain
также устанавливаются
в SSL-совместимые функции.worker_connections
в конфигурации Angie
ограничивает количество соединений на один рабочий процесс Angie.
Все структуры соединений предварительно создаются при запуске рабочего процесса и сохраняются в
поле connections
объекта цикла.
Для получения структуры соединения используйте функцию
ngx_get_connection(s, log)
.
Она принимает в качестве аргумента s
дескриптор сокета, который нужно
обернуть в структуру соединения.ngx_reusable_connection(c, reusable)
.
Вызов ngx_reusable_connection(c, 1)
устанавливает
флаг reuse
в структуре соединения и вставляет
соединение в reusable_connections_queue
цикла.
Когда ngx_get_connection()
обнаруживает, что нет
доступных соединений в списке free_connections
цикла,
она вызывает ngx_drain_connections()
для освобождения
определенного количества повторно используемых соединений.
Для каждого такого соединения устанавливается флаг close
и вызывается его обработчик чтения,
который должен освободить соединение, вызвав
ngx_close_connection(c)
и сделать его доступным для повторного использования.
Для выхода из состояния, когда соединение может быть повторно использовано,
вызывается ngx_reusable_connection(c, 0)
.
HTTP-соединения клиентов являются примером повторно используемых соединений в Angie; они
помечаются как повторно используемые до тех пор, пока первый байт запроса не будет получен от клиента.События#
Событие#
ngx_event_t
в Angie предоставляет механизм
для уведомления о том, что произошло определенное событие.ngx_event_t
включают:data
— произвольный контекст события, используемый в обработчиках событий,
обычно как указатель на соединение, связанное с событием.handler
— функция обратного вызова, которая будет вызвана при
наступлении события.write
— флаг, указывающий на событие записи.
Отсутствие флага указывает на событие чтения.active
— флаг, указывающий, что событие зарегистрировано для
получения уведомлений ввода-вывода, обычно от механизмов уведомления, таких как
epoll
, kqueue
, poll
.ready
— флаг, указывающий, что событие получило
уведомление ввода-вывода.delayed
— флаг, указывающий, что ввод-вывод задержан из-за
ограничения скорости.timer
— узел красно-черного дерева для вставки события в
дерево таймеров.timer_set
— флаг, указывающий, что таймер события установлен и
еще не истек.timedout
— флаг, указывающий, что таймер события истек.eof
— флаг, указывающий, что произошел EOF при чтении данных.pending_eof
— флаг, указывающий, что EOF ожидается на
сокете, даже если могут быть доступны некоторые данные перед ним.
Флаг доставляется через событие EPOLLRDHUP
epoll
или
флаг EV_EOF
kqueue
.error
— флаг, указывающий, что произошла ошибка во время
чтения (для события чтения) или записи (для события записи).cancelable
— флаг события таймера, указывающий, что событие
должно игнорироваться при завершении работы рабочего процесса.
Корректное завершение работы рабочего процесса задерживается до тех пор, пока не останется запланированных
неотменяемых событий таймера.posted
— флаг, указывающий, что событие помещено в очередь.queue
— узел очереди для помещения события в очередь.События ввода-вывода#
ngx_get_connection()
,
имеет два прикрепленных события, c->read
и
c->write
, которые используются для получения уведомления о том, что
сокет готов для чтения или записи.
Все такие события работают в режиме Edge-Triggered, что означает, что они
запускают уведомления только при изменении состояния сокета.
Например, выполнение частичного чтения из сокета не заставляет Angie доставить
повторное уведомление о чтении до тех пор, пока в сокет не поступят новые данные.
Даже когда базовый механизм уведомлений ввода-вывода по существу является
Level-Triggered (poll
, select
и т.д.), Angie
преобразует уведомления в Edge-Triggered.
Чтобы сделать уведомления о событиях Angie согласованными во всех системах
уведомлений на разных платформах, функции
ngx_handle_read_event(rev, flags)
и
ngx_handle_write_event(wev, lowat)
должны вызываться после
обработки уведомления сокета ввода-вывода или вызова любых функций ввода-вывода
для этого сокета.
Обычно функции вызываются один раз в конце каждого обработчика события чтения
или записи.События таймера#
ngx_msec_t
.
Его текущее значение можно получить из переменной ngx_current_msec
.ngx_add_timer(ev, timer)
устанавливает тайм-аут для
события, ngx_del_timer(ev)
удаляет ранее установленный тайм-аут.
Глобальное красно-черное дерево тайм-аутов ngx_event_timer_rbtree
хранит все установленные в данный момент тайм-ауты.
Ключ в дереве имеет тип ngx_msec_t
и представляет время,
когда происходит событие.
Структура дерева обеспечивает быстрые операции вставки и удаления, а также
доступ к ближайшим тайм-аутам, что Angie использует для определения того,
как долго ждать событий ввода-вывода и для истечения событий тайм-аута.Отложенные события#
ngx_post_event(ev, q)
помещает событие
ev
в очередь отложенных событий q
.
Макрос ngx_delete_posted_event(ev)
удаляет событие
ev
из очереди, в которой оно в данный момент находится.
Обычно события помещаются в очередь ngx_posted_events
,
которая обрабатывается поздно в цикле событий — после того, как все события
ввода-вывода и таймера уже обработаны.
Функция ngx_event_process_posted()
вызывается для обработки
очереди событий.
Она вызывает обработчики событий до тех пор, пока очередь не станет пустой.
Это означает, что обработчик отложенного события может поместить больше событий
для обработки в рамках текущей итерации цикла событий.void
ngx_my_connection_read(ngx_connection_t *c)
{
ngx_event_t *rev;
rev = c->read;
ngx_add_timer(rev, 1000);
rev->handler = ngx_my_read_handler;
ngx_my_read(rev);
}
void
ngx_my_read_handler(ngx_event_t *rev)
{
ssize_t n;
ngx_connection_t *c;
u_char buf[256];
if (rev->timedout) { /* timeout expired */ }
c = rev->data;
while (rev->ready) {
n = c->recv(c, buf, sizeof(buf));
if (n == NGX_AGAIN) {
break;
}
if (n == NGX_ERROR) { /* error */ }
/* process buf */
}
if (ngx_handle_read_event(rev, 0) != NGX_OK) { /* error */ }
}
Цикл событий#
sigsuspend()
, ожидая поступления сигналов.)
Цикл событий Angie реализован в функции
ngx_process_events_and_timers()
, которая вызывается
многократно до завершения процесса.ngx_event_find_timer()
.
Эта функция находит самый левый узел в дереве таймеров и возвращает
количество миллисекунд до истечения узла.ready
и вызывается обработчик события.
Для Linux обычно используется обработчик ngx_epoll_process_events()
,
который вызывает epoll_wait()
для ожидания событий ввода-вывода.ngx_event_expire_timers()
.
Дерево таймеров обходится от самого левого элемента вправо до тех пор, пока не будет найден
неистекший таймаут.
Для каждого истекшего узла устанавливается флаг события timedout
,
сбрасывается флаг timer_set
и вызывается обработчик события.ngx_event_process_posted()
.
Функция многократно удаляет первый элемент из очереди отложенных событий
и вызывает обработчик элемента, пока очередь не станет пустой.ngx_process_events_and_timers()
.Процессы#
ngx_process
и является одним из следующих:NGX_PROCESS_MASTER
— главный процесс, который читает
конфигурацию NGINX, создает циклы и запускает и контролирует дочерние процессы.
Он не выполняет никаких операций ввода-вывода и отвечает только на сигналы.
Его функция цикла — ngx_master_process_cycle()
.NGX_PROCESS_WORKER
— рабочий процесс, который обрабатывает
клиентские соединения.
Он запускается главным процессом и отвечает на его сигналы и команды канала.
Его функция цикла — ngx_worker_process_cycle()
.
Может быть несколько рабочих процессов, как настроено директивой
worker_processes
.NGX_PROCESS_SINGLE
— единственный процесс, который существует только в
режиме master_process off
и является единственным процессом, работающим в
этом режиме.
Он создает циклы (как делает главный процесс) и обрабатывает клиентские соединения
(как делает рабочий процесс).
Его функция цикла — ngx_single_process_cycle()
.NGX_PROCESS_HELPER
— вспомогательный процесс, из которых в настоящее время
существует два типа: менеджер кеша и загрузчик кеша.
Функция цикла для обоих —
ngx_cache_manager_process_cycle()
.NGX_SHUTDOWN_SIGNAL
(SIGQUIT
в большинстве
систем) — корректное завершение работы.
При получении этого сигнала главный процесс отправляет сигнал завершения всем
дочерним процессам.
Когда дочерних процессов не остается, главный процесс уничтожает пул циклов и завершается.
Когда рабочий процесс получает этот сигнал, он закрывает все слушающие сокеты и
ожидает, пока не останется запланированных неотменяемых событий, затем уничтожает
пул циклов и завершается.
Когда менеджер кеша или процесс загрузчика кеша получает этот сигнал, он
завершается немедленно.
Переменная ngx_quit
устанавливается в 1
, когда
процесс получает этот сигнал, и немедленно сбрасывается после обработки.
Переменная ngx_exiting
устанавливается в 1
, пока
рабочий процесс находится в состоянии завершения.NGX_TERMINATE_SIGNAL
(SIGTERM
в большинстве
систем) — завершить.
При получении этого сигнала главный процесс отправляет сигнал завершения всем
дочерним процессам.
Если дочерний процесс не завершается в течение 1 секунды, главный процесс отправляет
сигнал SIGKILL
для его принудительного завершения.
Когда дочерних процессов не остается, главный процесс уничтожает пул циклов и
завершается.
Когда рабочий процесс, процесс менеджера кеша или процесс загрузчика кеша
получает этот сигнал, он уничтожает пул циклов и завершается.
Переменная ngx_terminate
устанавливается в 1
при получении этого сигнала.NGX_NOACCEPT_SIGNAL
(SIGWINCH
в большинстве
систем) — завершить все рабочие и вспомогательные процессы.
При получении этого сигнала главный процесс завершает свои дочерние процессы.
Если ранее запущенный новый двоичный файл Angie завершается, дочерние процессы старого
главного процесса запускаются снова.
Когда рабочий процесс получает этот сигнал, он завершается в режиме отладки,
установленном директивой debug_points
.NGX_RECONFIGURE_SIGNAL
(SIGHUP
в большинстве
систем) — перенастроить.
При получении этого сигнала главный процесс перечитывает конфигурацию и
создает новый цикл на ее основе.
Если новый цикл создан успешно, старый цикл удаляется и запускаются новые
дочерние процессы.
Тем временем старые дочерние процессы получают
сигнал NGX_SHUTDOWN_SIGNAL
.
В однопроцессном режиме Angie создает новый цикл, но сохраняет старый до тех пор,
пока больше нет клиентов с активными соединениями, привязанными к нему.
Рабочие и вспомогательные процессы игнорируют этот сигнал.NGX_REOPEN_SIGNAL
(SIGUSR1
в большинстве
систем) — переоткрыть файлы.
Главный процесс отправляет этот сигнал рабочим процессам, которые переоткрывают все
open_files
, связанные с циклом.NGX_CHANGEBIN_SIGNAL
(SIGUSR2
в большинстве
систем) — изменить исполняемый файл Angie.
Главный процесс запускает новый исполняемый файл Angie и передает ему список всех прослушиваемых
сокетов.
Список в текстовом формате, передаваемый в переменной окружения "NGINX"
,
состоит из номеров дескрипторов, разделенных точками с запятой.
Новый исполняемый файл Angie читает переменную "NGINX"
и добавляет
сокеты в свой цикл инициализации.
Другие процессы игнорируют этот сигнал.kill()
для передачи сигналов рабочим процессам и вспомогательным процессам.
Вместо этого Angie использует межпроцессные пары сокетов, которые позволяют отправлять сообщения
между всеми процессами Angie.
В настоящее время, однако, сообщения отправляются только от главного процесса к его дочерним процессам.
Сообщения несут стандартные сигналы.Потоки#
pthreads
:typedef pthread_mutex_t ngx_thread_mutex_t;
ngx_int_t
ngx_thread_mutex_create(ngx_thread_mutex_t *mtx, ngx_log_t *log);
ngx_int_t
ngx_thread_mutex_destroy(ngx_thread_mutex_t *mtx, ngx_log_t *log);
ngx_int_t
ngx_thread_mutex_lock(ngx_thread_mutex_t *mtx, ngx_log_t *log);
ngx_int_t
ngx_thread_mutex_unlock(ngx_thread_mutex_t *mtx, ngx_log_t *log);
typedef pthread_cond_t ngx_thread_cond_t;
ngx_int_t
ngx_thread_cond_create(ngx_thread_cond_t *cond, ngx_log_t *log);
ngx_int_t
ngx_thread_cond_destroy(ngx_thread_cond_t *cond, ngx_log_t *log);
ngx_int_t
ngx_thread_cond_signal(ngx_thread_cond_t *cond, ngx_log_t *log);
ngx_int_t
ngx_thread_cond_wait(ngx_thread_cond_t *cond, ngx_thread_mutex_t *mtx,
ngx_log_t *log);
src/core/ngx_thread_pool.h
содержит
соответствующие определения:struct ngx_thread_task_s {
ngx_thread_task_t *next;
ngx_uint_t id;
void *ctx;
void (*handler)(void *data, ngx_log_t *log);
ngx_event_t event;
};
typedef struct ngx_thread_pool_s ngx_thread_pool_t;
ngx_thread_pool_t *ngx_thread_pool_add(ngx_conf_t *cf, ngx_str_t *name);
ngx_thread_pool_t *ngx_thread_pool_get(ngx_cycle_t *cycle, ngx_str_t *name);
ngx_thread_task_t *ngx_thread_task_alloc(ngx_pool_t *pool, size_t size);
ngx_int_t ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task);
ngx_thread_pool_add(cf, name)
, которая либо создает
новый пул потоков с заданным name
, либо возвращает ссылку
на пул с таким именем, если он уже существует.task
в очередь указанного пула потоков
tp
во время выполнения, используйте функцию
ngx_thread_task_post(tp, task)
.ngx_thread_task_t
:typedef struct {
int foo;
} my_thread_ctx_t;
static void
my_thread_func(void *data, ngx_log_t *log)
{
my_thread_ctx_t *ctx = data;
/* this function is executed in a separate thread */
}
static void
my_thread_completion(ngx_event_t *ev)
{
my_thread_ctx_t *ctx = ev->data;
/* executed in Angie event loop */
}
ngx_int_t
my_task_offload(my_conf_t *conf)
{
my_thread_ctx_t *ctx;
ngx_thread_task_t *task;
task = ngx_thread_task_alloc(conf->pool, sizeof(my_thread_ctx_t));
if (task == NULL) {
return NGX_ERROR;
}
ctx = task->ctx;
ctx->foo = 42;
task->handler = my_thread_func;
task->event.handler = my_thread_completion;
task->event.data = ctx;
if (ngx_thread_task_post(conf->thread_pool, task) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
Модули#
Добавление новых модулей#
config
и файл с исходным кодом модуля.
Файл config
содержит всю информацию, необходимую Angie для
интеграции модуля, например:ngx_module_type=CORE
ngx_module_name=ngx_foo_module
ngx_module_srcs="$ngx_addon_dir/ngx_foo_module.c"
. auto/module
ngx_addon_name=$ngx_module_name
config
является POSIX shell-скриптом, который может устанавливать
и обращаться к следующим переменным:ngx_module_type
— тип модуля для сборки.
Возможные значения: CORE
, HTTP
,
HTTP_FILTER
, HTTP_INIT_FILTER
,
HTTP_AUX_FILTER
, MAIL
,
STREAM
или MISC
.ngx_module_name
— имена модулей.
Для сборки нескольких модулей из набора исходных файлов укажите
разделенный пробелами список имен.
Первое имя указывает имя выходного двоичного файла для динамического модуля.
Имена в списке должны соответствовать именам, используемым в исходном коде.ngx_addon_name
— имя модуля, как оно появляется в выводе
на консоли из скрипта configure.ngx_module_srcs
— разделенный пробелами список исходных
файлов, используемых для компиляции модуля.
Переменная $ngx_addon_dir
может использоваться для представления пути
к каталогу модуля.ngx_module_incs
— пути включения, необходимые для сборки модуляngx_module_deps
— разделенный пробелами список зависимостей модуля.
Обычно это список заголовочных файлов.ngx_module_libs
— разделенный пробелами список библиотек для
связывания с модулем.
Например, используйте ngx_module_libs=-lpthread
для связывания
с библиотекой libpthread
.
Следующие макросы могут использоваться для связывания с теми же библиотеками, что и
Angie:
LIBXSLT
, LIBGD
, GEOIP
,
PCRE
, OPENSSL
, MD5
,
SHA1
, ZLIB
и PERL
.ngx_module_link
— переменная, устанавливаемая системой сборки в
DYNAMIC
для динамического модуля или ADDON
для статического модуля и используемая для определения различных действий
в зависимости от типа связывания.ngx_module_order
— порядок загрузки модуля;
полезно для типов модулей HTTP_FILTER
и
HTTP_AUX_FILTER
.
Формат этой опции — разделенный пробелами список модулей.
Все модули в списке, следующие за именем текущего модуля, оказываются после него в
глобальном списке модулей, что устанавливает порядок инициализации модулей.
Для фильтрующих модулей более поздняя инициализация означает более раннее выполнение.ngx_http_copy_filter_module
читает данные для других
фильтрующих модулей и размещается ближе к концу списка, чтобы быть одним из
первых выполняемых.
ngx_http_write_filter_module
записывает данные в
клиентский сокет и размещается ближе к началу списка, и выполняется последним.ngx_http_copy_filter
в списке модулей, чтобы обработчик фильтра
выполнялся после обработчика фильтра копирования.
Для других типов модулей по умолчанию используется пустая строка.--add-module=/path/to/module
для скрипта configure.
Для компиляции модуля для последующей динамической загрузки в Angie используйте аргумент
--add-dynamic-module=/path/to/module
.Основные модули#
ngx_module_t
, которая определена следующим образом:struct ngx_module_s {
/* private part is omitted */
void *ctx;
ngx_command_t *commands;
ngx_uint_t type;
ngx_int_t (*init_master)(ngx_log_t *log);
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle);
void (*exit_master)(ngx_cycle_t *cycle);
/* stubs for future extensions are omitted */
};
NGX_MODULE_V1
.ctx
,
распознает директивы конфигурации, указанные в массиве
commands
, и может быть вызван на определенных стадиях
жизненного цикла Angie.
Жизненный цикл модуля состоит из следующих событий:init_module
в контексте главного процесса.
Обработчик init_module
вызывается в главном процессе каждый
раз при загрузке конфигурации.init_process
вызывается в каждом из них.exit_process
.exit_master
перед
выходом.init_thread
и exit_thread
в настоящее время не вызываются.
Также отсутствует обработчик init_master
, поскольку он был бы
ненужными накладными расходами.type
модуля определяет точно, что хранится в поле
ctx
.
Его значение является одним из следующих типов:NGX_CORE_MODULE
NGX_EVENT_MODULE
NGX_HTTP_MODULE
NGX_MAIL_MODULE
NGX_STREAM_MODULE
NGX_CORE_MODULE
является наиболее базовым и, следовательно, наиболее
общим и низкоуровневым типом модуля.
Другие типы модулей реализованы поверх него и предоставляют более
удобный способ работы с соответствующими областями, такими как обработка событий или HTTP
запросов.ngx_core_module
,
ngx_errlog_module
, ngx_regex_module
,
ngx_thread_pool_module
и
ngx_openssl_module
.
HTTP модуль, stream модуль, mail модуль и модули событий также являются основными
модулями.
Контекст основного модуля определяется как:typedef struct {
ngx_str_t name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;
name
— это строка с именем модуля,
create_conf
и init_conf
— указатели на функции, которые создают и инициализируют конфигурацию модуля
соответственно.
Для основных модулей Angie вызывает create_conf
перед разбором
новой конфигурации и init_conf
после успешного разбора всей конфигурации.
Типичная функция create_conf
выделяет память для
конфигурации и устанавливает значения по умолчанию.ngx_foo_module
может
выглядеть следующим образом:/*
* Copyright (C) Author.
*/
#include <ngx_config.h>
#include <ngx_core.h>
typedef struct {
ngx_flag_t enable;
} ngx_foo_conf_t;
static void *ngx_foo_create_conf(ngx_cycle_t *cycle);
static char *ngx_foo_init_conf(ngx_cycle_t *cycle, void *conf);
static char *ngx_foo_enable(ngx_conf_t *cf, void *post, void *data);
static ngx_conf_post_t ngx_foo_enable_post = { ngx_foo_enable };
static ngx_command_t ngx_foo_commands[] = {
{ ngx_string("foo_enabled"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_foo_conf_t, enable),
&ngx_foo_enable_post },
ngx_null_command
};
static ngx_core_module_t ngx_foo_module_ctx = {
ngx_string("foo"),
ngx_foo_create_conf,
ngx_foo_init_conf
};
ngx_module_t ngx_foo_module = {
NGX_MODULE_V1,
&ngx_foo_module_ctx, /* module context */
ngx_foo_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_foo_create_conf(ngx_cycle_t *cycle)
{
ngx_foo_conf_t *fcf;
fcf = ngx_pcalloc(cycle->pool, sizeof(ngx_foo_conf_t));
if (fcf == NULL) {
return NULL;
}
fcf->enable = NGX_CONF_UNSET;
return fcf;
}
static char *
ngx_foo_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_foo_conf_t *fcf = conf;
ngx_conf_init_value(fcf->enable, 0);
return NGX_CONF_OK;
}
static char *
ngx_foo_enable(ngx_conf_t *cf, void *post, void *data)
{
ngx_flag_t *fp = data;
if (*fp == 0) {
return NGX_CONF_OK;
}
ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "Foo Module is enabled");
return NGX_CONF_OK;
}
Директивы конфигурации#
ngx_command_t
определяет одну директиву конфигурации.
Каждый модуль, который поддерживает конфигурацию, предоставляет массив таких структур,
которые описывают, как обрабатывать аргументы и какие обработчики вызывать:typedef struct ngx_command_s ngx_command_t;
struct ngx_command_s {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};
ngx_null_command
.
name
— это имя директивы, как оно появляется
в конфигурационном файле, например "worker_processes" или "listen".
type
— это битовое поле флагов, которое определяет количество
аргументов, принимаемых директивой, ее тип и контекст, в котором она появляется.
Флаги:NGX_CONF_NOARGS
— Директива не принимает аргументов.NGX_CONF_1MORE
— Директива принимает один или более аргументов.NGX_CONF_2MORE
— Директива принимает два или более аргументов.NGX_CONF_TAKE1
..:samp:NGX_CONF_TAKE7 —
Директива принимает точно указанное количество аргументов.NGX_CONF_TAKE12
, NGX_CONF_TAKE13
,
NGX_CONF_TAKE23
, NGX_CONF_TAKE123
,
NGX_CONF_TAKE1234
— Директива может принимать разное количество
аргументов.
Варианты ограничены указанными числами.
Например, NGX_CONF_TAKE12
означает, что она принимает один или два
аргумента.NGX_CONF_BLOCK
— Директива является блоком, то есть она может
содержать другие директивы внутри своих открывающих и закрывающих скобок, или даже
реализовать собственный парсер для обработки содержимого внутри.NGX_CONF_FLAG
— Директива принимает логическое значение, либо
on
, либо off
.NGX_MAIN_CONF
— В контексте верхнего уровня.NGX_HTTP_MAIN_CONF
— В блоке http
.NGX_HTTP_SRV_CONF
— В блоке server
внутри блока http
.NGX_HTTP_LOC_CONF
— В блоке location
внутри блока http
.NGX_HTTP_UPS_CONF
— В блоке upstream
внутри блока http
.NGX_HTTP_SIF_CONF
— В блоке if
внутри
блока server
в блоке http
.NGX_HTTP_LIF_CONF
— В блоке if
внутри
блока location
в блоке http
.NGX_HTTP_LMT_CONF
— В блоке limit_except
внутри блока http
.NGX_STREAM_MAIN_CONF
— В блоке stream
.NGX_STREAM_SRV_CONF
— В блоке server
внутри блока stream
.NGX_STREAM_UPS_CONF
— В блоке upstream
внутри блока stream
.NGX_MAIL_MAIN_CONF
— В блоке mail
.NGX_MAIL_SRV_CONF
— В блоке server
внутри блока mail
.NGX_EVENT_CONF
— В блоке events
.NGX_DIRECT_CONF
— Используется модулями, которые не
создают иерархию контекстов и имеют только одну глобальную конфигурацию.
Эта конфигурация передается обработчику как аргумент conf
.set
определяет обработчик, который обрабатывает директиву
и сохраняет разобранные значения в соответствующую конфигурацию.
Существует ряд функций, которые выполняют общие преобразования:ngx_conf_set_flag_slot
— Преобразует литеральные строки
on
и off
в значение
ngx_flag_t
со значениями 1 или 0, соответственно.ngx_conf_set_str_slot
— Сохраняет строку как значение типа
ngx_str_t
.ngx_conf_set_str_array_slot
— Добавляет значение в массив
ngx_array_t
строк ngx_str_t
.
Массив создается, если еще не существует.ngx_conf_set_keyval_slot
— Добавляет пару ключ-значение в
массив ngx_array_t
пар ключ-значение
ngx_keyval_t
.
Первая строка становится ключом, а вторая — значением.
Массив создается, если еще не существует.ngx_conf_set_num_slot
— Преобразует аргумент директивы
в значение ngx_int_t
.ngx_conf_set_size_slot
— Преобразует
размер в значение size_t
,
выраженное в байтах.ngx_conf_set_off_slot
— Преобразует
смещение в значение off_t
,
выраженное в байтах.ngx_conf_set_msec_slot
— Преобразует
время в значение ngx_msec_t
,
выраженное в миллисекундах.ngx_conf_set_sec_slot
— Преобразует
время в значение time_t
,
выраженное в секундах.ngx_conf_set_bufs_slot
— Преобразует два предоставленных аргумента
в объект ngx_bufs_t
, который содержит количество и
размер буферов.ngx_conf_set_enum_slot
— Преобразует предоставленный аргумент
в значение ngx_uint_t
.
Завершающийся нулем массив ngx_conf_enum_t
, переданный в поле
post
, определяет допустимые строки и соответствующие
целочисленные значения.ngx_conf_set_bitmask_slot
— Преобразует предоставленные аргументы
в значение ngx_uint_t
.
Значения масок для каждого аргумента объединяются операцией ИЛИ, создавая результат.
Завершающийся нулем массив ngx_conf_bitmask_t
, переданный в поле
post
, определяет допустимые строки и соответствующие
значения масок.ngx_conf_set_path_slot
— Преобразует предоставленные аргументы в значение
ngx_path_t
и выполняет все необходимые инициализации.
Подробности см. в документации для директивы
proxy_temp_path.ngx_conf_set_access_slot
— Преобразует предоставленные аргументы в маску
прав доступа к файлу.
Подробности см. в документации для директивы
proxy_store_access.conf
определяет, какая структура конфигурации
передается обработчику директивы.
Основные модули имеют только глобальную конфигурацию и устанавливают
флаг NGX_DIRECT_CONF
для доступа к ней.
Модули, такие как HTTP, Stream или Mail, создают иерархии конфигураций.
Например, конфигурация модуля создается для областей server
,
location
и if
.NGX_HTTP_MAIN_CONF_OFFSET
— Конфигурация для блока
http
.NGX_HTTP_SRV_CONF_OFFSET
— Конфигурация для блока
server
внутри блока http
.NGX_HTTP_LOC_CONF_OFFSET
— Конфигурация для блока
location
внутри блока http
.NGX_STREAM_MAIN_CONF_OFFSET
— Конфигурация для блока
stream
.NGX_STREAM_SRV_CONF_OFFSET
— Конфигурация для блока
server
внутри блока stream
.NGX_MAIL_MAIN_CONF_OFFSET
— Конфигурация для блока
mail
.NGX_MAIL_SRV_CONF_OFFSET
— Конфигурация для блока
server
внутри блока mail
.offset
определяет смещение поля в структуре конфигурации модуля,
которое содержит значения для данной конкретной директивы.
Типичное использование — применение макроса offsetof()
.post
имеет две цели: оно может использоваться для определения
обработчика, который будет вызван после завершения основного обработчика, или для передачи
дополнительных данных основному обработчику.
В первом случае структура ngx_conf_post_t
должна быть
инициализирована указателем на обработчик, например:static char *ngx_do_foo(ngx_conf_t *cf, void *post, void *data);
static ngx_conf_post_t ngx_foo_post = { ngx_do_foo };
post
является самим объектом ngx_conf_post_t
,
а data
— это указатель на значение,
преобразованное из аргументов основным обработчиком с соответствующим типом.HTTP#
Соединение#
ngx_event_accept()
принимает TCP-соединение клиента.
Этот обработчик вызывается в ответ на уведомление о чтении на слушающем сокете.
На этом этапе создается новый объект ngx_connection_t
для обертывания вновь принятого клиентского сокета.
Каждый слушатель Angie предоставляет обработчик для передачи нового объекта соединения.
Для HTTP-соединений это ngx_http_init_connection(c)
.ngx_http_init_connection()
выполняет раннюю инициализацию
HTTP-соединения.
На этом этапе создается объект ngx_http_connection_t
для
соединения, и его ссылка сохраняется в поле data
соединения.
Позже он будет заменен объектом HTTP-запроса.
На этом этапе также запускается парсер протокола PROXY и SSL-рукопожатие.ngx_http_wait_request_handler()
обработчик события чтения
вызывается, когда данные доступны на клиентском сокете.
На этом этапе создается объект HTTP-запроса ngx_http_request_t
и устанавливается в поле data
соединения.ngx_http_process_request_line()
обработчик события чтения
читает строку запроса клиента.
Обработчик устанавливается ngx_http_wait_request_handler()
.
Данные читаются в buffer
соединения.
Размер буфера изначально устанавливается директивой
client_header_buffer_size.
Предполагается, что весь заголовок клиента поместится в буфер.
Если начального размера недостаточно, выделяется больший буфер
с емкостью, установленной директивой large_client_header_buffers.ngx_http_process_request_headers()
обработчик события чтения
устанавливается после ngx_http_process_request_line()
для чтения
заголовка запроса клиента.ngx_http_core_run_phases()
вызывается, когда заголовок запроса
полностью прочитан и разобран.
Эта функция запускает фазы запроса от
NGX_HTTP_POST_READ_PHASE
до
NGX_HTTP_CONTENT_PHASE
.
Последняя фаза предназначена для генерации ответа и передачи его по цепочке фильтров.
Ответ не обязательно отправляется клиенту на этой фазе.
Он может остаться в буфере и быть отправленным на этапе финализации.ngx_http_finalize_request()
обычно вызывается, когда
запрос сгенерировал весь вывод или произвел ошибку.
В последнем случае ищется соответствующая страница ошибки и используется как ответ.
Если ответ не полностью отправлен клиенту к этому моменту,
активируется HTTP-писатель ngx_http_writer()
для завершения
отправки оставшихся данных.ngx_http_finalize_connection()
вызывается, когда полный
ответ отправлен клиенту и запрос может быть уничтожен.
Если включена функция keepalive клиентского соединения,
вызывается ngx_http_set_keepalive()
, которая уничтожает
текущий запрос и ожидает следующий запрос на соединении.
В противном случае ngx_http_close_request()
уничтожает
как запрос, так и соединение.Запрос#
ngx_http_request_t
.
Некоторые поля этого объекта:connection
— Указатель на объект клиентского соединения
ngx_connection_t
.
Несколько запросов могут ссылаться на один и тот же объект соединения одновременно -
один основной запрос и его подзапросы.
После удаления запроса на том же соединении может быть создан новый запрос.data
объекта ngx_connection_t
указывает обратно на запрос.
Такие запросы называются активными, в отличие от других запросов, привязанных к соединению.
Активный запрос используется для обработки событий клиентского соединения и может
выводить свой ответ клиенту.
Обычно каждый запрос становится активным в какой-то момент, чтобы отправить свой вывод.ctx
— Массив контекстов HTTP-модулей.
Каждый модуль типа NGX_HTTP_MODULE
может хранить любое значение
(обычно указатель на структуру) в запросе.
Значение сохраняется в массиве ctx
в позиции
ctx_index
модуля.
Следующие макросы предоставляют удобный способ получения и установки контекстов запроса:ngx_http_get_module_ctx(r, module)
— Возвращает
контекст module
ngx_http_set_ctx(r, c, module)
— Устанавливает c
как контекст module
main_conf
, srv_conf
,
loc_conf
— Массивы конфигураций текущего запроса.
Конфигурации сохраняются в позициях ctx_index
модуля.read_event_handler
, write_event_handler
-
Обработчики событий чтения и записи для запроса.
Обычно как обработчики событий чтения, так и записи для HTTP-соединения
устанавливаются в ngx_http_request_handler()
.
Эта функция вызывает обработчики read_event_handler
и
write_event_handler
для текущего активного запроса.cache
— Объект кеша запроса для кеширования
ответа upstream.upstream
— Объект upstream запроса для проксирования.pool
— Пул запроса.
Сам объект запроса выделяется в этом пуле, который уничтожается при
удалении запроса.
Для выделений, которые должны быть доступны в течение всего времени жизни клиентского соединения,
используйте вместо этого пул ngx_connection_t
.header_in
— Буфер, в который читается заголовок
HTTP-запроса клиента.headers_in
, headers_out
— Объекты входящих и
исходящих HTTP-заголовков.
Оба объекта содержат поле headers
типа
ngx_list_t
для хранения сырого списка заголовков.
В дополнение к этому, специфические заголовки доступны для получения и установки как
отдельные поля, например content_length_n
,
status
и т.д.request_body
— Объект тела запроса клиента.start_sec
, start_msec
— Момент времени, когда
запрос был создан, используется для отслеживания продолжительности запроса.method
, method_name
— Числовое и текстовое
представление метода HTTP-запроса клиента.
Числовые значения для методов определены в
src/http/ngx_http_request.h
макросами
NGX_HTTP_GET
, NGX_HTTP_HEAD
,
NGX_HTTP_POST
и т.д.http_protocol
— Версия HTTP-протокола клиента в
оригинальной текстовой форме ("HTTP/1.0", "HTTP/1.1" и т.д.).http_version
— Версия HTTP-протокола клиента в
числовой форме (NGX_HTTP_VERSION_10
,
NGX_HTTP_VERSION_11
и т.д.).http_major
, http_minor
— Версия HTTP-протокола
клиента в числовой форме, разделенная на основную и младшую части.request_line
, unparsed_uri
— Строка запроса
и URI в оригинальном запросе клиента.uri
, args
, exten
—
URI, аргументы и расширение файла для текущего запроса.
Значение URI здесь может отличаться от оригинального URI, отправленного клиентом,
из-за нормализации.
В процессе обработки запроса эти значения могут изменяться при выполнении внутренних перенаправлений.main
— Указатель на объект основного запроса.
Этот объект создается для обработки HTTP-запроса клиента, в отличие от
подзапросов, которые создаются для выполнения конкретной подзадачи в рамках основного запроса.parent
— Указатель на родительский запрос подзапроса.postponed
— Список выходных буферов и подзапросов в том
порядке, в котором они отправляются и создаются.
Список используется фильтром postpone для обеспечения согласованного вывода запроса,
когда части его создаются подзапросами.post_subrequest
— Указатель на обработчик с контекстом,
который должен быть вызван при завершении подзапроса.
Не используется для основных запросов.posted_requests
— Список запросов, которые должны быть запущены или
возобновлены, что выполняется путем вызова
write_event_handler
запроса.
Обычно этот обработчик содержит основную функцию запроса, которая сначала выполняет
фазы запроса, а затем производит вывод.ngx_http_post_request(r, NULL)
.
Он всегда помещается в список posted_requests
основного запроса.
Функция ngx_http_run_posted_requests(c)
выполняет все
запросы, которые помещены в очередь в основном запросе активного запроса
переданного соединения.
Все обработчики событий вызывают ngx_http_run_posted_requests
,
что может привести к новым запросам в очереди.
Обычно она вызывается после вызова обработчика чтения или записи запроса.phase_handler
— Индекс текущей фазы запроса.phase_handler
— Индекс текущей фазы запроса.ncaptures
, captures
,
captures_data
— Захваты регулярных выражений, полученные
при последнем совпадении регулярного выражения запроса.
Совпадение регулярного выражения может произойти в нескольких местах во время обработки запроса:
поиск в карте, поиск сервера по SNI или HTTP Host, перезапись, proxy_redirect и т.д.
Захваты, полученные при поиске, сохраняются в вышеупомянутых полях.
Поле ncaptures
содержит количество захватов,
captures
содержит границы захватов, а
captures_data
содержит строку, с которой сравнивалось регулярное выражение
и которая используется для извлечения захватов.
После каждого нового совпадения регулярного выражения захваты запроса сбрасываются для хранения новых значений.count
— Счетчик ссылок запроса.
Поле имеет смысл только для основного запроса.
Увеличение счетчика выполняется простым r->main->count++
.
Для уменьшения счетчика вызовите
ngx_http_finalize_request(r, rc)
.
Создание подзапроса и запуск процесса чтения тела запроса увеличивают счетчик.subrequests
— Текущий уровень вложенности подзапросов.
Каждый подзапрос наследует уровень вложенности своего родителя, уменьшенный на единицу.
Если значение достигает нуля, генерируется ошибка.
Значение для основного запроса определяется константой
NGX_HTTP_MAX_SUBREQUESTS
.uri_changes
— Количество оставшихся изменений URI для
запроса.
Общее количество раз, когда запрос может изменить свой URI, ограничено константой
NGX_HTTP_MAX_URI_CHANGES
.
С каждым изменением значение уменьшается до тех пор, пока не достигнет нуля, после чего
генерируется ошибка.
Перезаписи и внутренние перенаправления к обычным или именованным местоположениям считаются изменениями URI.blocked
— Счетчик блокировок, удерживаемых на запросе.
Пока это значение не равно нулю, запрос не может быть завершен.
В настоящее время это значение увеличивается ожидающими операциями AIO (POSIX AIO и
операции потоков) и активной блокировкой кэша.buffered
— Битовая маска, показывающая, какие модули буферизовали
вывод, произведенный запросом.
Ряд фильтров может буферизовать вывод; например, sub_filter может буферизовать данные
из-за частичного совпадения строки, copy filter может буферизовать данные из-за
отсутствия свободных буферов вывода и т.д.
Пока это значение не равно нулю, запрос не завершается
в ожидании сброса.header_only
— Флаг, указывающий, что вывод не требует
тела.
Например, этот флаг используется HTTP-запросами HEAD.keepalive
— Флаг, указывающий, поддерживается ли
keepalive клиентского соединения.
Значение выводится из версии HTTP и значения заголовка
"Connection".header_sent
— Флаг, указывающий, что заголовок вывода
уже был отправлен запросом.internal
— Флаг, указывающий, что текущий запрос
является внутренним.
Чтобы войти во внутреннее состояние, запрос должен пройти через внутреннее
перенаправление или быть подзапросом.
Внутренним запросам разрешено входить во внутренние местоположения.allow_ranges
— Флаг, указывающий, что частичный ответ
может быть отправлен клиенту, как запрошено заголовком HTTP Range.subrequest_ranges
— Флаг, указывающий, что частичный ответ
может быть отправлен во время обработки подзапроса.single_range
— Флаг, указывающий, что только один непрерывный
диапазон выходных данных может быть отправлен клиенту.
Этот флаг обычно устанавливается при отправке потока данных, например, от
прокси-сервера, и весь ответ недоступен в одном буфере.main_filter_need_in_memory
,
filter_need_in_memory
— Флаги,
запрашивающие, чтобы вывод производился в буферах памяти, а не в файлах.
Это сигнал для copy filter читать данные из файловых буферов, даже если
sendfile включен.
Разница между двумя флагами заключается в расположении модулей фильтров, которые
их устанавливают.
Фильтры, вызываемые до фильтра postpone в цепочке фильтров, устанавливают
filter_need_in_memory
, запрашивая, чтобы только вывод текущего
запроса поступал в буферы памяти.
Фильтры, вызываемые позже в цепочке фильтров, устанавливают
main_filter_need_in_memory
, запрашивая, чтобы
как основной запрос, так и все подзапросы читали файлы в памяти
при отправке вывода.filter_need_temporary
— Флаг, запрашивающий, чтобы вывод запроса
производился во временных буферах, но не в буферах памяти только для чтения или
файловых буферах.
Это используется фильтрами, которые могут изменять вывод непосредственно в буферах, где
он отправляется.Конфигурация#
http
.
Функционирует как глобальные настройки для модуля.server
.
Функционирует как специфичные для сервера настройки модуля.location
,
if
или limit_except
.
Функционирует как специфичные для местоположения настройки модуля.foo
, типа
unsigned integer.typedef struct {
ngx_uint_t foo;
} ngx_http_foo_loc_conf_t;
static ngx_http_module_t ngx_http_foo_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_foo_create_loc_conf, /* create location configuration */
ngx_http_foo_merge_loc_conf /* merge location configuration */
};
static void *
ngx_http_foo_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_foo_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_foo_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->foo = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_http_foo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_foo_loc_conf_t *prev = parent;
ngx_http_foo_loc_conf_t *conf = child;
ngx_conf_merge_uint_value(conf->foo, prev->foo, 1);
}
ngx_http_foo_create_loc_conf()
создает новую структуру конфигурации, а
ngx_http_foo_merge_loc_conf()
объединяет конфигурацию с
конфигурацией более высокого уровня.
На самом деле, конфигурации сервера и location
существуют не только на уровнях
сервера и location
, но также создаются для всех уровней выше них.
В частности, конфигурация сервера также создается на главном уровне, а
конфигурации location
создаются на главном уровне, уровне сервера и уровне location
.
Эти конфигурации позволяют указывать настройки, специфичные для сервера и location
,
на любом уровне конфигурационного файла Angie.
В конечном итоге конфигурации объединяются вниз по иерархии.
Предоставляется ряд макросов, таких как NGX_CONF_UNSET
и
NGX_CONF_UNSET_UINT
, для обозначения отсутствующей настройки
и игнорирования ее при объединении.
Стандартные макросы объединения Angie, такие как ngx_conf_merge_value()
и
ngx_conf_merge_uint_value()
, предоставляют удобный способ
объединить настройку и установить значение по умолчанию, если ни одна из конфигураций
не предоставила явного значения.
Полный список макросов для различных типов см. в
src/core/ngx_conf_file.h
.ngx_conf_t
в качестве первого аргумента.ngx_http_conf_get_module_main_conf(cf, module)
ngx_http_conf_get_module_srv_conf(cf, module)
ngx_http_conf_get_module_loc_conf(cf, module)
location
стандартного основного модуля
и заменяет обработчик содержимого location
, хранящийся
в поле handler
структуры.static ngx_int_t ngx_http_foo_handler(ngx_http_request_t *r);
static ngx_command_t ngx_http_foo_commands[] = {
{ ngx_string("foo"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_foo,
0,
0,
NULL },
ngx_null_command
};
static char *
ngx_http_foo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_bar_handler;
return NGX_CONF_OK;
}
ngx_http_get_module_main_conf(r, module)
ngx_http_get_module_srv_conf(r, module)
ngx_http_get_module_loc_conf(r, module)
ngx_http_request_t
.
Главная конфигурация запроса никогда не изменяется.
Конфигурация сервера может измениться с настроек по умолчанию после
выбора виртуального сервера для запроса.
Конфигурация location
, выбранная для обработки запроса, может изменяться
несколько раз в результате операции перезаписи или внутреннего перенаправления.
Следующий пример показывает, как получить доступ к HTTP-конфигурации модуля
во время выполнения.static ngx_int_t
ngx_http_foo_handler(ngx_http_request_t *r)
{
ngx_http_foo_loc_conf_t *flcf;
flcf = ngx_http_get_module_loc_conf(r, ngx_http_foo_module);
...
}
Фазы#
NGX_HTTP_POST_READ_PHASE
— Первая фаза.
RealIP
регистрирует свой обработчик на этой фазе, чтобы включить
замену адресов клиентов до вызова любого другого модуля.NGX_HTTP_SERVER_REWRITE_PHASE
— Фаза, где
обрабатываются директивы rewrite, определенные в блоке server
(но вне блока location
).
Rewrite
устанавливает свой обработчик на этой фазе.NGX_HTTP_FIND_CONFIG_PHASE
— Специальная фаза,
где выбирается location
на основе URI запроса.
До этой фазы запросу назначается location
по умолчанию для соответствующего виртуального сервера,
и любой модуль, запрашивающий конфигурацию location
,
получает конфигурацию для location
сервера по умолчанию.
Эта фаза назначает новый location
запросу.
На этой фазе нельзя зарегистрировать дополнительные обработчики.NGX_HTTP_REWRITE_PHASE
— То же самое, что и
NGX_HTTP_SERVER_REWRITE_PHASE
, но для
правил rewrite, определенных в location
, выбранном на предыдущей фазе.NGX_HTTP_POST_REWRITE_PHASE
— Специальная фаза,
где запрос перенаправляется в новый location
, если его URI изменился
во время rewrite.
Это реализуется путем прохождения запроса через
NGX_HTTP_FIND_CONFIG_PHASE
снова.
На этой фазе нельзя зарегистрировать дополнительные обработчики.NGX_HTTP_PREACCESS_PHASE
— Общая фаза для различных
типов обработчиков, не связанных с контролем доступа.
Стандартные модули Angie
Limit Conn и
Limit Req регистрируют свои обработчики на этой фазе.NGX_HTTP_ACCESS_PHASE
— Фаза, где проверяется,
что клиент авторизован для выполнения запроса.
Стандартные модули Angie, такие как
Access и
Auth Basic регистрируют свои обработчики на этой фазе.
По умолчанию клиент должен пройти проверку авторизации всех обработчиков,
зарегистрированных на этой фазе, чтобы запрос продолжился к следующей фазе.
Директива satisfy
может использоваться для разрешения продолжения обработки, если любой из обработчиков фазы
авторизует клиента.NGX_HTTP_POST_ACCESS_PHASE
— Специальная фаза, где обрабатывается
директива satisfy.
Если некоторые обработчики фазы доступа запретили доступ и ни один явно не разрешил его,
запрос завершается.
На этой фазе нельзя зарегистрировать дополнительные обработчики.NGX_HTTP_PRECONTENT_PHASE
— Фаза для обработчиков, которые должны быть вызваны
до генерации контента.
Стандартные модули, такие как
try_files и
Mirror,
регистрируют свои обработчики на этой фазе.NGX_HTTP_CONTENT_PHASE
— Фаза, где обычно
генерируется ответ.
Множество стандартных модулей Angie регистрируют свои обработчики на этой фазе,
включая
Index.
Они вызываются последовательно, пока один из них не произведет
вывод.
Также возможно установить обработчики контента для каждого location
отдельно.
Если в конфигурации location
HTTP-модуль установлен handler
, он
установлен handler
, он
вызывается как обработчик контента, и обработчики, установленные на этой фазе,
игнорируются.NGX_HTTP_LOG_PHASE
— Фаза, где выполняется
логирование запроса.
В настоящее время только
Log
регистрирует свой обработчик
на этой стадии для логирования доступа.
Обработчики фазы логирования вызываются в самом конце обработки запроса, прямо
перед освобождением запроса.static ngx_http_module_t ngx_http_foo_module_ctx = {
NULL, /* preconfiguration */
ngx_http_foo_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
static ngx_int_t
ngx_http_foo_handler(ngx_http_request_t *r)
{
ngx_table_elt_t *ua;
ua = r->headers_in.user_agent;
if (ua == NULL) {
return NGX_DECLINED;
}
/* reject requests with "User-Agent: foo" */
if (ua->value.len == 3 && ngx_strncmp(ua->value.data, "foo", 3) == 0) {
return NGX_HTTP_FORBIDDEN;
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_foo_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_foo_handler;
return NGX_OK;
}
NGX_OK
— перейти к следующей фазе.NGX_DECLINED
— перейти к следующему обработчику текущей
фазы.
Если текущий обработчик является последним в текущей фазе,
перейти к следующей фазе.NGX_AGAIN
, NGX_DONE
— приостановить
обработку фазы до некоторого будущего события, которым может быть
асинхронная операция ввода-вывода или просто задержка, например.
Предполагается, что обработка фазы будет возобновлена позже путем вызова
ngx_http_core_run_phases()
.NGX_DECLINED
, считается кодом завершения.
Любой код возврата от обработчиков содержимого местоположения считается
кодом завершения.
На фазе доступа в режиме
satisfy any
любой код возврата, отличный от NGX_OK
,
NGX_DECLINED
, NGX_AGAIN
,
NGX_DONE
, считается отказом.
Если никакие последующие обработчики доступа не разрешают или не запрещают доступ с другим
кодом, код отказа станет кодом завершения.Примеры#
Стиль кода#
Общие правила#
ngx_
или более специфичный префикс, такой как
ngx_http_
и ngx_mail_
size_t
ngx_utf8_length(u_char *p, size_t n)
{
u_char c, *last;
size_t len;
last = p + n;
for (len = 0; p < last; len++) {
c = *p;
if (c < 0x80) {
p++;
continue;
}
if (ngx_utf8_decode(&p, last - p) > 0x10ffff) {
/* invalid UTF-8 */
return n;
}
}
return len;
}
Файлы#
/*
* Copyright (C) Author Name
* Copyright (C) Organization, Inc.
*/
ngx_config.h
и ngx_core.h
всегда включаются первыми, за ними следует один из
ngx_http.h
, ngx_stream.h
,
или ngx_mail.h
.
Затем следуют необязательные внешние заголовочные файлы:#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxslt/xslt.h>
#if (NGX_HAVE_EXSLT)
#include <libexslt/exslt.h>
#endif
#ifndef _NGX_PROCESS_CYCLE_H_INCLUDED_
#define _NGX_PROCESS_CYCLE_H_INCLUDED_
...
#endif /* _NGX_PROCESS_CYCLE_H_INCLUDED_ */
Комментарии#
//
не используются/*
* The red-black tree code is based on the algorithm described in
* the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
*/
/* find the server configuration for the address:port */
Препроцессор#
ngx_
или NGX_
(или более специфичного).
Имена макросов для констант записываются прописными буквами.
Параметризованные макросы и макросы для инициализаторов записываются строчными буквами.
Имя макроса и значение разделяются как минимум двумя пробелами:#define NGX_CONF_BUFFER 4096
#define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap)
#define ngx_buf_size(b) \
(ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos): \
(b->file_last - b->file_pos))
#define ngx_null_string { 0, NULL }
#if (NGX_HAVE_KQUEUE)
...
#elif ((NGX_HAVE_DEVPOLL && !(NGX_TEST_BUILD_DEVPOLL)) \
|| (NGX_HAVE_EVENTPORT && !(NGX_TEST_BUILD_EVENTPORT)))
...
#elif (NGX_HAVE_EPOLL && !(NGX_TEST_BUILD_EPOLL))
...
#elif (NGX_HAVE_POLL)
...
#else /* select */
...
#endif /* NGX_HAVE_KQUEUE */
Типы#
_t
.
Определенное имя типа отделяется как минимум двумя пробелами:typedef ngx_uint_t ngx_rbtree_key_t;
typedef
.
Внутри структур типы и имена членов выравниваются:typedef struct {
size_t len;
u_char *data;
} ngx_str_t;
_s
.
Соседние определения структур разделяются двумя пустыми строками:typedef struct ngx_list_part_s ngx_list_part_t;
struct ngx_list_part_s {
void *elts;
ngx_uint_t nelts;
ngx_list_part_t *next;
};
typedef struct {
ngx_list_part_t *last;
ngx_list_part_t part;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *pool;
} ngx_list_t;
typedef struct {
ngx_uint_t hash;
ngx_str_t key;
ngx_str_t value;
u_char *lowcase_key;
} ngx_table_elt_t;
_pt
:typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size);
typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
typedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size);
typedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
typedef struct {
ngx_recv_pt recv;
ngx_recv_chain_pt recv_chain;
ngx_recv_pt udp_recv;
ngx_send_pt send;
ngx_send_pt udp_send;
ngx_send_chain_pt udp_send_chain;
ngx_send_chain_pt send_chain;
ngx_uint_t flags;
} ngx_os_io_t;
_e
:typedef enum {
ngx_http_fastcgi_st_version = 0,
ngx_http_fastcgi_st_type,
...
ngx_http_fastcgi_st_padding
} ngx_http_fastcgi_state_e;
Переменные#
u_char *rv, *p;
ngx_conf_t *cf;
ngx_uint_t i, j, k;
unsigned int len;
struct sockaddr *sa;
const unsigned char *data;
ngx_peer_connection_t *pc;
ngx_http_core_srv_conf_t **cscfp;
ngx_http_upstream_srv_conf_t *us, *uscf;
u_char text[NGX_SOCKADDR_STRLEN];
static ngx_str_t ngx_http_memcached_key = ngx_string("memcached_key");
static ngx_uint_t mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static uint32_t ngx_crc32_table16[] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
...
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
u_char *rv;
ngx_int_t rc;
ngx_conf_t *cf;
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_peer_connection_t *pc;
ngx_http_upstream_srv_conf_t *us, *uscf;
Функции#
static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_init_phases(ngx_conf_t *cf,
ngx_http_core_main_conf_t *cmcf);
static char *ngx_http_merge_servers(ngx_conf_t *cf,
ngx_http_core_main_conf_t *cmcf, ngx_http_module_t *module,
ngx_uint_t ctx_index);
static ngx_int_t
ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len)
{
...
}
static ngx_int_t
ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)
{
...
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http header: \"%V: %V\"",
&h->key, &h->value);
hc->busy = ngx_palloc(r->connection->pool,
cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
ngx_inline
следует использовать вместо
inline
:static ngx_inline void ngx_cpuid(uint32_t i, uint32_t *buf);
Выражения#
.
и ->
,
должны быть отделены от своих операндов одним пробелом.
Унарные операторы и индексы не отделяются от своих операндов пробелами:width = width * 10 + (*fmt++ - '0');
ch = (u_char) ((decoded << 4) + (ch - '0'));
r->exten.data = &r->uri.data[i + 1];
len = ngx_sock_ntop((struct sockaddr *) sin6, p, len, 1);
if (status == NGX_HTTP_MOVED_PERMANENTLY
|| status == NGX_HTTP_MOVED_TEMPORARILY
|| status == NGX_HTTP_SEE_OTHER
|| status == NGX_HTTP_TEMPORARY_REDIRECT
|| status == NGX_HTTP_PERMANENT_REDIRECT)
{
...
}
p->temp_file->warn = "an upstream response is buffered "
"to a temporary file";
hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
+ size * sizeof(ngx_hash_elt_t *));
if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)
|| c->stale_updating) && !r->background
&& u->conf->cache_background_update)
{
...
}
node = (ngx_rbtree_node_t *)
((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
NULL
(не с 0
):if (ptr != NULL) {
...
}
Условные операторы и циклы#
if
отделяется от условия
одним пробелом.
Открывающая скобка располагается на той же строке или на
отдельной строке, если условие занимает несколько строк.
Закрывающая скобка располагается на отдельной строке, опционально за которой следует
else if
/ else
.
Обычно перед частью
else if
/ else
есть пустая строка:if (node->left == sentinel) {
temp = node->right;
subst = node;
} else if (node->right == sentinel) {
temp = node->left;
subst = node;
} else {
subst = ngx_rbtree_min(node->right, sentinel);
if (subst->left != sentinel) {
temp = subst->left;
} else {
temp = subst->right;
}
}
do
и while
:while (p < last && *p == ' ') {
p++;
}
while (p < last && *p == ' ') {
p++;
}
do {
ctx->node = rn;
ctx = ctx->next;
} while (ctx);
switch
отделяется от условия
одним пробелом.
Открывающая скобка располагается на той же строке.
Закрывающая скобка располагается на отдельной строке.
Ключевые слова case
выравниваются с
switch
:switch (ch) {
case '!':
looked = 2;
state = ssi_comment0_state;
break;
case '<':
copy_end = p;
break;
default:
copy_end = p;
looked = 0;
state = ssi_start_state;
break;
}
for
форматируются следующим образом:for (i = 0; i < ccf->env.nelts; i++) {
...
}
for (q = ngx_queue_head(locations);
q != ngx_queue_sentinel(locations);
q = ngx_queue_next(q))
{
...
}
for
опущена,
это обозначается комментарием /* void */
:for (i = 0; /* void */ ; i++) {
...
}
/* void */
, который может быть размещен на той же строке:for (cl = *busy; cl->next; cl = cl->next) { /* void */ }
for ( ;; ) {
...
}
Метки#
if (i == 0) {
u->err = "host not found";
goto failed;
}
u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t));
if (u->addrs == NULL) {
goto failed;
}
u->naddrs = i;
...
return NGX_OK;
failed:
freeaddrinfo(res);
return NGX_ERROR;
Отладка проблем с памятью#
gcc
и clang
используйте опцию компилятора и компоновщика -fsanitize=address
.
При сборке Angie это можно сделать, добавив опцию к
параметрам --with-cc-opt
и --with-ld-opt
скрипта configure
.NGX_DEBUG_PALLOC
в 1
.
В этом случае выделения передаются напрямую системному аллокатору, предоставляя ему
полный контроль над границами буферов.auto/configure --with-cc-opt='-fsanitize=address -DNGX_DEBUG_PALLOC=1'
--with-ld-opt=-fsanitize=address
Распространенные ошибки#
Написание C-модуля#
переменные <#http_variables>
.C-строки#
ngx_str_t <#string_overview>
, не является C-строкой,
завершающейся нулем.
Вы не можете передавать данные в стандартные функции библиотеки C,
такие как strlen()
или strstr()
.
Вместо этого следует использовать Angie аналоги <#string_overview>
,
которые принимают либо ngx_str_t
, либо указатель на данные и длину.
Однако есть случай, когда ngx_str_t
содержит
указатель на строку, завершающуюся нулем: строки, которые получаются в результате
разбора файла конфигурации, завершаются нулем.Глобальные переменные#
циклу конфигурации <#cycle>
и выделяться из соответствующего пула памяти <#pool>
.
Это позволяет Angie выполнять плавную перезагрузку конфигурации.
Попытка использовать глобальные переменные, вероятно, нарушит эту функциональность,
поскольку будет невозможно иметь две конфигурации одновременно
и избавляться от них.
Иногда глобальные переменные необходимы.
В этом случае требуется особое внимание для правильного управления
реконфигурацией.
Также проверьте, имеют ли библиотеки, используемые вашим кодом, неявное
глобальное состояние, которое может быть нарушено при перезагрузке.Ручное управление памятью#
пулы <#pool>
.
Пул создается и привязывается к объекту —
конфигурации <#http_conf>
,
циклу <#cycle>
,
соединению <#connection>
или HTTP-запросу <#http_request>
.
Когда объект уничтожается, связанный пул также уничтожается.
Поэтому при работе с объектом можно выделить необходимое количество памяти
из соответствующего пула и не заботиться об освобождении памяти
даже в случае ошибок.Потоки#
init_process
и выполнить необходимые действия в обработчике таймера.
Внутренне Angie использует потоки <#threads>
для
ускорения операций, связанных с вводом-выводом, но это особый случай с множеством
ограничений.Блокирующие библиотеки#
HTTP-запросы к внешним сервисам#
блокирующего <#using_libraries>
!) кода
для задачи, которая может быть выполнена самим Angie.API подзапросов <#http_subrequests>
.
Вместо прямого обращения к внешнему сервису вы объявляете location
в конфигурации Angie и направляете свой подзапрос к этому location
.
Этот location
не ограничивается
проксированием
запросов, но может содержать другие директивы Angie.
Пример такого подхода — директива
auth_request,
реализованная в модуле
angie__auth_request.