Boost::Asio HTTP Server extremely slow
我目前正在尝试使用Boost.Asio创建http服务器,使它类似于此HTTP Server 3。
目前,我只是阅读请求并始终返回OK消息。 所以没有什么特别的或费时的。
我遇到的问题是,以12个线程(16个内核@ 2.53GHz)运行服务器,服务器每秒处理大约200-300个请求。
我在C#中使用HttpListener在12个线程中运行,它处理了大约5000-7000个请求。
Boost.Asio在做什么?
在Visual Studio中使用Instrumentation Profiling获得以下"功能最多的工作":
1 2 3 4 5 | 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 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("\
\
\
\
");
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("\
\
", 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("\
\
", 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个线程,每个线程迭代1000次而没有任何睡眠。
1 2 3 4 5 | 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(); |
速度慢的原因可能是Boost :: Asio HTTP Server 3示例始终在每个响应后关闭连接,从而迫使客户端为每个请求创建一个新连接。在每个请求上打开和关闭连接需要很多时间。显然,这不能胜过任何支持HTTP / 1.1和Keep-alive的服务器(基本上,它不会关闭客户端连接,并允许客户端将其重新用于后续请求)。
您的C#服务器System.Net.HttpListener不支持Keep-alive。默认情况下,客户端System.Net.HttpWebRequest也启用了保持活动状态。因此,在此配置中可以重新使用连接。
向HTTP Server 3示例添加保持活动很简单:
内部连接:: handle_read()在客户端请求保持活动状态时检查请求,并将此标志存储在连接中
更改connection :: handle_write(),以便仅在客户端不支持Keep-alive时才启动优美的连接关闭,否则就像在connection :: start()中一样启动async_read_some():
1 2 3 4 5 | 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。
似乎
\
\
\
");
\
\
\
", lastposition);
有可能使用这里找到的
\
\
\
");
或
关于HTTP服务器3的示例。查看request_parser源代码。该方法解析/使用。从缓冲区逐个字节地获取数据并处理每个字节确实不是最佳选择。使用push_back推入std :: string等。这只是一个例子。
另外,如果您使用的是asio :: strand,请注意它使用了互斥锁t"实现"。对于HTTP服务器,可以很容易地删除asio :: strand,所以我建议这样做。如果您希望使用strands-为了避免延迟锁定,可以在编译时设置这些定义:
1 | -DBOOST_ASIO_STRAND_IMPLEMENTATIONS=30000 -DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION |