Модуль Apache mod_unique_id
| Описание: | Предоставляет переменную окружения с уникальным идентификатором для каждого запроса |
|---|---|
| Статус: | Расширение |
| Идентификатор модуля: | unique_id_module |
| Файл исходного кода: | mod_unique_id.c |
Краткое описание
Этот модуль предоставляет магический токен для каждого запроса, который гарантированно уникален для «всех» запросов при очень специфических условиях. Уникальный идентификатор также уникален на нескольких машинах в правильно настроенном кластере машин. Переменная окружения UNIQUE_ID устанавливается на идентификатор для каждого запроса. Уникальные идентификаторы полезны по различным причинам, которые выходят за рамки этого документа.
Теория
Сначала краткий обзор работы сервера Apache на Unix-машинах. Эта функция в настоящее время не поддерживается в Windows NT. На Unix-машинах Apache создает несколько дочерних процессов, которые обрабатывают запросы по одному. Каждый дочерний процесс может обслуживать несколько запросов за свою жизнь. В целях этой дискуссии дочерние процессы не делят данные друг с другом. Мы будем называть дочерние процессы процессами httpd.
Ваш веб-сайт имеет одну или несколько машин под вашим административным контролем, вместе мы назовем их кластером машин. Каждая машина может потенциально запускать несколько экземпляров Apache. Все они вместе считаются «вселенной», и при определенных предположениях мы покажем, что в этой вселенной мы можем генерировать уникальные идентификаторы для каждого запроса без обширной связи между машинами в кластере.
Машины в вашем кластере должны удовлетворять этим требованиям. (Даже если у вас только одна машина, вы должны синхронизировать ее время с NTP.)
- Время машин синхронизируется с помощью NTP или другого сетевого протокола времени.
- Имена хостов машин отличаются, чтобы модуль мог выполнить поиск имени хоста и получить разные IP-адреса для каждой машины в кластере.
Что касается предположений об операционной системе, мы предполагаем, что идентификаторы процессов (PID) помещаются в 32 бита. Если операционная система использует более 32 битов для PID, исправление тривиально, но должно быть выполнено в коде.
С учетом этих предположений в любой момент времени мы можем идентифицировать любой процесс httpd на любой машине в кластере от всех других процессов httpd. IP-адрес машины и PID процесса httpd достаточно для этого. Один процесс httpd может обрабатывать несколько запросов одновременно, если вы используете многопоточную MPM. Для идентификации потоков мы используем внутренний индекс потока, который использует Apache httpd. Таким образом, для генерации уникальных идентификаторов запросов нам нужно только различать различные моменты времени.
Для различения времени мы будем использовать метку времени Unix (секунды с момента 1 января 1970 года по UTC) и 16-битный счетчик. Метка времени имеет только разрешение в одну секунду, поэтому счетчик используется для представления до 65536 значений за одну секунду. Четверка ( ip_addr, pid, time_stamp, counter ) достаточно для перечисления 65536 запросов в секунду на каждый процесс httpd. Однако существуют проблемы с повторным использованием PID со временем, и счетчик используется для решения этой проблемы.
Когда создается дочерний процесс httpd, счетчик инициализируется с помощью (текущие микросекунды, деленные на 10) по модулю 65536 (эта формула была выбрана для устранения некоторых проблем с вариациями с младшими битами таймеров микросекунд на некоторых системах). При генерации уникального идентификатора используется отметка времени, соответствующая времени поступления запроса на веб-сервер. Счетчик увеличивается каждый раз при генерации идентификатора (и допускается переполнение).
Ядро генерирует PID для каждого процесса при его разветвлении и PID могут переполняться (они имеют 16 бит на многих Unix-системах, но более новые системы расширены до 32 бит). Таким образом, с течением времени тот же PID будет повторно использоваться. Однако если он повторно используется не в той же секунде, это не разрушает уникальность нашей четверки. То есть, мы предполагаем, что система не порождает 65536 процессов за один интервал в секунду (она может даже быть 32768 процессов на некоторых Unix-системах, но даже это маловероятно).
Предположим, что время повторяется по какой-либо причине. То есть, предположим, что часы системы сломаны и она возвращается к прошлому времени (или слишком сильно забегает вперед, корректно устанавливается, а затем посещает будущее время). В этом случае мы можем легко показать, что мы можем получить повторное использование PID и отметки времени. Выбор инициализатора для счетчика предназначен для помощи в борьбе с этим. Обратите внимание, что нам действительно нужен случайный номер для инициализации счетчика, но на большинстве систем нет легкодоступных чисел (например, вы не можете использовать rand(), потому что вам нужно инициализировать генератор, и вы не можете инициализировать его временем, потому что время, по крайней мере, с разрешением в секунду, повторилось). Это не идеальная защита.
Насколько хороша эта защита? Предположим, что одна из ваших машин обслуживает не более 500 запросов в секунду (что является очень разумной верхней границей на данный момент, потому что системы обычно делают больше, чем просто передают статические файлы). Для этого потребуется определенное количество дочерних процессов, которое зависит от того, сколько у вас одновременных клиентов. Но мы будем пессимистичны и предположим, что один дочерний процесс может обслуживать 500 запросов в секунду. Существует 1000 возможных начальных значений счетчика, таких что две последовательности из 500 запросов перекрываются. Таким образом, существует 1,5% вероятность того, что, если время (с разрешением в одну секунду) повторяется, этот дочерний процесс повторит значение счетчика, и уникальность будет нарушена. Это был очень пессимистичный пример, и с реальными значениями это еще менее вероятно. Если ваша система такова, что это все еще вероятно, возможно, вам следует сделать счетчик 32 бита (изменив код).
Вы можете беспокоиться о «переводе часов назад» во время летнего времени. Однако это не проблема, потому что здесь используются UTC-времена, которые «всегда» идут вперед. Обратите внимание, что системам x86 на базе Unix может потребоваться правильная настройка для того, чтобы это было правдой — они должны быть настроены так, чтобы предполагать, что часы материнской платы находятся в UTC, и соответствующим образом компенсировать. Но даже в этом случае, если вы используете NTP, ваше UTC-время будет правильным очень скоро после перезагрузки.
Переменная окружения UNIQUE_ID создается путем кодирования четверки из 144 бит (32-битный IP-адрес, 32-битный PID, 32-битная отметка времени, 16-битный счетчик, 32-битный индекс потока) с помощью алфавита [A-Za-z0-9@-] аналогичным образом, как кодирование MIME base64, что дает 24 символа. Алфавит MIME base64 фактически [A-Za-z0-9+/], однако + и / должны быть закодированы специальным образом в URL-адресах, что делает их менее предпочтительными. Все значения кодируются в сетевом порядке байтов, чтобы кодирование было сопоставимым на архитектурах с различным порядком байтов. Фактический порядок кодирования: отметка времени, IP-адрес, PID, счетчик. Этот порядок имеет цель, но следует подчеркнуть, что приложения не должны разбирать кодирование. Приложения должны рассматривать весь закодированный UNIQUE_ID как нерасшифрованный маркер, который может быть сравнен с другими UNIQUE_ID на равенство только.
Порядок был выбран таким образом, чтобы в будущем можно было изменить кодирование, не беспокоясь о столкновении с существующей базой данных UNIQUE_ID. Новые кодировки также должны хранить метку времени в качестве первого элемента и могут использовать тот же алфавит и длину в битах. Поскольку метки времени в основном представляют собой возрастающую последовательность, достаточно иметь флаг секунды, в котором все машины в кластере прекращают обслуживать любые запросы и прекращают использование старого формата кодирования. После этого они могут возобновить запросы и начать выдавать новые кодировки.
Мы считаем, что это относительно переносимое решение этой проблемы. Сгенерированные идентификаторы имеют по существу бесконечный срок действия, поскольку будущие идентификаторы могут быть сделаны длиннее по мере необходимости. В принципе не требуется никакой связи между машинами в кластере (требуется только синхронизация времени NTP, что является малой нагрузкой), и не требуется никакой связи между процессами httpd (связь подразумевается в значении PID, назначенном ядром). В очень специфических ситуациях идентификатор может быть сокращен, но необходимо сделать больше предположений (например, 32-битный IP-адрес избыточен для любого сайта, но нет переносимого более короткого замены для него).
© 2018 The Apache Software Foundation
Licensed under the Apache License, Version 2.0.
https://httpd.apache.org/docs/2.4/en/mod/mod_unique_id.html