1

我目前正在尝试使用 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# 应用程序,它启动 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();
4

3 回答 3

6

缓慢的原因可能是 Boost::Asio HTTP Server 3 示例总是在每次响应后关闭连接,强制客户端为每个请求创建一个新连接。在每个请求上打开和关闭连接都需要大量时间。显然,这不能胜过任何支持 HTTP/1.1 和 Keep-alive 的服务器(基本上,不会关闭客户端连接并允许客户端将其重用于后续请求)。

您的 C# 服务器 System.Net.HttpListener 支持 Keep-alive。客户端 System.Net.HttpWebRequest 也默认启用 Keep-alive。因此,连接在此配置中被重用。

将 keep-alive 添加到 HTTP Server 3 示例很简单:

  1. 内部 connection::handle_read() 如果客户端请求保持活动,则检查请求并将此标志存储在连接中

  2. 更改 connection::handle_write() 以便它仅在客户端不支持 Keep-alive 时启动优雅的连接关闭,否则只需像您在 connection::start() 中所做的那样启动 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。

于 2013-12-03T01:55:07.940 回答
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");找到

或者async_read应该阅读全部(而不是一些)。

于 2014-05-30T09:12:43.407 回答
0

关于 HTTP 服务器 3 示例。查看request_parser源代码。方法解析/使用。这确实不是最佳选择,因为它从缓冲区逐字节获取数据并处理每个字节;使用push_back推入std::string等等。它只是一个例子。

此外,如果您使用asio::strand请注意它使用互斥锁“ strand implementation ”。对于 HTTP 服务器,它很容易完全删除asio::strand,所以我建议这样做。如果您想保留strands - 为了避免锁定延迟,您可以在编译时设置这些定义:

-DBOOST_ASIO_STRAND_IMPLEMENTATIONS=30000 -DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION
于 2013-08-29T10:10:19.793 回答