【问题标题】:C++ Boost.ASIO async_read_until slowC++ Boost.ASIO async_read_until 慢
【发布时间】:2013-07-04 21:21:27
【问题描述】:

我遇到了一个不寻常的问题。我有一个 C++ Boost.ASIO Web 服务器,为了处理传入的请求,我正在使用以下代码:

boost::asio::async_read_until(
    socket_,
    response_,
    "\r\n\r\n",
    boost::bind(
            &connection::handle_read_headers,
            shared_from_this(),
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred
    )
);

(其中“socket_”是我的 boost::asio::ip::tcp::socket,“response_”是 boost::asio::streambuf)

我试图只获取请求的标头,然后我稍后执行第二个 async_read_until 与 transfer_exactly 匹配从请求标头解析的“Content-Length”。问题是上面的代码需要 100-900 毫秒才能在一个非常现代的服务器上返回(从那个读取块,直到调用 handle_read_headers())。传入的请求如下所示:

POST /load HTTP/1.1
host: www.mysite.com
Accept: */*
Accept-Encoding: gzip,deflate
Content-type: application/x-www-form-urlencoded
From: googlebot(at)googlebot.com
Origin: http://www.mysite.com
Referer: http://www.mysite.com/another-page/
User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
X-Forwarded-For: 66.249.75.103
X-Forwarded-Port: 80
X-Forwarded-Proto: http
Content-Length: 287
Connection: keep-alive

and-the-actual-content-is-here.... (287 bytes worth)

标题似乎以 \r\n\r\n 终止,并且它在一直读取到 EOF 之前触发了 handle_read_headers() 函数(因此它没有读取整个页面) - 它实际上是在跳闸正则表达式。这些请求来自 Google,所以我很有信心它不会滞后。

关于为什么要花这么长时间才能返回,有什么我可以忽略的吗?我可能错过了 aync_read_until 的任何其他问题?

谢谢!

编辑/更新: 好吧,现在我很困惑。在尝试兆字节的建议时,我从一个 streambuf 切换到一个字符数组(运气不好),然后我重构我的代码以使用 async_read_some 而不是 async_read_until,然后手动扫描分隔符。我还将所有操作系统变量(sysctrl.conf)重置为骨库默认值(以缩小可能性)。不幸的是,在使用相同的传入 POST 请求调用 handle_read() 时,以下代码中仍然存在 100-900 毫秒的延迟:

socket_.async_read_some(
    boost::asio::buffer(response_),
    boost::bind(
        &connection::handle_read,
        shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred
    )
);

response_ 现在在哪里:

boost::array<char, 4096> response_;

无济于事(同样的 100-900 毫秒延迟)。这不可能是正常的——有什么想法吗?

编辑2: 根据 Rhashimoto 的建议,我启用了处理程序跟踪,并在日志中发现了这个奇怪之处:

[2013-07-05 15:58:39 - Thread 7fae57e3f700]: Incoming connection (0ms elapsed)
@asio|1373054319.874916|506*508|socket@0x7fae50004f98.async_receive
@asio|1373054319.874963|506*509|socket@0x7fffd40fed68.async_accept
@asio|1373054319.875008|<506|
@asio|1373054320.609088|>508|ec=system:0,bytes_transferred=512
@asio|1373054320.609233|508*510|socket@0x7fae50004f98.async_receive
@asio|1373054320.609264|<508|
@asio|1373054320.609284|>510|ec=system:0,bytes_transferred=404
[2013-07-05 15:58:40 - Thread 7fae57e3f700]: Received packet headers (638 bytes) - 734ms elapsed

async_accept 和 async_receive 之间有超过 700 毫秒。在代码中,它来自这个块(实际上直接来自http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/examples/cpp03_examples.html的“HTTP Server 2” - server.cpp 和 connection.cpp):

new_connection_->start();
new_connection_.reset(new connection(
        io_service_pool_.get_io_service()
));
acceptor_.async_accept(
        new_connection_->socket(),
        boost::bind(
                &server::handle_accept,
                this,
                boost::asio::placeholders::error
        )
);

从 start() 到:

void connection::start()
{
    boost::asio::async_read_until(
        socket_,
        response_,
        "\r\n\r\n",
        boost::bind(
            &connection::handle_read_headers,
            shared_from_this(),
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred
        )
    );
}

当handle_read_headers()被调用时,已经过了700ms。

有人有任何想法吗?我完全迷路了。

非常感谢!

【问题讨论】:

  • 首先,您使用性能分析器吗?它说什么?
  • 你是单线程的吗?我想知道是否另一个任务可能会阻止您在队列中的异步读取。你能发布handler tracking的日志吗?
  • Rhashimoto,它是一个多线程环境,很大程度上基于boost.org/doc/libs/1_54_0/doc/html/boost_asio/examples/… 的“HTTP Server 2”示例 - 这是处理程序跟踪日志:pastebin.com/7F22gFCz - 我引用的毫秒数来自减去两个 boost::posix_time::microsec_clock::local_time() 快照。谢谢大家!
  • 使用 @namereply to a specific commenter 使他们更有可能看到它。我怀疑这个问题是boost::asio 的外部问题。如果您从同一主机发出请求,您是否看到延迟?或者(或另外),您可以尝试跟踪数据包,例如用sudo tcpdump -i en0 tcp and port 80,看看握手和数据之间是否有相应的延迟。
  • 您较长的处理程序跟踪日志有许多“丢失”接收,例如处理程序 15、17、19、27、29、31、36、38、43 等。我也没有看到这些套接字关闭 - 我想知道它们是否只是保持打开状态。那里也有一些asio.misc:2 (EOF) 接收错误,不知道您是否期望这样,因为看起来您无论如何都会发回数据。除了对此进行调查之外,我还将获得并发数据包跟踪并尝试将其与处理程序匹配,以期验证延迟是否是外部的。如果是并且不是延迟,那么它就是远程主机,尽管 Google 是这样。

标签: c++ boost boost-asio


【解决方案1】:

让我们看看处理程序日志

[2013-07-05 15:58:39 - Thread 7fae57e3f700]: Incoming connection (0ms elapsed)
@asio|1373054319.874916|506*508|socket@0x7fae50004f98.async_receive
@asio|1373054319.874963|506*509|socket@0x7fffd40fed68.async_accept
@asio|1373054319.875008|<506|
@asio|1373054320.609088|>508|ec=system:0,bytes_transferred=512
@asio|1373054320.609233|508*510|socket@0x7fae50004f98.async_receive
@asio|1373054320.609264|<508|
@asio|1373054320.609284|>510|ec=system:0,bytes_transferred=404
[2013-07-05 15:58:40 - Thread 7fae57e3f700]: Received packet headers (638 bytes) - 734ms elapsed

从日志中我们可以看到async_receive 被调用了两次:第一次被调用(#508) 734 毫秒后处理程序设置(#506)。现在,第二个 async_receive 在处理程序设置 (#508) 53 微秒后被调用 (#510)。就是这样,第二个处理程序调用被触发得非常快,因为数据(那 404 个字节)已经在 TCP 堆栈中准备好了。

结论:不是handler call delay,是transport delay。可能是 ISP 或平衡器有问题,或者 Google 真的不想用请求和设置延迟来打扰您。

UPD:我想你可以通过tcpdump 进行检查

附:我不喜欢 HTTP 服务器 2 示例中的 io_service_pool_ 实现。这也可能导致一些问题,但我认为它不是当前的情况。

【讨论】:

  • 谢谢,我刚刚在主帖中添加了一条评论,解释了我在这个方向上的进一步研究。至于 PS,io_service_pool 的实现呢?您是否推荐不同的设计以获得更好的性能?这是这个问题范围之外的一个领域,但我真的很感激一些洞察力。谢谢!
  • @Harry 回答了顶部的评论。似乎真的延迟问题与软件无关。关于 io_service 池它的故事很长,但简而言之:如果 io_service 的一个实例超载(或执行长时间工作的作业) - 此实例中的所有处理程序将等待并阻塞,因为每个 io_service 设计只有 1 个线程。我更喜欢运行 1 io_service 和一个为其处理程序服务的线程池。
猜你喜欢
  • 1970-01-01
  • 2012-08-13
  • 2011-07-29
  • 2017-03-04
  • 2011-03-04
  • 1970-01-01
  • 1970-01-01
  • 2018-04-05
  • 1970-01-01
相关资源
最近更新 更多