使用 libcurl 发送 HTTP 请求时根据业务需求需要对不同场景设置不同的超时时间,但 libcurl 提供了多种使请求超时的机制,不同的机制有不同的使用场景,一旦使用错误或未进行正确配置可能会导致一些出乎意外的情况。

总超时

libcurl 提供了对单个请求的总超时时间设置,即 CURLOPT_TIMEOUT(秒)CURLOPT_TIMEOUT_MS(毫秒),该参数设置的是从请求开始到请求结束的总时间,包括 DNS 解析、连接建立、传输等,如果超过该时间则请求失败,则返回 CURLE_OPERATION_TIMEDOUT 28 的错误码。设置也非常简单,调用 curl_easy_setopt 函数设置即可,如:

1
2
3
4
// 秒
curl_easy_setopt(easy_handle, CURLOPT_TIMEOUT, 1);
// 毫秒
curl_easy_setopt(easy_handle, CURLOPT_TIMEOUT_MS, 1000);

这样设置后简单粗暴,无论这个请求到底在哪个过程被阻塞,最终超过这个时间的请求都会被终止,但这样设置也有一些问题,比如我们在上传或下载一个比较大的文件时,我们并不知道文件在什么时间内会传送完毕,所以如果设置一个固定的超时时间对于这种大文件传输场景并不合适。

传输超时

传输超时的设置正是为了解决上面提到的大文件传输场景,当上传或下载文件过程中如果在指定一段时间内传输的速度不足某个阈值时,则判定超时。比如我们下载一个文件 5 秒内只传输了不到 10 字节的内容,这种情况下我们可以认为网络传输出现了问题,可以判定为超时。libcurl 提供了 CURLOPT_LOW_SPEED_LIMITCURLOPT_LOW_SPEED_TIME 两个参数来设置传输超时,其中 CURLOPT_LOW_SPEED_LIMIT 设置传输速度的阈值,单位是字节/秒,CURLOPT_LOW_SPEED_TIME 设置传输速度低于阈值的时间,单位是秒,如果在指定时间内传输速度低于阈值,则判定为超时。设置也非常简单,调用 curl_easy_setopt 函数设置即可,如:

1
2
3
// 当请求在 5 秒内每一秒的传输速率都不足 10 字节时,则判定为超时
curl_easy_setopt(easy_handle, CURLOPT_LOW_SPEED_LIMIT, 10);
curl_easy_setopt(easy_handle, CURLOPT_LOW_SPEED_TIME, 5);

这样在传输文件过程中,我们就不需要设置总超时时间了,不必担心请求因速度较慢而在固定时间被中断超时的情况了。当网络质量良好请求会顺利完成,当网络质量不好时,即使我们不设置总超时时间,当传输速度低于阈值时,请求也会被中断,这样就可以保证请求不会一直阻塞在网络传输上。返回的错误码一样是 CURLE_OPERATION_TIMEDOUT 28。

连接超时

上面提到总的超时时间是包含与指定地址建立连接的过程的,这个过程如果时间较长也会影响请求的质量,我们可以通过 CURLOPT_CONNECTTIMEOUT(秒)CURLOPT_CONNECTTIMEOUT_MS(毫秒)来设置连接超时时间,如果在指定时间内连接未建立成功,则判定为超时。调用 curl_easy_setopt 函数设置即可,如:

1
2
3
4
// 秒
curl_easy_setopt(easy_handle, CURLOPT_CONNECTTIMEOUT, 1);
// 毫秒
curl_easy_setopt(easy_handle, CURLOPT_CONNECTTIMEOUT_MS, 1000);

总结

上面介绍了 libcurl 提供的几种超时设置,总超时、传输超时、连接超时,这几种超时设置各有各的使用场景,总超时设置简单粗暴,适用于对请求的总时间有严格要求的场景,比如单个 RESTFul API 请求的场景。传输超时设置适用于对请求传输速度有严格要求的场景,比如下载大文件场景。连接超时设置适用于对请求连接建立时间有严格要求的场景,比如对请求响应时间有严格要求的场景。在实际使用中,我们可以根据业务场景合理的设置这几种超时参数,以达到最佳的请求质量。