使用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 :: 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

解决方法

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仅在一个线程上运行,“野兽”示例中的工作人员列表(第一个引用的对象)的目的是什么?”

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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...