В этой статье мы в основном хотим протестировать и изучить несколько моментов:
- Простейшая программа для пересылки HTTP-запросов на основе Netty, разница между производительностью четырех слоев и семи слоев
- Разница в производительности трех моделей прокси-потоков будет подробно объяснена ниже.
- Разница в производительности ByteBuffer в пуле и без пула
Код, используемый в этом тесте:GitHub.com/Джозеф З Тайгер19…
В коде мы реализуем два набора агентов:
Конфигурация компьютера, использованная для теста, (Alibaba Cloud ECS):
Всего три машины:- На серверном сервере установлен nginx в качестве бэкенда.
- На клиентском сервере установлен wrk в качестве клиента стресс-теста.
- Прокси-сервер установил наш тестовый код (прокси)
Серверная часть Nginx
nginx настраивает тестовую страницу по умолчанию (некоторый контент удален, чтобы уменьшить пропускную способность интрасети):
Количество запросов в секунду, измеренное непосредственно против nginx, составляет 266 000:О четвертом и седьмом этажах
Для четырехуровневого прокси мы просто используем Netty для пересылки ByteBuf. Семиуровневый прокси будет иметь больше дополнительных накладных расходов, в основном кодирование и декодирование запросов Http и агрегирование запросов Http.Сервер:
Клиент:
Здесь мы можем думать, что производительность четырехслойного прокси определенно намного лучше, чем у семислойного из-за отсутствия процесса кодирования и декодирования данных Http.Мы можем видеть результаты тестирования.
О потоковой модели
Мы знаем, что в качестве прокси нам нужно запустить сервер, чтобы получить запрос от восходящего потока, а затем переслать запрос нисходящему потоку в качестве клиента, получить ответ от нисходящего потока и вернуть его восходящему. И нашему серверу, и клиенту нужны рабочие потоки для обработки запросов ввода-вывода.Есть три подхода;
- A: Независимый пул потоков NioEventLoopGroup клиента Bootstrap и сервера ServerBootstrap, именуемый IndividualGroup.
- B: клиент и сервер совместно используют набор пулов потоков, называемый ReuseServerGroup.
- C: клиент напрямую повторно использует серверный поток EventLoop, называемый ReuseServerThread.
В качестве примера возьмем код семислойного прокси:
В следующем тесте мы проверим эти три модели потоков. Естественно предположить, что производительность плана А является лучшей, поскольку независимые пулы потоков не влияют друг на друга. Давайте посмотрим на результаты далее.
Четырехуровневый агент + потоковая модель ReuseServerThread
Layer4ProxyServer Started with config: ServerConfig(type=Layer4ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerThread, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
Четырехуровневый агент + модель потоков IndividualGroup
Layer4ProxyServer Started with config: ServerConfig(type=Layer4ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=IndividualGroup, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
Четырехуровневый агент + модель потока ReuseServerGroup
Layer4ProxyServer Started with config: ServerConfig(type=Layer4ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerGroup, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
Видя, что здесь уже есть результаты, ReuseServerThread имеет лучшую производительность, за ней следует ReuseServerGroup, а худшая — IndividualGroup, что не согласуется с нашим предположением.
Диаграмма мониторинга четырехуровневой системы
Как видно из пропускной способности сети, протестированный ReuseServerThread сначала работал с максимальной пропускной способностью (последние три пика представляют три теста соответственно):
Из мониторинга ЦП видно, что наиболее производительный ReuseServerThread использует наименьшее количество ресурсов ЦП (последние три пика представляют три теста соответственно):Семиуровневый агент + потоковая модель ReuseServerThread
Layer7ProxyServer Started with config: ServerConfig(type=Layer7ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerThread, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
Семиуровневый агент + модель потоков IndividualGroup
Layer7ProxyServer Started with config: ServerConfig(type=Layer7ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=IndividualGroup, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
Семиуровневый агент + модель потока ReuseServerGroup
Layer7ProxyServer Started with config: ServerConfig(type=Layer7ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerGroup, receiveBuffer=10240, sendBuffer=10240, allocatorType=Unpooled, maxContentLength=2000)
В заключение ReuseServerThread имеет лучшую производительность, за ней следует ReuseServerGroup, а худшая — IndividualGroup. Я думаю, что это имеет смысл следующим образом:
- При повторном использовании потока ввода-вывода переключение контекста будет меньше, а производительность будет лучшей.Позже я также проверил этот вывод с помощью наблюдения pidstat, но я забыл сделать скриншот в то время.
- Повторно используйте пул потоков, клиент имеет возможность повторно использовать поток сервера, избегая некоторого переключения контекста, а производительность умеренная.
- Независимый пул потоков, много переключений контекста (наблюдается 4-кратное мультиплексирование потоков ввода-вывода), худшая производительность
Схема мониторинга семиуровневой системы
Ниже приведены графики мониторинга пропускной способности сети и ЦП:
Видно, что семислойный прокси потребляет больше ресурсов, но пропускная способность меньше, чем у четырехслойного (намного меньше QPS). Исходящий трафик чуть больше входящего, что должно быть вызвано дополнительными заголовками запросов в коде:Попробуйте HttpObjectAggregator установить большую maxContentLength
Layer7ProxyServer Started with config: ServerConfig(type=Layer7ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerThread, receiveBuffer=10240, sendBuffer=10240, allocatorType=Pooled, maxContentLength=100000000)
Попробуйте пуледбайтбуфаллокатор
Layer7ProxyServer Started with config: ServerConfig(type=Layer7ProxyServer, serverIp=172.26.5.213, serverPort=8888, backendIp=172.26.5.214, backendPort=80, backendThreadModel=ReuseServerThread, receiveBuffer=10240, sendBuffer=10240, allocatorType=Pooled, maxContentLength=2000)
Вы можете видеть, что распределитель по умолчанию был установлен на PooledByteBufAllocator в Netty 4.1.
Суммировать
Вот краткое изложение таблицы, коэффициент потери производительности основан на первой строке, напрямую использующей Nginx в качестве эталона:
Вывод таков:
- Nginx очень хорош. На самом деле, конфигурация машины не очень хороша. Его можно запустить на физическом сервере с лучшей конфигурацией. Nginx не проблема для одной машины.
- Netty очень хороша, ведь это Java-сервер, четырехуровневый форвардинг теряет всего 3% QPS.
- Будь то семь слоев или четыре уровня, способ мультиплексирования потоков, очевидно, имеет наилучшую производительность и занимает меньше всего ресурсов процессора.
- Из-за переключения контекста использование Netty для разработки сетевых агентов должно повторно использовать потоки ввода-вывода.
- Потребление семи слоев гораздо больше, чем четырех, даже Netty не может этого избежать, это проблема с HTTP-протоколом.
- Производительность PooledByteBufAllocator лучше, чем у UnpooledByteBufAllocator (около 3%).
- Если HttpObjectAggregator задает большую максимальную длину содержимого, это немного повлияет на производительность.
Причина, по которой я написал эту статью для этого анализа, заключается в том, что недавно я занимался оптимизацией производительности и нагрузочным тестированием нашего шлюза собственной разработки.GitHub.com/spring-av и…. Я обнаружил, что есть некоторые другие прокси-проекты на основе Netty с открытым исходным кодом, которые не используют соединения повторно. Возможно, автор не осознал эту проблему. Я посмотрел код Zuul, и он также был повторно использован.