Вопрос

Я разработал мини-HTTP-сервер на C ++, используя boost:: asio, и теперь я тестирую его под нагрузкой с несколькими клиентами, и мне не удалось приблизиться к насыщению процессора.Я тестирую на экземпляре Amazon EC2 и получаю около 50% загрузки одного процессора, 20% другого, а остальные два простаивают (согласно htop).

Подробные сведения:

  • Сервер запускает по одному потоку на ядро
  • Запросы принимаются, анализируются, обрабатываются, и ответы выписываются
  • Запросы предназначены для данных, которые считываются из памяти (доступны только для чтения для этого теста).
  • Я "загружаю" сервер, используя две машины, на каждой из которых запущено java-приложение, запущено 25 потоков, отправляются запросы
  • Я вижу пропускную способность около 230 запросов в секунду (это применение запросы, которые состоят из множества HTTP-запросов)

Итак, на что мне следует обратить внимание, чтобы улучшить этот результат?Учитывая, что процессор в основном простаивает, я бы хотел использовать эту дополнительную емкость, чтобы получить более высокую пропускную способность, скажем, 800 запросов в секунду или что-то еще.

Идеи, которые у меня были:

  • Запросы очень маленькие и часто выполняются за несколько мс, я мог бы модифицировать клиент для отправки / составления больших запросов (возможно, используя пакетную обработку)
  • Я мог бы изменить HTTP-сервер, чтобы использовать шаблон проектирования Select, подходит ли это здесь?
  • Я мог бы провести некоторое профилирование, чтобы попытаться понять, что является узким местом
Это было полезно?

Решение

boost::asio не так удобен для потоков, как вы надеялись бы - в boost / asio /detail/epoll_reactor.hpp есть большая блокировка вокруг кода epoll, что означает, что только один поток может одновременно вызывать системный вызов epoll ядра.И для очень маленьких запросов это имеет решающее значение (что означает, что вы увидите только приблизительную однопоточную производительность).

Обратите внимание, что это ограничение того, как boost::asio использует возможности ядра Linux, не обязательно само ядро Linux.Системный вызов epoll поддерживает несколько потоков при использовании событий, запускаемых по краю, но сделать это правильно (без чрезмерной блокировки) может быть довольно сложно.

Кстати, я проделал некоторую работу в этой области (объединив полностью многопоточный цикл событий epoll, инициируемый edge, с запланированными пользователем потоками / волокнами) и сделал некоторый код доступным в nginetd ( нгинетд ) проект.

Другие советы

Поскольку вы используете EC2, все ставки отменены.

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

Я еще не разобрался, для чего полезен EC2, если кто-нибудь узнает, пожалуйста, дайте мне знать.

Из ваших комментариев об использовании сети,
Похоже, у вас не так много сетевого движения.

3 + 2.5 MiB/sec находится во всем 50Mbps максимальная скорость (по сравнению с вашим портом 1 Гбит/с).

Я бы сказал, что у вас возникла одна из следующих двух проблем,

  1. Недостаточная рабочая нагрузка (низкий уровень запросов от ваших клиентов)
    • Блокировка на сервере (генерация ответа с помехами)

Глядя на cmeerwпримечания пользователя и показатели загрузки вашего процессора
(работает на холостом ходу при 50% + 20% + 0% + 0%)
скорее всего, это ограничение в вашей серверной реализации.
Я второй cmeerwответ автора (+1).

230 запросов в секунду кажутся очень низкими для таких простых асинхронных запросов.Таким образом, использование нескольких потоков, вероятно, является преждевременной оптимизацией - заставьте его работать должным образом и настройте в одном потоке и посмотрите, нужны ли они вам по-прежнему.Простое избавление от ненужной блокировки может ускорить процесс.

Эта статья содержит некоторые подробности и обсуждение стратегий ввода-вывода для производительности в стиле веб-сервера примерно в 2003 году.У кого-нибудь есть что-нибудь более свежее?

ASIO хорош для небольших и средних задач, но он не очень хорош в использовании возможностей базовой системы.Это также не необработанные вызовы сокетов или даже IOCP в Windows, но если у вас есть опыт, вы всегда будете лучше, чем ASIO.В любом случае, все эти методы сопряжены с большими накладными расходами, только больше с ASIO.

Чего бы это ни стоило.использование необработанных вызовов сокетов на моем пользовательском HTTP может обслуживать 800 тыс. динамических запросов в секунду с 4-ядерным I7.Он работает из оперативной памяти, именно там вам и нужно находиться для такого уровня производительности.При таком уровне производительности сетевой драйвер и операционная система потребляют около 40% процессора.Используя ASIO, я могу получать от 50 до 100 тысяч запросов в секунду, его производительность довольно изменчива и в основном ограничена в моем приложении.Сообщение @cmeerw в основном объясняет, почему.

Одним из способов повысить производительность является внедрение UDP-прокси.Перехватывая HTTP-запросы и затем направляя их по UDP на ваш внутренний UDP-HTTP-сервер, вы можете обойти большую нагрузку TCP в стеках операционной системы.У вас также могут быть интерфейсы, которые сами подключаются к UDP, что не должно быть слишком сложно сделать самостоятельно.Преимущество HTTP-UDP прокси заключается в том, что он позволяет вам использовать любой хороший интерфейс без изменений, и вы можете менять их местами по своему усмотрению без каких-либо последствий.Вам просто нужна еще пара серверов, чтобы реализовать это.Эта модификация в моем примере снизила загрузку процессора ОС до 10%, что увеличило количество моих запросов в секунду до чуть более миллиона на этом единственном сервере.И, FWIW, у вас всегда должна быть настройка frontend-backend для любого высокопроизводительного сайта, потому что интерфейсы могут кэшировать данные, не замедляя работу более важных серверных систем динамических запросов.

Будущее, похоже, за написанием вашего собственного драйвера, который реализует свой собственный сетевой стек, чтобы вы могли максимально приблизиться к запросам и реализовать там свой собственный протокол.Это, вероятно, не то, что большинство программистов хотят услышать, поскольку это более сложно.В моем случае я смог бы использовать на 40% больше процессора и выполнять более 1 миллиона динамических запросов в секунду.Метод UDP proxy может приблизить вас к оптимальной производительности без необходимости делать это, однако вам потребуется больше серверов - хотя, если вы выполняете такое количество запросов в секунду, вам обычно потребуется несколько сетевых карт и несколько интерфейсов для обработки пропускной способности, поэтому наличие пары легких UDP-прокси не так уж и важно.

Надеюсь, что кое-что из этого может быть вам полезно.

Сколько экземпляров io_service у вас есть?Boost asio имеет пример это создает io_service для каждого процессора и использует их в виде RoundRobin.

Вы все еще можете создать четыре потока и назначить по одному для каждого процессора, но каждый поток может проводить опрос в своем собственном io_service.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top