我目前正在尝试使用Boost.Asio创建一个http服务器,我就像这样HTTP Server 3。
目前我只是阅读了请求并始终返回OK消息。所以没什么特别或耗时的。
我遇到的问题是,运行带有12个线程的服务器(16核@ 2.53GHz),服务器每秒处理200-300个请求。
我在C#中使用HttpListener做了同样的事情,运行了12个线程,它处理了5000到7000个请求。
Boost.Asio在做什么?
在Visual Studio中使用Instrumentation Profiling获得以下“具有最多个人工作的功能”:
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#-Application,每次迭代启动128个线程,没有任何睡眠。
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 :(得分:6)
缓慢的原因可能是Boost :: Asio HTTP Server 3示例在每次响应后始终关闭连接,强制客户端为每个请求创建新连接。每个请求的打开和关闭连接都需要很多时间。显然,这不能胜过任何支持HTTP / 1.1和Keep-alive的服务器(基本上,不会关闭客户端连接并允许客户端将其重用于后续请求)。
您的C#服务器System.Net.HttpListener确实支持Keep-alive。客户端System.Net.HttpWebRequest默认情况下也启用了Keep-alive。因此,在此配置中重用连接。
将保持活动添加到HTTP Server 3的示例非常简单:
内部连接:: handle_read()检查请求是否客户端请求保持活动状态并将此标志存储在连接中
更改connection :: handle_write(),以便仅在客户端不支持Keep-alive时才启动正常连接关闭,否则只需启动async_read_some()就像你在connection :: start()中所做的那样: / p>
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。
答案 1 :(得分: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
应该全部读取(而不是一些)。
答案 2 :(得分:0)
关于HTTP服务器3示例。查看request_parser源代码。方法解析/消费。从缓冲区逐字节获取数据并处理每个字节真的不是最优的。使用 push_back 等推进 std :: string 。这只是一个例子。
此外,如果您使用 asio :: strand ,请注意它使用互斥锁t“strand implementation”。对于HTTP服务器,它很容易删除 asio :: strand ,所以我建议这样做。如果你想继续使用 strands - 为了避免延迟锁定,可以在编译时设置这些定义:
-DBOOST_ASIO_STRAND_IMPLEMENTATIONS=30000 -DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION