Кейс · высоконагруженное сетевое программное обеспечение

Высоконагруженный HTTP-сервер на C++

Учебно-исследовательский HTTP-сервер на C++: вместо потока на каждого клиента он распределяет тысячи соединений между несколькими рабочими потоками и с помощью системного вызова select() определяет, какие из них готовы к обмену данными. Нагрузочные испытания показали конкретные пределы — до 1024 соединений, около 220 МБ/с и ~3500 запросов в секунду.

Роль — разработал и исследовалТип — HTTP-сервер на C++Сфера — исследовательский проект

Главный результат

Один процесс с фиксированным числом рабочих потоков стабильно обслуживает до 1024 одновременных клиентов — вместо создания отдельного потока на каждое соединение.

01Проблема

Почему обычный сервер не выдерживает тысячи клиентов

Поток на каждого клиента

Классическая модель заводит отдельный поток на соединение — на тысячах клиентов это расходует память и время планировщика.

Ресурсы кончаются раньше нагрузки

Сервер исчерпывает ресурсы на переключении контекстов и потреблении памяти задолго до реального предела канала.

Пределы неизвестны

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

Небезопасная отдача файлов

Наивная отдача статических файлов открывает доступ к посторонним файлам и падает на больших ответах.

02Задача

Что нужно было сделать

Написать веб-сервер для отдачи статических файлов — страниц, оформления, изображений — который обслуживает множество клиентов небольшим фиксированным числом потоков, а не выделяет поток на каждого. И измерить его реальные пределы под нагрузкой.

03Решение

Что я сделал

Построил сервер на неблокирующем вводе-выводе: вместо потока на клиента фиксированное число рабочих потоков распределяют между собой соединения и с помощью системного вызова select() опрашивают их на готовность к чтению и записи. Это экономит ресурсы компьютера на тысячах подключений.

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

04Как это работает

Как сервер обрабатывает соединения

01

Принимает соединения

Складывает входящие подключения в общий пул, не заводя поток на каждое.

02

Ждёт готовности

Опрашивает соединения вызовом select() и обрабатывает только те, что готовы к чтению или записи.

03

Отдаёт данные

Разбирает запрос и потоково передаёт статический файл, не загружая его целиком в память.

04

Освобождает

Закрывает соединение и возвращает рабочий поток в пул для следующего клиента.

05Возможности

Что умеет сервер

Мультиплексирование соединений

Опрашивает соединения одним вызовом select() и обслуживает множество клиентов небольшим фиксированным числом потоков.

Потоковая отдача файлов

Передаёт большие статические файлы порциями, без полной загрузки в память.

Основные возможности HTTP

Разбор запросов, корректные ответы и отдача статических файлов: страниц, стилей, изображений.

Защита от атак

Отражает типичные атаки и попытки доступа к посторонним файлам.

Нагрузочное тестирование

Серия испытаний с графиками и сравнением разных подходов к обработке соединений.

Экономия ресурсов

Никакого потока на клиента — минимум переключений контекста и памяти под нагрузкой.

06Проверка нагрузкой

Пределы определены экспериментально

Сервер прошёл серию нагрузочных испытаний, которые показали конкретные пределы: он стабильно держит до 1024 одновременных подключений — этот предел задан ограничением системного вызова select() на число отслеживаемых дескрипторов, — обеспечивает скорость передачи около 220 МБ/с и справляется примерно с 3500 запросами в секунду до начала снижения качества обслуживания.

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

Тест точки деградации — запросы в секунду и время отклика при росте нагрузки
07Скриншоты

Как это выглядит

08Результат

Что изменилось

Было

  • Поток на каждого клиента перегружает память и планировщик
  • Ресурсы кончаются задолго до предела канала
  • Реальные пределы сервера — неизвестны

Стало

  • Тысячи клиентов на фиксированном числе потоков
  • Большие файлы отдаются потоково, без загрузки в память
  • Пределы измерены: 1024 соединения, ~220 МБ/с, ~3500 запросов/с

Сервер показал, что модель с мультиплексированием соединений через select() и неблокирующим вводом-выводом обслуживает тысячи клиентов ограниченным числом потоков — с измеренными пределами и защитой отдачи файлов.

Хотите такой же результат?

Опишите идею или задачу — разберу бесплатно и предложу решение, сроки и смету. Ни к чему не обязывает.

Отвечу в течение 24 часов