Библиотека сайта rus-linux.net
Библиотека Warp
Глава 11 из книги "Производительность приложений с открытым исходным кодом".
Оригинал: Warp
Авторы: Kazu Yamamoto, Michael Snoyman, Andreas Voellmy
Перевод: Н.Ромоданов
Композер HTTP-ответа
В этом разделе описывается композер HTTP-ответа библиотеки Warp. Ответ Response
в WAI имеет три конструктора:
ResponseFile Status ResponseHeaders FilePath (Maybe FilePart) ResponseBuilder Status ResponseHeaders Builder ResponseSource Status ResponseHeaders (Source (ResourceT IO) (Flush Builder))
Конструктор ResponseFile
используется для отправки статического файла, тогда как конструкторы ResponseBuilder
и ResponseSource
предназначены для отправки динамического содержимого, созданного в памяти. В состав каждого конструктора входят составляющие Status
и ResponseHeaders
. ResponseHeaders
определяется как список пар заголовка ключ/значение.
Композер заголовка HTTP-ответа
Старый композер собирал заголовок ответа HTTP с использованием структуры Builder
, структуры данных, работа с которой походила на плетение веревки. Во-первых, Status
и каждый элемент ResponseHeaders
он преобразовывался в Builder
. Каждое преобразование работает за O(1). Затем с помощью добавления еще одного Builder
происходило их объединение их друг к другу. Благодаря свойствам Builder
, каждая операция добавления также выполнялась за время O(1). Наконец, за время O(N) при помощи копирования данных из структуры Builder
в буфер происходила упаковка заголовка HTTP-ответа.
Во многих случаях, производительность структуры Builder
была достаточной. Но на практике мы убедились, что ее недостаточно для высокопроизводительных серверов. Для устранения накладных расходов, связанных с Builder
, мы для заголовков HTTP-ответов реализовали специальный композер, в котором используется memcpy()
, очень хорошо настроенная функция языка С, предназначенная для копирования байтов.
Композер тела HTTP-ответа
Для конструкторов ResponseBuilder
и ResponseSource
, значения Builder
, передаваемые приложением, упакованы в список строк типа ByteString
. Собранный заголовок добавляется к началу списка и для того, чтобы отправить список в буфер. используется команда send()
.
В случае конструктора ResponseFile
, Warp для того, чтобы отправить заголовок и тело HTTP-ответа, использует команды send()
и sendfile()
, соответственно. На рис.11.7 проиллюстрирована эта ситуация. Опять же, благодаря механизму кэширования, описанному в разделе «Таймеры дескрипторов файлов» можно не выполнять некоторые системные вызовы open()
, stat()
, close()
и другие. В следующем подразделе описывается еще один вариант настройки производительности в случае использования конструктора ResponseFile
.
Совместная отсылка заголовка и тела
Когда мы измеряли производительность Warp при отправке статических файлов, мы всегда выполняли это с высокой степенью распараллеливания (несколько подключений одновременно) и достигали хороших результатов. Однако, когда мы установили значение распараллеливания, равным 1, мы обнаружили, что Warp работает очень медленно.
Наблюдая за результатами работы команды tcpdump
, мы поняли, что это происходит из-за того, что первоначально в Warp используется комбинация команды writev()
, применяемой для заголовка HTTP, и команды sendfile()
, применяемой для тела HTTP. В этом случае заголовок и тело HTTP посылаются в отдельных пакетах TCP (рис. 11.11).
Рис.11.11: Последовательность пакетов в старом варианте библиотеки Warp
Для того, чтобы отправить их в одном пакете TCP (когда это возможно), в новой версии Warp мы перешли от команды writev()
к команде send()
. В новом варианте для запоминания заголовка используется команда send()
с флагом MSG_MORE
, а для отправки запомненного заголовка и файл используется команда sendfile()
. Это сделало пропускную способность, по крайней мере, в 100 раз выше в сравнении с пропускной способностью нашего первоначального варианта.
Продолжение статьи: Очистка с помощью таймеров.