Apache 1. Часть 5: Особенности архитектуры

Архитектура первого Apache имеет свои особенности — все функции имеют префикс ap_, реализация кроссплатформенности сделана на основе отдельных модулей, модули не могут общаться друг с другом, а только с ядром, все модули можно разбить на 7 основных групп: трансляция, аутентификация, MIME, fix-ups, генераторы контента, логирование, прокси. Управление памятью реализовано на основе пулов. Система конфигурация имеет широкие возможности для предварительной настройки сервера.

Сергей Яковлев, Консультант, независимый специалист

Яковлев Сергей — независимый разработчик с многолетним опытом прикладного и системного программирования; вносит вклад в развитие open-source на своем персональном сайте www.iakovlev.org. Консультант.



27.01.2011

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

1. Архитектура Apache 1.x

Клиентский запрос проходит в Apache несколько фаз:

  1. Запрашиваемый URL переводится в имя локального файла.
  2. Проверяется аутентификация пользователя.
  3. Проверяются права доступа.
  4. Определяется тип запрашиваемого документа: это может быть текст, картинка, звук, видео.
  5. Отсылается документ клиенту.
  6. Логирование.

Каждая фаза управляется собственным модулем Apache.

Управление клиентским запросом модулями Apache

На каждый запрос ядро Apache создает объект структуры request_rec, которую затем передает по очереди соответствующему модулю и получает ее в модифицированном варианте.

На следующем рисунке архитектура Apache представлена более детально. Есть небольшие функциональные модули — ap, regex, которые используются как ядром, так и другими базовыми модулями. Отдельный модуль OS реализует кроссплатформенность Apache.

Детальная архитектура Apache 1

При ближайшем рассмотрении дерева исходников выясняется:

  1. Все заголовки сгруппированы в каталоге include.
  2. Ядро лежит в каталоге main (libmain.a).
  3. Базовые модули лежат в каталоге modules (libstandard.a).
  4. Реализация кроссплатформенности находится в каталоге os, который включает в себя соответствующие подкаталоги, в каждом из которых лежит свой соответствующий заголовок os.h. (libos.a).
  5. Отдельный библиотечный компонент для работы с регулярными выражениями лежит в каталоге regex (libregex.a).
  6. Отдельный компонент лежит в каталоге ap и включает набор различных функций (libap.a).
  7. Отдельный компонент лежит в каталоге support и включает в себя скрипты и код для вспомогательных утилит Apache, таких как ротация лог файлов, манипуляция с файлами паролей и т.д.
  8. Каталог helpers включает скрипты, используемые при компиляции самого Apache.

Практически все функции имеют префикс ap_, это было введено в версии 1.3 для избежания конфликта имен при переходе с версии 1.2.


2. Архитектура ядра Apache 1

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

Архитектура ядра Apache 1
  • http_main.c включает основной серверный цикл, управление таймаутами и дочерними процессами. В цикле устанавливается TCP/IP соединение, выделяется память под клиентский запрос, читается запрос и вызывается функция из http_request.c для управления этим запросом, после чего соединение закрывается и память освобождается.
  • http_core.h описывает наиболее важные функции, обрабатывающие запрос.
  • mod_core.c определяет структуру каждого модуля.
  • http_core.c определяет таблицу стандартных конфигурационных команд, инициализацию самого ядра и управление фазами обработки запроса.
  • http_request.c инициализирует модули и другие компоненты ядра. В нем реализовано чтение конфигурационных файлов и вызов соответствующих табличных конфигурационных команд. Хэндлеры всех модулей по обработке запроса находятся в связном списке, и из него происходит выбор нужного хэндлера для обработки соответствующей фазы запроса.
  • http_protocol.c реализует HTTP протокол, управляет соединением с клиентом, закрывает коннект в случае ошибки. Для хранения информации о запросе есть текстовый заголовок и бинарный контент. Этот компонент ядра вызывается после установки коннекта, после его закрытия, а также в случае, когда документ готов для отправки клиенту.
  • Файлы buff.c, alloc.c управляют ресурсами, буферизуют ввод/вывод. Пул ресурсов — это кусок памяти, необходимый для управления запросом, включает в себя файловые дескрипторы. Каждый запрос имеет свой собственный пул, который может быть освобожден после обработки запроса.
  • http_log.c — управляет логированием, приоритетом сообщений, очередью сообщений.
  • http_chost.c — один сервер может работать с нескольких адресов в соответствии с прописанными в конфиге правами для каждого из них.

3. Модули Apache

Модули реализуют функционал Apache. Модули могут напрямую общаться с ядром, но не могут напрямую общаться друг с другом. Каждый модуль фактически является набором обработчиков (хэндлеров) для различных фаз HTTP запроса. Следующий рисунок дает детальный обзор архитектуры модуля. Модуль — это коллекция функций-обработчиков, вызываемых ядром. Каждый модуль имеет собственную структуру под названием module, в которой хранятся указатели на обработчики, и объект такой структуры каждый модуль передает ядру.

Модули Apache 1

Каждый модуль должен быть проинициализирован ядром. По идее, любой модуль может использовать команды, которые пользователь прописывает в конфигурационном файле. Чтением файла занимается ядро, которое затем сопоставляет их с таблицей команд, полученных от модуля в структуре module. Каждая строка в этой таблице состоит из пары: тип контента — указатель на обработчик, где тип контента — это MIME type.

Обработчики, которые занимаются непосредственной отсылкой данных клиенту, называются content handlers или response handlers. Для каждого типа объекта имеется свой собственный обработчик.

В Apache все модули сгруппированы в каталоге modules. Стандартные модули сгруппированы в подкаталоге standard. реализация прокси лежит в каталоге proxy. Демонстрационный модуль, имеющий всего один обработчик, лежит в каталоге examples.

Все стандартные модули загружаются статично, хотя возможна динамическая загрузка: их можно слинковать с ядром и так, и так. При инсталляции Apache, когда вы запускаете конфигурацию, создается файл modules.c, в котором определяются 2 массива указателей на модульные структуры:

module *ap_prelinked_modules[] = {
  &core_module,
  &env_module,
  &config_log_module,
  &mime_module,
  ...


dule *ap_preloaded_modules[] = {
  &core_module,
  &env_module,
  &config_log_module,
  &mime_module,
  &negotiation_module,
	...

При этом все перечисленные модули можно разбить на следующие группы:

  1. Трансляция URL
    • mod_userdir — перевод домашнего каталога в урл;
    • mod_rewrite — переписывает урл на базе регулярных выражений.
  2. Аутентификация / авторизация
    • mod_auth, mod_auth_anon,mod_auth_db, mod_auth_dbm — аутентификация пользователей на основе парольных файлов;
    • mod_access — контроль доступа.
  3. MIME
    • mod_mime — определение типа документа на основе файлового расширения;
    • mod_mime_magic — определение типа документа по его заголовку;
    • mod_negotiation.
  4. fix-ups
    • mod_alias — алиасы;
    • mod_env — переменные среды на основе конфигов;
    • mod_speling — исправление ошибок в урлах;
    • mod_expires, mod_headers.
  5. Отсылка данных клиенту
    • mod_actions, mod_asis, mod_autoindex, mod_cgi, mod_dir, mod_imap, mod_include, mod_info, mod_negotiation, mod_status, mod_core.
  6. Логирование
    • mod_log_
  7. Proxy
    • mod_proxy, proxy_cache, proxy_connect, proxy_ftp, proxy_http, proxy_util.

4. Параллелизм

При старте Apache создает (fork) по умолчанию 5 главных процессов, это минимальное возможное число процессов. Каждый процесс в свою очередь может поддерживать как минимум 50 потоков (thread — если многопоточность поддерживается операционной системой). На каждый клиентский запрос отводится один процесс. Существует специальная структура — scoreboard — которая хранит состояние всех процессов. Число одновременных возможных процессов по умолчанию — от 5 до 10. Максимальное число таких процессов — 256. Есть также специальная очередь для запросов на ожидание, которым пока нет места в scoreboard. Максимальная длина такой очереди — 511.

Максимально возможное число одновременных клиентских коннектов — 100.


5. Структуры данных

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

Временная диаграмма прохождения фаз клиентского запроса в Apache 1

При вызове обработчика каждому модулю передается в качестве параметра одна и та же публичная структура — request_rec. При этом может происходить изменение состояния различных полей этой структуры. Responce handlers возвращают контент клиенту. Обработчик также может выполнять другой под-запрос. Структура request_rec может включать указатель на другую структуру request_rec в том случае, когда происходит редирект, а в случае под-запроса указатель может указывать на самое себя. Редирект означает вызов обработчика картинки внутри документа или вызов CGI-скрипта. При этом вызывается обработчик ap_internal_redirect, который создает новый объект request_rec. Такой объект может быть помещен в связный список request_recs.

Структура request_rec содержит в себе следующие поля:

  • указатели на другие request_rec;
  • указатель на пул ресурсов;
  • обьекты запроса:
    • URI
    • filename
    • path
  • информация о контенте:
    • тип контента
    • кодировка
  • MIME headers;
  • информация о запросе:
    • протокол
    • метод

Ниже дано выборочное представление структуры request_rec с наиболее часто используемыми полями:

struct request_rec {

  pool *pool;
  conn_rec *connection;
  server_rec *server;

  /* запрос */
  
  char *uri;
  char *filename;
  char *path_info;
  char *args;			/* QUERY_ARGS */
  
  char *content_type;
  char *content_encoding;
  
  /* здесь представлен заголовок MIME  
   * также массив с переменными, передаваемый в дочерний процесс
   */
  
  table *headers_in;
  table *headers_out;
  table *err_headers_out;
  table *subprocess_env;

  /* информация о запросе  */
  
  int header_only;		/* HEAD — заголовок запроса */
  char *protocol;		/* Protocol */
  char *method;		/* GET, HEAD, POST. */
  int method_number;		/* M_GET, M_POST */

  /* логирование */

  char *the_request;
  int bytes_sent;

  /* флаг кеширования   */

  int no_cache;

  /* массивы конфигурационных параметров с указателями типа  void*   */
  
  void *per_dir_config;		
  void *request_config;		
  
};

Apache использует пул ресурсов — pool — структуру данных, в которой хранится список всех выделенных ресурсов для запроса. Когда обработка запроса заканчивается, все ресурсы, затраченные на этот запрос, освобождаются. У модулей могут быть свои собственные пулы на свои нужды.

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

  • имя команды;
  • указатель на функцию-обработчик;
  • передаваемый аргумент;
  • условия инициализации;
  • тип и число аргументов;
  • описание аргументов;

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

 SERVER_DEAD 0
 SERVER_STARTING 1       /* Server Starting up */
 SERVER_READY 2          /* Waiting for connection (or accept() lock) */
 SERVER_BUSY_READ 3      /* Reading a client request */
 SERVER_BUSY_WRITE 4     /* Processing a client request */
 SERVER_BUSY_KEEPALIVE 5 /* Waiting for more requests via keepalive */
 SERVER_BUSY_LOG 6       /* Logging the request */
 SERVER_BUSY_DNS 7       /* Looking up a hostname */
 SERVER_GRACEFUL 8       /* server is gracefully finishing request */
 SERVER_NUM_STATUS 9     /* number of status settings */

6. Обработчик response

Результат работы большинства обработчиков сводится к банальному изменению значения полей структуры request_rec, либо к возврату специальных кодов. Данный же обработчик отсылает реальные данные клиенту. Вначале клиенту отсылается HTTP-заголовок с помощью функции send_http_header. Если у запроса есть метка header_only, то клиенту более ничего не высылается. В противном случае, клиенту высылается само сообщение, для этого используются специальные примитивы rputc, rprintf, send_fd. Следующий код показывает обработку GET-запроса и отсылку данных клиенту:

int default_handler (request_rec *r)
{
    int errstatus;
    FILE *f;
    
    if (r->method_number != M_GET) return DECLINED;
    if (r->finfo.st_mode == 0) return NOT_FOUND;
	
    if ((errstatus = set_content_length (r, r->finfo.st_size))
	|| (errstatus = set_last_modified (r, r->finfo.st_mtime)))
        return errstatus;
    
    f = fopen (r->filename, "r");

    if (f == NULL) {
        log_reason("file permissions deny server access", r->filename, r);
        return FORBIDDEN;
    }
      
    register_timeout ("send", r);
    send_http_header (r);

    if (!r->header_only) send_fd (f, r);
    pfclose (r->pool, f);
    return OK;
}

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


7. Пул

Основная проблема, решаемая с помощью пула — pool — это предотвращение утечек памяти.

Пул в Apache разработан таким образом, что освобождение всех ресурсов — памяти, дескрипторов и т.д. — происходит автоматически, после завершения обработки клиентского запроса. Каждому такому запросу выделяется свой собственный пул, представленный структурой pool. После обработки этот пул удаляется.

При старте Apache выделятся специальный пул — конфигурационный. При перезапуске сервера этот пул также очищается.

Память выделяется с помощью palloc, что быстрее, чем malloc. Эта функция имеет 2 аргумента — указатель на пул и количество выделяемой памяти:

int my_handler(request_rec *r)
{
    struct my_structure *foo;
    ...

    foo = (foo *)palloc (r->pool, sizeof(my_structure));
}

Функция pfree отсутствует, поскольку освобождение памяти идет автоматически тогда, когда освобождается пул. Есть специальные прикладные функции, выделяющие память : например, в следующем примере функция pstrcat выделяет память под 8 байтовый строковый модуль:

	pstrcat (r->pool, "foo", "/", "bar", NULL);

Аналогично происходит открытие файлов:

     FILE *f = pfopen (r->pool, r->filename, "r");

В отличие от работы с памятью, здесь есть соответствующая закрывающая функция pfclose.

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

Пул также можно освободить в любой момент с помощью clear_pool или destroy_pool.

В случае подзапросов для надежного очищения памяти используется destroy_sub_request.


8. Конфигурация

Первый Apache разрабатывался с прицелом на максимальную совместимость со своим предшественником — вебсервером NCSA 1.3 — в плане чтения конфигурационных файлов. Была поставлена задача вынести максимально весь функционал из ядра в модули. Для этого в ядре была создана специальная таблица команд. Определения типа файла по суффиксу делается на основе конфигурационных директив AddType и DefaultType.

Работа с файловой системой выполняется с помощью директив Aliases и Redirect.

Работа с вложенными конфигурационными файлами выполняется на базе файлов .htaccess.

Когда сервер читает в файле директиву <Directory>, он создает структуру mime_dir_config:

typedef struct {
    table *forced_types;	/*  AddType */
    table *encoding_types;	/* AddEncoding */
} mime_dir_config;

Команды AddType и AddEncoding обычно присутствуют в .htaccess.

Для создания такой структуры нужны 2 параметра — пул и имя каталога:

void *create_mime_dir_config (pool *p, char *dummy)
{
    mime_dir_config *new =
      (mime_dir_config *) palloc (p, sizeof(mime_dir_config));

    new->forced_types = make_table (p, 4);
    new->encoding_types = make_table (p, 4);
    
    return new;
}

Если в нашем серверном каталоге, который мы только что прочитали, есть вложенный каталог, и там есть свой собственный .htaccess, нам придется объединять две структуры:

void *merge_mime_dir_configs (pool *p, void *parent_dirv, void *subdirv)
{
    mime_dir_config *parent_dir = (mime_dir_config *)parent_dirv;
    mime_dir_config *subdir = (mime_dir_config *)subdirv;
    mime_dir_config *new =
      (mime_dir_config *)palloc (p, sizeof(mime_dir_config));

    new->forced_types = overlay_tables (p, subdir->forced_types,
					parent_dir->forced_types);
    new->encoding_types = overlay_tables (p, subdir->encoding_types,
					  parent_dir->encoding_types);

    return new;
}

Заключение

Архитектура первого Apache имеет свои особенности — все функции имеют префикс ap_, реализация кроссплатформенности сделана на основе отдельных модулей, модули не могут общаться друг с другом, а только с ядром, все модули можно разбить на 7 основных групп: трансляция, аутентификация, MIME, fix-ups, генераторы контента, логирование, прокси. Управление памятью реализовано на основе пулов. Система конфигурация имеет широкие возможности для предварительной настройки сервера.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


  • Bluemix

    Узнайте больше информации о платформе IBM Bluemix, создавайте приложения, используя готовые решения!

  • developerWorks Premium

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

  • Библиотека документов

    Более трех тысяч статей, обзоров, руководств и других полезных материалов.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source, Linux
ArticleID=620084
ArticleTitle=Apache 1. Часть 5: Особенности архитектуры
publish-date=01272011