Повышение Beast 1 секунда задержки в ответе сервера

Я пытаюсь создать HTTP-сервер, используя boost :: beast, но заметил время отклика в одну секунду. Я использовал расширенный пример сервера и пример синхронного клиента, чтобы сравнить это поведение.

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

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

Кажется, это связано с boost :: asio сокетами, потому что я также создал HTTP-сервер некоторое время назад (с boost :: asio — Boost v1.64, я думаю, когда Beast не было рядом), и я экспериментирую с тем же высоким временем отклика — это причина, по которой я перешел на Зверя.

Мои вопросы:

Это известное поведение для boost :: asio сокетов?

Можно ли устранить эту задержку?

Есть ли причина, по которой установка no_delay для сокета может не работать?

Результат теста:

Среднее время ответа от примера сервера было между 1005 и 1020 мс, как показано на скриншоте ниже.
Напротив, тот же клиент получает ответ от google.com менее чем за 120 мс.

Вот сравнение запросов на мой локальный сервер с запросами на www.google.com

Отсюда вопрос: откуда может возникнуть эта задержка в 900 мс?
Это не приемлемое время ответа для любого HTTP-сервера.

А также попробовал быстрый пример сервера и пример синхронного сервера с тем же результатом: более 1000 мс времени отклика.

Настройка бенчмарка

Тестовая система:

  • Windows 10 Pro x64
  • Процессор Intel i7-7700, 16 ГБ оперативной памяти
  • Boost 1.66
  • Visual Studio 2015

Протестировано в x86 и x64 со сборками Debug и Release. Единственное отличие заключалось в дополнительной задержке сборки Debug на 20-30 мс.

Настройка сервера

  • Я взял расширенный пример сервера из документации зверя
  • Создан новый пустой консольный проект в Visual Studio 2015
  • Добавлен пример кода в новый исходный файл cpp
  • В свойствах проекта> Каталоги VC ++> Включить каталоги установите boost «… boost \ include» include folder
  • В Свойствах проекта> Каталоги VC ++> Каталоги библиотек установите папку библиотеки повышения
  • В C / C ++> Препроцессор добавил «_WIN32_WINNT = 0x0601»
  • В «Отладка> Командные аргументы» я добавил «0.0.0.0 8080. 3», что означает «привязать сервер ко всем локальным адресам с портом 8080, используя. (Текущий каталог) в качестве корневой папки и запустить сервер в 3 потоках».

Настройка клиента

  • я использовал синхронный клиент из документации зверя
  • Создан аналогичный проект Visual Studio

Чтобы измерить время запроса, я добавил таймеры в коде клиента и закомментировал вывод ответа на cout:

auto start = std::chrono::steady_clock::now();
/* client code */

// printing the response to cout can slow the client
//std::cout << res << std::endl;

auto finish = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(finish - start);
std::cout << "request time: " << ms.count() << "\n";

Это код клиента, который отключает задержку TCP:

boost::asio::connect(socket, results.begin(), results.end());
socket.set_option(tcp::no_delay(true));

Это единственная модификация примера кода сервера.
И код сервера для отключения задержки TCP:

void
on_accept(boost::system::error_code ec)
{
if(ec)
{
fail(ec, "accept");
}
else
{
// disable TCP delay
socket_.set_option(tcp::no_delay(true));

// Create the http_session and run it
std::make_shared<http_session>(
std::move(socket_),
doc_root_)->run();
}

// Accept another connection
do_accept();
}

Вот модифицированный клиент:

//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//

//------------------------------------------------------------------------------
//
// Example: HTTP client, synchronous
//
//------------------------------------------------------------------------------

//[example_http_client

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <chrono>

using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>
namespace http = boost::beast::http;    // from <boost/beast/http.hpp>

// Performs an HTTP GET and prints the response
int main(int argc, char** argv)
{
try
{
// Check command line arguments.
if(argc != 4 && argc != 5)
{
std::cerr <<
"Usage: http-client-sync <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
"Example:\n" <<
"    http-client-sync www.example.com 80 /\n" <<
"    http-client-sync www.example.com 80 / 1.0\n";
return EXIT_FAILURE;
}
auto const host = argv[1];
auto const port = argv[2];
auto const target = argv[3];
int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11;

auto start = std::chrono::steady_clock::now();

// The io_context is required for all I/O
boost::asio::io_context ioc;

// These objects perform our I/O
tcp::resolver resolver{ioc};
tcp::socket socket{ioc};

// Look up the domain name
auto const results = resolver.resolve(host, port);

// Make the connection on the IP address we get from a lookup
boost::asio::connect(socket, results.begin(), results.end());
//socket.set_option(tcp::no_delay(true));

// Set up an HTTP GET request message
http::request<http::string_body> req{http::verb::get, target, version};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);

// Send the HTTP request to the remote host
http::write(socket, req);

// This buffer is used for reading and must be persisted
boost::beast::flat_buffer buffer;

// Declare a container to hold the response
http::response<http::dynamic_body> res;

// Receive the HTTP response
http::read(socket, buffer, res);

// Write the message to standard out
//std::cout << res << std::endl;

// Gracefully close the socket
boost::system::error_code ec;
socket.shutdown(tcp::socket::shutdown_both, ec);

// not_connected happens sometimes
// so don't bother reporting it.
//
if(ec && ec != boost::system::errc::not_connected)
throw boost::system::system_error{ec};

// If we get here then the connection is closed gracefully
auto finish = std::chrono::steady_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(finish - start);
std::cout << "request time: " << ms.count() << "\n";

}
catch(std::exception const& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

//]

3

Решение

Я попытался запустить отладочную версию вашей программы для отладочной версии примера с расширенными серверами в Visual Studio 2017 и Windows 10. Это был вывод:

request time: 5

Я думаю, что 5 миллисекунд довольно разумно 🙂

Попробуйте инициализировать время начала непосредственно перед вызовом http::read:

// Receive the HTTP response
auto start = std::chrono::steady_clock::now();
http::read(socket, buffer, res);

Таким образом, мы можем выяснить, выполняется ли поиск DNS медленно (возможно).

Я не думаю, что TCP_NODELAY вообще поможет, добавляемая задержка минимальна, а в Windows я считаю, что устройство обратной связи не реализует его. Задержка, которую вы видите, намного больше, чем может навязать алгоритм Nagle.

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

4

Другие решения

Теперь глупо думать, как легко это исправить 🙂

Публикация здесь для тех, кто интересуется, что является решением:
замените «localhost» на IP, например, «127.0.0.1» при отправке запроса от клиента.

Вот новые времена ответа, используя 127.0.0.1 вместо «localhost».

Кажется, это проблема конфигурации Windows. Файл C: \ Windows \ System32 \ drivers \ etc \ hosts содержит

# localhost name resolution is handled within DNS itself.
#   127.0.0.1       localhost

но строка разрешения «localhost» закомментирована, а разрешение DNS медленное.

4

По вопросам рекламы [email protected]