2013-08-29 28 views
1

我目前正在尝试使用Boost.Asio创建一个http服务器,我这样做了这个HTTP Server 3Boost :: Asio HTTP Server极其缓慢

目前我只是阅读请求,并总是返回一个OK消息。所以没有什么特别的或耗时的。

我遇到的问题是,使用12个线程(16个核心@ 2.53GHz)运行服务器,服务器每秒处理200-300个请求。

我在C#中使用HttpListener进行了相同的处理,它使用12个线程运行,它处理了5000-7000个请求。

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 

编辑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(); 
     } 
    } 
    } 
} 

编辑2:

用于测试的客户端是一个简单的C#应用​​程序,它启动128个线程,每次迭代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 :: string使用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

其原因可能是缓慢是升压::短耳HTTP Server 3的例子总是关闭在每个响应之后进行连接,强制客户端为每个请求创建一个新的连接。每次请求打开和关闭连接都需要很长时间。显然,这不能超越任何支持HTTP/1.1和Keep-alive的服务器(基本上,不关闭客户端连接并允许客户端在后续请求中重用它)。

您的C#服务器System.Net.HttpListener确实支持保持活动状态。客户端System.Net.HttpWebRequest在默认情况下也启用了Keep-alive。所以,连接在这个配置中被重用。

添加保活到HTTP Server 3的例子很简单:

  1. 内线:: handle_read()检查请求,如果客户端请求保持有效和连接

  2. 内存储该标志

    改变连接:: handle_write(),以便它发起优雅关闭连接,只有当客户端不支持保持活动,否则只是开始async_read_some()就像你在连接已经做::开始():

    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()之前,请不要忘记清除请求/回复并重置request_parser。

+0

正如我在主要评论中写的,我尝试过使用.net TcpListener和解析请求,就像我在C++中所做的一样,但仍然很容易达到每秒4500个请求。在那里我也关闭了每一个连接。 – RaphaelH

+1

我仍然没有得到没有Keepalive的HTTP服务器性能测试的意义。没有一个Web服务器以这种方式工作,因为它太慢了。 – 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应读取所有(而不是一些)。