网络负载下特定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和端口的数据包:
用Wireshark分析pcap文件,确认请求是否真的发送到交易所、是否收到响应、响应是否被内核正确传递到进程。tcpdump -i eth0 host <交易所IP> and port 443 -w capture.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
相关产品推荐
相关产品推荐

