使用 Boost Beast 处理并发请求

Posted

技术标签:

【中文标题】使用 Boost Beast 处理并发请求【英文标题】:Concurrent request processing with Boost Beast 【发布时间】:2020-09-03 03:42:49 【问题描述】:

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

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

 boost::asio::io_context ioc1;
 tcp::acceptor acceptorioc, 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::acceptor 的 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 processing\n";
            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() + "'\r\n");
            break;
        
    

这是我的问题:如果我同时向我的服务器发送 2 个 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)

【问题讨论】:

【参考方案1】:

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

要使这个例子有效,你必须:

    向池中添加更多线程(如您提到的第二个示例)
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
    在需要的地方(至少对于套接字读取和写入)添加同步(例如,使用第二个示例中的strand),因为现在您的应用程序是多线程的。

更新 1

回答问题“如果事实上io_context 仅在一个线程上运行,那么 Beast 示例(第一个提到的)中的工作人员列表的目的是什么?”

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

【讨论】:

感谢您的建议。如果您不介意我问,如果实际上 io_context 仅在一个线程上运行,那么 Beast 示例(第一个提到的)中的工作人员列表的目的是什么?

以上是关于使用 Boost Beast 处理并发请求的主要内容,如果未能解决你的问题,请参考以下文章

Boost Beast服务器响应延迟1秒

C++ Boost 1.66 使用 Beast http request Parser 来解析字符串

如何正确写c++ boost beast websocket server

为啥 Boost.Asio SSL 请求返​​回 405 Not Allowed?

试图用 Boost::Beast 替换我的 libwebsocket 代码

C++ 使用 Boost.asio 和 Beast 库在正文中发送数据