优化基于Boost.Async Beast的异步服务器浏览器响应延迟问题——是否可采用read_some方法?
问题描述
我基于Boost.Asio Beast实现了一个异步代理服务器,工作流程是:接收浏览器请求后,建立第二个Socket连接到目标服务器,异步写入请求,等拿到目标服务器的响应后,再异步返回给浏览器。目前的问题是,触发向浏览器返回响应的on_read函数存在较长延迟,导致浏览器等待时间久。相关核心代码如下:
void on_write( boost::system::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if(ec) return fail2(ec, "write"); std::cout << "===========on_write============" << std::endl; stopper("async_write" , 0); stopper("on_write" , 1); // Receive the HTTP response http::async_read(redirect_stream_, redirect_buffer_, redirect_res_, std::bind( &session2::on_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void on_read( boost::system::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if(ec) return fail2(ec, "read"); std::cout << "===========on_read============" << std::endl; stopper("on_write" , 0); stopper("on_read" , 1); // Write the message to standard out std::cout << redirect_res_.base() << std::endl; http::async_write(stream_, redirect_res_, std::bind( &session2::start_shutdown, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); // Gracefully close the stream }
想请教:有没有更优方案缩短浏览器响应时间?是否可以用read_some方法来优化?
解决方案
嘿,这个场景我太熟了——异步代理的延迟优化核心就是不要等完整响应接收完再回传,咱们一步步拆解:
1. 关于read_some的可行性
答案是肯定的,但不能直接替换http::async_read,因为HTTP是结构化协议,read_some只能拿到原始字节流,没法直接解析响应格式。不过Beast提供了更灵活的方式:用http::parser分块读取+即时回传,每解析出一部分数据就立刻写给浏览器,让浏览器不用等完整响应就能开始处理,感知延迟会大幅降低。
核心思路的代码示例如下:
// 把成员变量里的redirect_res_换成parser http::response_parser<http::string_body> redirect_parser_; bool header_sent_ = false; void on_write(boost::system::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if(ec) return fail2(ec, "write"); std::cout << "===========on_write============" << std::endl; stopper("async_write" , 0); stopper("on_write" , 1); // 改用async_read_some分批读取数据 redirect_stream_.async_read_some(redirect_buffer_.prepare(8192), std::bind(&session2::on_read_some, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void on_read_some(boost::system::error_code ec, std::size_t bytes_read) { if(ec) { if(ec != boost::asio::error::eof) fail2(ec, "read_some"); // 读取结束,处理收尾 start_shutdown(boost::system::error_code{}, 0); return; } redirect_buffer_.commit(bytes_read); boost::system::error_code parse_ec; // 解析已读取的字节 auto parsed_bytes = redirect_parser_.put(redirect_buffer_.data(), parse_ec); redirect_buffer_.consume(parsed_bytes); if(parse_ec) { fail2(parse_ec, "parse"); return; } auto& response = redirect_parser_.get(); // 先回传响应头部(只传一次) if(!header_sent_ && redirect_parser_.got_header()) { http::async_write(stream_, response.base(), std::bind(&session2::on_write_to_browser, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); header_sent_ = true; } // 回传已接收的body分段,避免重复发送 if(!response.body().empty()) { std::string chunk = std::move(response.body()); http::async_write(stream_, http::buffer(chunk), std::bind(&session2::on_write_to_browser, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } // 继续读取下一批数据 redirect_stream_.async_read_some(redirect_buffer_.prepare(8192), std::bind(&session2::on_read_some, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void on_write_to_browser(boost::system::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if(ec) fail2(ec, "write_to_browser"); // 写入完成后无需额外操作,等待下一次读取回调即可 }
2. 其他降低延迟的优化点
- 调整缓冲区大小:用8192或16384字节的缓冲区,减少系统调用次数,提升读写效率。
- 禁用Nagle算法:给
redirect_stream_的socket设置boost::asio::ip::tcp::no_delay(true),减少TCP小包的合并延迟,尤其适合低带宽高延迟的网络场景。 - 移除同步操作:你代码里的
std::cout << redirect_res_.base() << std::endl;是同步IO,高并发下会拖慢回调执行,建议换成异步日志或直接移除调试输出。 - 复用TCP连接:如果代理频繁和同一目标服务器通信,实现连接池复用已建立的连接,省去TCP三次握手的时间开销。
3. 原有方案延迟高的原因
你当前用http::async_read会等待完整的HTTP响应被接收并解析完毕才触发on_read,再开始写回浏览器。如果目标服务器返回大体积响应(比如大文件、长文本),浏览器要等很久才能收到第一个字节,感知到的自然就是高延迟。而分块回传的“边读边写”模式,能让浏览器提前接收数据,大幅优化体验。
内容的提问来源于stack exchange,提问作者redemption
相关产品推荐
相关产品推荐

