2013-08-29 1 views
1

Я в настоящее время пытаюсь создать http-сервер с помощью Boost.Asio, я сделал это следующим образом: HTTP Server 3.Boost :: Asio HTTP Server чрезвычайно медленный

В настоящее время я просто прочитал запрос и всегда возвращаю сообщение OK. Так что ничего особенного или много времени.

Проблема, с которой я сталкиваюсь, работает на сервере с 12 потоками (16 ядер на 2,53 ГГц), сервер обрабатывает 200-300 запросов в секунду.

Я сделал то же самое в C#, используя HttpListener, работающий с 12 потоками, обрабатывает запросы около 5000-7000 arround.

Какая черта Boost.Asio делает?

Использование инструментария профилирование с Visual Studio получить следующие "функции с большинством индивидуальная работа":

Name       Exclusive Time % 
GetQueuedCompletionStatus    44,46 
std::_Lockit::_Lockit     14,54 
std::_Container_base12::_Orphan_all  3,46 
std::_Iterator_base12::~_Iterator_base12 2,06 

Edit 1:

if (!err) { 
    //Add data to client request 
    if(client_request_.empty()) 
    client_request_ = std::string(client_buffer_.data(), bytes_transferred); 
    else 
    client_request_ += std::string(client_buffer_.data(), bytes_transferred); 
    //Check if headers complete 
    client_headerEnd_ = client_request_.find("\r\n\r\n"); 
    if(client_headerEnd_ == std::string::npos) { 
    //Headers not yet complete, read again 
    client_socket_.async_read_some(boost::asio::buffer(client_buffer_), 
     boost::bind(&session::handle_client_read_headers, shared_from_this(), 
      boost::asio::placeholders::error, 
      boost::asio::placeholders::bytes_transferred)); 
    } else { 
    //Search Cookie 
    std::string::size_type loc=client_request_.find("Cookie"); 
    if(loc != std::string::npos) { 
    //Found Cookie 
    std::string::size_type locend=client_request_.find_first_of("\r\n", loc); 
    if(locend != std::string::npos) { 
     std::string lCookie = client_request_.substr(loc, (locend-loc));   loc = lCookie.find(": ");   if(loc != std::string::npos) { 
     std::string sCookies = lCookie.substr(loc+2); 
     std::vector<std::string> vCookies; 
     boost::split(vCookies, sCookies, boost::is_any_of(";")); 
     for (std::size_t i = 0; i < vCookies.size(); ++i) { 
      std::vector<std::string> vCookie; 
      boost::split(vCookie, vCookies[i], boost::is_any_of("=")); 
      if(vCookie[0].compare("sessionid") == 0) { 
      if(vCookie.size() > 1) { 
       client_sessionid_ = vCookie[1]; 
       break; 
      } 
      } 
     }    } 
    }   } 
    //Search Content-Length 
    loc=client_request_.find("Content-Length"); 
    if(loc == std::string::npos) { 
     //No Content-Length, no Content? -> stop further reading 
     send_bad_request(); 
     return; 
    } 
    else { 
     //Parse Content-Length, for further body reading 
     std::string::size_type locend=client_request_.find_first_of("\r\n", loc); 
     if(locend == std::string::npos) { 
     //Couldn't find header end, can't parse Content-Length -> stop further reading 
     send_bad_request(); 
     return; 
     } 
     std::string lHeader = client_request_.substr(loc, (locend-loc)); 
     loc = lHeader.find(": "); 
     if(loc == std::string::npos) { 
     //Couldn't find colon, can't parse Content-Length -> stop further reading 
     send_bad_request(); 
     return; 
     } 
     //Save Content-Length 
     client_request_content_length_ = boost::lexical_cast<std::string::size_type>(lHeader.substr(loc+2)); 
     //Check if already read complete body 
     if((client_request_.size()-(client_headerEnd_)) < client_request_content_length_) { 
     //Content-Length greater than current body, start reading. 
     client_socket_.async_read_some(boost::asio::buffer(client_buffer_), 
      boost::bind(&session::handle_client_read_body, shared_from_this(), 
      boost::asio::placeholders::error, 
      boost::asio::placeholders::bytes_transferred)); 
     } 
     else { 
     //Body is complete, start handling 
     handle_request(); 
     } 
    } 
    } 
} 

Edit 2:

Клиент, используемый для тестирования, представляет собой простую C# -Application, которая запускает 128-Threads каждый раз итерации 1000 раз без сна.

System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(BaseUrl); 
req.Method = "POST"; 
byte[] buffer = Encoding.ASCII.GetBytes("{\"method\":\"User.Login\",\"params\":[]}"); 
req.GetRequestStream().Write(buffer, 0, buffer.Length); 
req.GetRequestStream().Close(); 
+0

Внесите код просто. Это не оптимально. request_parser в примере синтаксического анализа на каждый байт и push_back на строку, без каких-либо резервов. Существует также недокументированная проблема с asio :: strand. – ForEveR

+0

Трудно сказать, не видя какой-то настоящий код. Пробовали ли вы его профилировать? Кроме того, вы скомпилировали его с любыми оптимизациями? – Nim

+0

Это часто проблема с выходом Nagle. См: http://stackoverflow.com/questions/2039224/poor-boost-asio-performance/2039378#2039378 – janm

ответ

0

О примере HTTP-сервера 3. Посмотрите на исходный код request_parser. Методы анализируют/потребляют. Это действительно не оптимистично, поскольку он получает данные из буфера побайта и работает с каждым байтом; нажав на std :: строка с использованием push_back и так далее. Это просто пример.

Кроме того, если вы используете asio :: strand обратите внимание, что он использует блокировку мьютекса t «strand implementation». Для HTTP-сервера легко можно удалить asio :: strand, поэтому я рекомендую это сделать. Если Вы хотите остановиться в этом городе прядей - чтобы избежать задержек на замок вы можете установить эти Определяет во время компиляции:

-DBOOST_ASIO_STRAND_IMPLEMENTATIONS=30000 -DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION 
+0

Просто удалил все «нити», но все же очень медленно. Я не использую учебник request_parser, добавленный код, который я использую. – RaphaelH

6

Причина медлительности, вероятно, что подталкивания :: Asio HTTP Server 3 Пример всегда закрывает после каждого ответа, заставляя клиента создавать новое соединение для каждого запроса. Открытие и закрытие соединения по каждому запросу занимает много времени. Очевидно, что это не может превзойти любой сервер, поддерживающий HTTP/1.1 и Keep-alive (в основном, не закрывает клиентское соединение и позволяет клиенту повторно использовать его для последующих запросов).

Ваш сервер C#, System.Net.HttpListener, поддерживает Keep-alive. Клиент, System.Net.HttpWebRequest, также поддерживает Keep-alive по умолчанию. Таким образом, соединения используются в этой конфигурации повторно.

Добавление поддержания активности в HTTP Server 3 Пример прост:

  1. в связи :: handle_read() проверить запрос, если клиент просил Keep-Alive и сохранить этот флаг в связи

  2. измените соединение :: handle_write(), чтобы оно инициировало изящное закрытие соединения только тогда, когда клиент не поддерживает Keep-alive, иначе просто запустите async_read_some(), как вы уже это делаете в связи :: start():

    socket_.async_read_some(boost::asio::buffer(buffer_), 
        strand_.wrap(
         boost::bind(&connection::handle_read, shared_from_this(), 
          boost::asio::placeholders::error, 
          boost::asio::placeholders::bytes_transferred))); 
    

И не забудьте очистить свой запрос/ответ и сбросить запрос_параметр перед вызовом async_read_some().

+0

Как я уже писал в основных комментариях, я попытался использовать .net TcpListener и разбор запроса, как я делал на C++, но все же я достигаю легко 4500 запросов в секунду. И там я также закрыл каждое соединение. – RaphaelH

+1

Я все еще не понимаю, в чем смысл тестирования производительности HTTP-сервера без keepalive. Ни один из веб-серверов не работает таким образом, потому что он слишком медленный. – vond

+0

Я понимаю ваше возражение, но речь идет о путанице в том, что .Net это быстрее. – RaphaelH

2

Кажется, что client_request_.find("\r\n\r\n"); вызывается многократно - охота за конечными токенами с начала строки каждой петли. используйте начальную позицию позиции. такие как client_request_.find("\r\n\r\n", lastposition); (с использованием bytes_transferred)

его можно использовать asycn_read_until( ,"\r\n\r\n"); нашел here

или async_read, который должен прочитать все (вместо некоторых).