与Boost Beast并行处理请求



我指的是Beast存储库中的此示例程序:https://www.boost.org/doc/libs/1_67_0/libs/beast/example/http/server/fast/http_server_fast.cpp

我对代码进行了一些更改,以检查同时处理多个请求的能力。

boost::asio::io_context ioc{1};
tcp::acceptor acceptor{ioc, {address, port}};
std::list<http_worker> workers;
for (int i = 0; i < 10; ++i)
{
workers.emplace_back(acceptor, doc_root);
workers.back().start();
}
ioc.run();

我对以上内容的理解是,我现在将有10个工作对象来运行I/O,即处理传入连接。

那么,我的第一个问题是,上述理解正确吗?

假设上面的内容是正确的,我已经对传递给tcp::receiver:的lambda(处理程序(进行了一些更改

void accept()
{
// Clean up any previous connection.
boost::beast::error_code ec;
socket_.close(ec);
buffer_.consume(buffer_.size());
acceptor_.async_accept(
socket_,
[this](boost::beast::error_code ec)
{
if (ec)
{
accept();
}
else
{
boost::system::error_code ec2;
boost::asio::ip::tcp::endpoint endpoint = socket_.remote_endpoint(ec2);
// Request must be fully processed within 60 seconds.
request_deadline_.expires_after(
std::chrono::seconds(60));
std::cerr << "Remote Endpoint address: " <<  endpoint.address() << " port: " << endpoint.port() << "n";
read_request();
}
});
}

以及在process_request():中

void process_request(http::request<request_body_t, http::basic_fields<alloc_t>> const& req)
{
switch (req.method())
{
case http::verb::get:
std::cerr << "Simulate processingn";
std::this_thread::sleep_for(std::chrono::seconds(30));
send_file(req.target());
break;
default:
// We return responses indicating an error if
// we do not recognize the request method.
send_bad_response(
http::status::bad_request,
"Invalid request-method '" + req.method_string().to_string() + "'rn");
break;
}
}

我的问题是:如果我向服务器同时发送两个GET请求,它们将被按顺序处理,我知道这一点,因为第二个";模拟处理";语句在前一个语句后打印约30秒,这意味着执行在第一个线程上被阻止。

我试着阅读boost::asio的文档来更好地理解这一点,但没有用。

acceptor::async_accept的文档中写道:

无论异步操作是否立即完成,处理程序都不会>从该函数中调用。将以与>使用boost::asio::io_service::post((。

boost::asio::io_service::post()的文档中写道:

io_service保证只在run((,>run_one((、poll((或poll_one((成员函数当前正在被调用。

那么,如果有10个工作线程处于run()状态,那么这两个请求为什么要排队呢?

此外,有没有一种方法可以在不适应不同示例的情况下解决这种行为?(例如。https://www.boost.org/doc/libs/1_67_0/libs/beast/example/http/server/async/http_server_async.cpp)

io_context不在内部创建线程来执行任务,而是使用显式调用io_context::run的线程。在本例中,仅从一个线程(主线程(调用io_context::run。因此,只有一个线程用于执行任务,该线程在sleep中被阻塞,并且没有其他线程来执行其他任务。

要使这个例子发挥作用,你必须:

  1. 向池中添加更多线程(如您提到的第二个示例(
size_t const threads_count = 4;
std::vector<std::thread> v;
v.reserve(threads_count - 1);
for(size_t i = 0; i < threads_count - 1; ++i) { // add thraed_count threads into the pool
v.emplace_back([&ioc]{ ioc.run(); });
}
ioc.run(); // add the main thread into the pool as well
  1. 在需要的地方添加同步(例如,像第二个示例中那样使用strand((至少对于套接字读取和写入(,因为现在您的应用程序是多线程的

更新1

回答问题";如果事实上io_context只在一个线程上运行,Beast示例(引用的第一个(中的工作程序列表的目的是什么">

请注意,无论线程数如何,这里的IO操作都是异步的,这意味着http::async_write(socket_...)不会阻塞线程。请注意,我在这里解释了原始示例(而不是您的修改版本(。这里的一个工作人员处理一个往返的"请求-响应"。想象一下这种情况。有两个客户端client1和client2。客户端1的互联网连接不好(或请求一个很大的文件(,而客户端2的情况正好相反。客户端1发出请求。然后客户端2发出请求。因此,如果只有一个工作进程,那么客户端2将不得不等待,直到客户端1完成整个往返"请求-响应"。但是,由于有多个工作线程,client2立即得到响应,而不是等待client1(请记住,IO不会阻塞单个线程(该示例针对瓶颈是IO而不是实际工作的情况进行了优化。在修改后的例子中,情况正好相反——与IO相比,工作(30秒(非常昂贵。对于这种情况,最好使用第二个例子。

最新更新