我试图用C++中的libcurl多界面编写一个小应用程序,遇到一个问题,它执行的速度(远低于预期):对于一个静态的HTML页面(在一个小型VM上由nginx提供服务)的49个请求(任意数字),它需要相当恒定的32秒时间。
在Google或GitHub上运行应用程序所需的时间和使用嘿生成的静态页面所需的时间一样长,因此我非常肯定这不是服务器的错误。在curl命令行应用程序中连续循环50次也要快得多,总共13秒。
下面是所讨论的代码(可读性最低的代码):
#include <curl/curl.h>
#include <memory>
#include <string>
#include <vector>
struct Request {
std::shared_ptr<CURL> request;
};
int request(std::string url, CURLM *curlm, std::vector<Request> &requests) {
Request r = {
.request = std::shared_ptr<CURL>(curl_easy_init(), curl_easy_cleanup),
};
curl_easy_setopt(r.request.get(), CURLOPT_URL, url.c_str());
curl_multi_add_handle(curlm, r.request.get());
requests.push_back(r);
return 0;
}
int main(__attribute__((unused)) int argc, char *argv[]) {
curl_global_init(CURL_GLOBAL_ALL);
std::vector<Request> requests;
CURLM *multi = curl_multi_init();
// generate an arbitrary amount of requests
for(int i = 0; i < 49; i++)
request(std::string(argv[1]), multi, requests);
int running = 0;
curl_multi_perform(multi, &running);
do {
int numfds = 0;
curl_multi_perform(multi, &running);
// select
fd_set fdread, fdwrite, fdexcep;
FD_ZERO(&fdread);
FD_ZERO(&fdwrite);
FD_ZERO(&fdexcep);
struct timeval timeout = {
.tv_sec = 0,
.tv_usec = 500
};
curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &numfds);
select(numfds+1, &fdread, &fdwrite, &fdexcep, &timeout);
// or poll
//curl_multi_poll(multi, nullptr, 0, 1000, &numfds);
} while(running > 0);
return 0;
}我在多个客户端系统上进行了测试:我的工作站是Ubuntu20.04,结果可以在CentOS 8、Ubuntu18.04和Debian 10 (全部是libcurl 7.60系列)上复制,但是在CentOS 7 (libcurl 7.29.0)上相同的代码在3到5秒内完成!除了我的工作站之外,我测试的所有这些系统都是新安装的VM,它们运行在同一台主机上,具有相同的网络配置。与此同时,互联网上的虚拟机(Debian 10,libcurl 7.64)与我预期的次秒结果差不多。
使用Wireshark,我可以看到到see服务器的所有连接都是一次打开的,但是实际的HTTP请求是以越来越多的间隔分批发送的--最后一个请求在打开连接后30秒才会发送。
select()的做事方式和使用curl_multi_poll()的方式之间也没有时间差,我已经阅读了cURL文档,寻找可能提高性能的选项;启用流水线不会产生任何影响,也不会禁用HTTP2,而且大多数限制(例如,最大连接)在默认情况下似乎设置为无限。
下面是有趣的部分:当我通过strace运行这个应用程序时,在任何本地VM上每次运行只需要2-4秒。起初,我认为strace可能破坏了一些东西,但是如果我通过shell重定向丢弃strace的输出,那么应用程序看起来工作正常,那么是什么原因呢?
为什么使用strace 会改善所有的事情,以及什么会导致运行时在这些系统之间有很大的不同?
知道我错过了什么吗?我的Google-fu这次离开了我。
发布于 2021-06-19 19:43:18
curl_multi_fdset只添加它自己的描述符,它不为零或以其他方式删除任何其他描述符。您必须先执行以下curl_multi_fdset操作
FD_ZERO(&fdread);
FD_ZERO(&fdwrite);
FD_ZERO(&fdexcep);而且,如果curl_multi_fdset是0,那么select和进一步的running就没有意义了。
https://stackoverflow.com/questions/68049873
复制相似问题