重试逻辑必须与HTTP状态码解耦,仅对网络层失败(http_code == 0)或5xx服务端临时错误重试,避免对4xx客户端错误无差别重试;C++中须调用curl_easy_getinfo获取真实HTTP状态码。

重试逻辑必须和HTTP状态码解耦
很多开发者直接在 curl_easy_perform() 失败时无差别重试,结果遇到 400 Bad Request 或 401 Unauthorized 也重试,浪费资源还掩盖真实问题。重试只该针对临时性失败:网络超时、连接被拒、502/503/504 等。C++中需显式检查HTTP响应码,不能只依赖libcurl返回值。
-
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_code)必须在每次请求后调用,否则拿不到真实状态码 - 把
http_code == 0视为网络层失败(如DNS解析失败、TCP连接超时),这类可立即重试 http_code >= 500 && http_code 是服务端临时错误,适合指数退避-
http_code == 408(Request Timeout)也要纳入重试范围,它和5xx同属临时性错误
用std::chrono控制退避间隔,别手写sleep
硬编码 sleep(1) 或 usleep(100000) 在Linux下不可靠,Windows不支持,且无法响应中断。C++11起应统一用 std::this_thread::sleep_for() 配合 std::chrono::milliseconds。
- 首次失败后等待
100ms,第二次200ms,第三次400ms——用std::pow(2, attempt) * 100计算,但要限制最大等待时间(比如不超过5s) - 每次休眠前检查是否已超总超时阈值(例如上传整体限时
30s),避免重试耗尽全部时间 - 不要用
clock()或time()做退避计算,它们精度低且易受系统时间跳变影响
文件句柄和内存缓冲区必须在重试前重置
libcurl的 CURLOPT_READFUNCTION 回调如果读取的是文件流(std::ifstream),重试时若不重置文件位置指针,会从上次中断处继续读,导致上传内容错乱或截断。
- 每次重试前调用
file_stream.clear()和file_stream.seekg(0, std::ios::beg) - 若使用内存缓冲区(如
std::vector<char></char>),确保重试时传给CURLOPT_POSTFIELDS的仍是完整数据起始地址,而非偏移后的指针 - 避免在回调里做
fseek()或rewind(),这些操作在多线程环境下不安全;重置动作必须在主重试循环内完成
重试次数和错误分类要分开配置
网络超时(CURLE_OPERATION_TIMEDOUT)和连接拒绝(CURLE_COULDNT_CONNECT)可以多试几次(比如3次),而SSL握手失败(CURLE_SSL_CONNECT_ERROR)大概率是证书或协议问题,重试无意义。
立即学习“C++免费学习笔记(深入)”;
- 定义枚举或map映射错误码到重试策略:
{CURLE_COULDNT_CONNECT: 3, CURLE_OPERATION_TIMEDOUT: 3, CURLE_SSL_CONNECT_ERROR: 0} - 把重试上限设为变量(如
max_retries = 3),不要写死在循环条件里,方便单元测试覆盖边界情况 - 记录每次重试的错误码和HTTP状态码,日志格式建议:
[retry=1] curl_error=28 http_code=0,便于后续分析失败模式
真正麻烦的是断点续传场景——如果服务端不支持 Content-Range,重试就得从头开始,这时候重试本身就会放大带宽压力。这种情况下,重试机制得配合服务端能力探测,不是加个for循环就能解决的。


















