You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

网络负载下特定HTTP请求无响应问题排查求助

问题描述

我们正在开发一款对接多家交易所的低延迟高吞吐量客户端,核心HTTP请求发送逻辑基于Boost.Asio与Boost.Beast实现。为追求低延迟,IO上下文采用busy polling循环而非.run()方法。该客户端在99%的场景下运行正常,但当市场波动、网络负载升高时,部分HTTP请求经async_write发送成功后,始终无法通过async_read获取响应,触发5秒重试逻辑后仍无结果。孤立测试时无此问题,仅在与其他系统对接时出现。

现求助排查以下问题:

  • 是否存在Boost.Asio导致TCP读写被丢弃的情况?
  • 如何可靠追踪请求的内核、网络传输路径?
  • 如何确保完成处理器处理待处理请求?
  • 或是有其他原因引发该问题?

排查分析与解决方案

关于Boost.Asio是否会丢弃TCP读写

Boost.Asio本身不会主动丢弃TCP读写操作,但busy polling模式的实现缺陷可能导致事件丢失:

  • 若busy polling循环中未正确处理所有就绪事件,比如只检查部分fd、未处理asio::error::would_block之外的错误,会导致读写完成事件被遗漏,表现为"发送成功但收不到响应"。
  • 必须在循环中持续调用poll_one()或poll(),直到返回0,且要处理所有返回的错误码,不能因为某次would_block就直接退出循环。
  • 修正示例:
// 正确的busy polling循环逻辑
asio::error_code ec;
do {
    ec.clear();
    auto cnt = io_context.poll_one(ec);
    if (ec && ec != asio::error::would_block) {
        // 记录错误并处理资源清理
        LOG_ERROR("Poll error: {}", ec.message());
    }
} while (cnt > 0 || !ec);

追踪请求的内核、网络传输路径

用系统工具做端到端追踪:

  • 内核层面:使用perf trace追踪目标进程的TCP系统调用,过滤send/recv操作:
    perf trace -e syscalls:sys_enter_sendto,syscalls:sys_enter_recvfrom -p <你的进程PID>
    
    同时用ss -ti dst <交易所IP>查看TCP连接状态,重点关注retrans(重传次数)、rtt(往返时间)、cwnd(拥塞窗口)指标,判断是否存在网络丢包或拥塞。
  • 网络层面:用tcpdump抓取指定IP和端口的数据包:
    tcpdump -i eth0 host <交易所IP> and port 443 -w capture.pcap
    
    用Wireshark分析pcap文件,确认请求是否真的发送到交易所、是否收到响应、响应是否被内核正确传递到进程。
  • 应用层面:在async_write和async_read的完成处理器中添加微秒级精度的日志,记录请求ID、文件描述符、操作类型、错误码,关联请求与响应的完整生命周期。

确保完成处理器处理待处理请求

  • 捕获所有异常:完成处理器中若抛出未捕获异常,会导致io_context停止处理后续事件,必须添加try-catch块:
    void on_write(beast::error_code ec, std::size_t bytes_transferred) {
        try {
            if (ec) {
                LOG_ERROR("Write failed: {}", ec.message());
                return;
            }
            // 启动async_read逻辑
            async_read(...);
        } catch (const std::exception& e) {
            LOG_ERROR("Write handler exception: {}", e.what());
        }
    }
    
  • 确认线程模型:若使用多线程busy polling,必须确保每个线程持续处理io_context事件,不能出现线程退出或挂起。可改用io_context::run_for()替代手动循环,设置合理超时避免线程卡住。
  • 管理请求生命周期:若请求对象(如beast::http::request、beast::tcp_stream)在完成处理器执行前被销毁,会导致未定义行为。需用std::shared_ptr托管这些对象,确保生命周期覆盖异步操作全程。

其他可能的原因

  • TCP拥塞控制:市场波动时交易所服务器可能触发拥塞控制,导致响应延迟超过5秒重试阈值。可调整TCP参数(如tcp_syn_retries、tcp_fin_timeout),或延长重试等待时间,同时处理asio::error::timed_out错误。
  • 连接复用问题:长连接复用场景下,若服务器已关闭连接但客户端未检测到,会导致后续请求发送成功但无响应。需在async_read中处理beast::error::eof或asio::error::connection_reset错误,及时重建连接。
  • CPU亲和性:若未给IO线程绑定CPU核心,高负载时线程被调度到其他核心会导致延迟升高。可使用pthread_setaffinity_np或Asio的execution::strand将线程绑定到指定核心。

内容的提问来源于stack exchange,提问作者Rohith Uppala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 17:34:52