Для начала расскажу, как обычно работают сервера:
- Создаётся сокет, который слушает порт
- Вызывается системный вызов accept, который берёт первого клиента из очереди соединений, а если его нет – то блокирует приложение до тех пор, пока он не появится
- Приложение что-то делает с этим клиентом и в конце концов с ним прощается
- goto пункт два
nginx и lighttpd использует вместо accept функции select/kevent/epoll, которые позволяют работать со многими клиентами одновременно, но это уже отдельная история (к асинхронным веб-серверам accept-фильтры применимяются точно так же)
Accept-фильтры – это модули ядра FreeBSD, которые не говорят, что сокет готов до тех пор, пока не выполнится какое-то условие. Я нашёл только два фильтра:
- dataready – accept блокирует сокет до тех пор, пока в него прийдут данные. Когда мы получаем сокет, мы уже точно можем из него что-то считать
- httpready – сокет блокируется до тех пор, пока не прийдёт полный GET/HEAD запрос. Соответственно, нам не надо тратить время на переключение контекста, если запрос ещё не собрался.
Остановимся на фильтре httpready
Буферизирует он только GET и HEAD запросы, так как в POST/PUT может прийти 600мегабайтный порнофильм.
Итак, подключаем модуль accf_http:
kldload accf_http
echo 'accf_http_load=”YES”' >> /boot/loader.confДалее нужно изменить работу с сокетом — установить опцию httpready:
struct accept_filter_arg afa; bzero(&afa, sizeof(afa)); strcpy(afa.af_name, "httpready"); setsockopt(sok, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
Результаты тестов
Так как существует мнение, что accept-фильтры бесполезны, мы решили провести тестирование. Веб-сервер был выбран AnyEvent::HTTPD, клиент – ab. Итак, результаты:
- локальная машина, 10000 запросов, concurrency = 4: httpready даёт прирост в 10%
- локальная сеть, 10000 запросов, concurrency=1000: httpready даёт прирост в 10%
- локальная сеть, 10000 запросов, concurrency = 4: прирост в 25%
- локальная сеть, 1000 запросов, concurrency = 1: в два раза!
В боевых условиях, при наличии GPRS, 256k и прочего позора цивилизации, скорость ещё увеличится.
Настройка веб-серверов
В apache 1.3+ и nginx accept-фильтры поддерживаются.
Apache под FreeBSD изначально содержит такую конфигурацию:
AcceptFilter http httpready AcceptFilter https dataready
и менять её не стоит.
А вот в настройках сервера nginx стоит добавить опцию
accept_filter "httpready";
Ссылки
- статья Игоря Сысоева
- man accf_http
- man accf_data
Спасибо, очень интересно.