欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 手游 > HTTP 失败重试(重发)方案

HTTP 失败重试(重发)方案

2025/3/20 16:01:50 来源:https://blog.csdn.net/m0_62573048/article/details/146362122  浏览:    关键词:HTTP 失败重试(重发)方案

   在 Qt 网络开发中,使用 QNetworkAccessManager 进行 HTTP 请求时,可能会遇到网络超时、服务器错误等情况。为了提高请求的可靠性,可以实现 HTTP 失败重试(重发) 机制。下面介绍几种常见的 失败重发方案

单请求

1. 基本重试策略

适用于: 短暂的网络抖动、服务器瞬时不可用等情况。

实现思路:

  • 监听 QNetworkReply 的 finished() 信号,检查 error() 是否为失败状态。
  • 如果失败,等待一段时间后重新发送请求。
  • 设定 最大重试次数,避免无限循环重试。
  • #include <QCoreApplication>
    #include <QNetworkAccessManager>
    #include <QNetworkRequest>
    #include <QNetworkReply>
    #include <QTimer>
    #include <QDebug>class HttpRetryHandler : public QObject {Q_OBJECT
    public:HttpRetryHandler(QObject *parent = nullptr): QObject(parent), networkManager(new QNetworkAccessManager(this)), retryCount(0) {connect(networkManager, &QNetworkAccessManager::finished, this, &HttpRetryHandler::onReplyFinished);}void sendRequest() {QNetworkRequest request(QUrl("https://example.com/api"));request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");reply = networkManager->get(request);connect(reply, &QNetworkReply::finished, this, &HttpRetryHandler::onReplyFinished);}private slots:void onReplyFinished() {if (reply->error() == QNetworkReply::NoError) {qDebug() << "Request successful:" << reply->readAll();reply->deleteLater();} else {qDebug() << "Request failed:" << reply->errorString();if (retryCount < maxRetries) {retryCount++;qDebug() << "Retrying..." << retryCount;QTimer::singleShot(retryInterval, this, &HttpRetryHandler::sendRequest);} else {qDebug() << "Max retries reached. Giving up.";}}}private:QNetworkAccessManager *networkManager;QNetworkReply *reply;int retryCount;const int maxRetries = 3; // 最大重试次数const int retryInterval = 2000; // 失败后等待 2 秒再重试
    };int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);HttpRetryHandler handler;handler.sendRequest();return a.exec();
    }#include "main.moc"

    关键点

  • 设置最大重试次数 (maxRetries),避免无限重试。
  • 使用 QTimer::singleShot() 延迟重试,避免立即重试导致服务器压力增大。

2. 指数退避(Exponential Backoff)重试

适用于: API 速率限制、网络负载较高时的自动恢复。

实现思路:

  • 每次重试时,等待时间 指数增长(如 2^retryCount)。
  • 可以设置一个 最大等待时间,避免等待过长。
  • 适用于 API 限流(如 HTTP 429 Too Many Requests)或 服务器负载高的情况。
void retryWithBackoff() {if (retryCount < maxRetries) {int delay = qMin(initialRetryInterval * (1 << retryCount), maxRetryInterval);qDebug() << "Retrying in" << delay << "ms...";QTimer::singleShot(delay, this, &HttpRetryHandler::sendRequest);retryCount++;} else {qDebug() << "Max retries reached. Stopping.";}
}

关键点

  • 指数增长等待时间delay = initialRetryInterval * (1 << retryCount)
  • 避免等待过长:使用 qMin() 限制最大重试间隔。

3. 仅对特定 HTTP 状态码重试

适用于:

  • 服务器错误(HTTP 5xx,如 500502503)。
  • 速率限制(HTTP 429 Too Many Requests)。
void onReplyFinished() {int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();if (reply->error() == QNetworkReply::NoError) {qDebug() << "Success:" << reply->readAll();} else if (statusCode == 500 || statusCode == 502 || statusCode == 503 || statusCode == 429) {qDebug() << "Server error, retrying...";retryWithBackoff();} else {qDebug() << "Request failed permanently:" << reply->errorString();}
}

关键点

  • 避免对所有错误重试,只对 5xx/429 进行重试,减少无效请求。

4. 结合 QNetworkReply::redirected() 处理重定向

适用于: 遇到 301/302/307 重定向 时自动跟随新 URL。

void onReplyFinished() {QVariant redirectTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);if (!redirectTarget.isNull()) {QUrl newUrl = redirectTarget.toUrl();qDebug() << "Redirected to:" << newUrl;request.setUrl(newUrl);sendRequest();return;}
}

关键点

  • 检查 RedirectionTargetAttribute 并重新发起请求。

总结

方案适用情况优缺点
基本重试网络波动、短暂的服务器异常简单有效,但可能导致过多请求
指数退避API 限流、服务器负载高避免频繁请求,适应性更强
特定状态码重试5xx/429 错误只在必要时重试,减少无效请求
自动重定向301/302/307 响应处理 URL 变更,防止访问失败

在实际开发中,可以 结合多种策略

  • 对 5xx/429 进行指数退避重试
  • 对 301/302 自动重定向
  • 对不可恢复错误(如 403/404)直接放弃

多请求

如果有多个失败请求需要重试,可以使用 队列管理 机制来处理所有失败的请求,而不是单独重试每个请求。这可以确保 多个请求按顺序重试,并且不会让服务器负担过重。或者可以让请求并行。

方案 1:使用队列逐个重试

  • 适用于: 不希望所有请求同时重试,逐个处理失败请求,降低服务器压力。

实现思路:

  1. 维护一个 QQueue<QNetworkRequest> 队列,存储失败的请求
  2. 失败请求会进入队列,并按照 FIFO(先进先出)顺序依次重试。
  3. 使用 QTimer 控制指数退避重试时间依次出队并重试
  4. 如果重试成功,从队列中移除请求,否则增加重试次数,重新排队。
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QTimer>
#include <QQueue>
#include <QDebug>
#include <QRandomGenerator>class HttpRetryHandler : public QObject {Q_OBJECT
public:HttpRetryHandler(QObject *parent = nullptr): QObject(parent), networkManager(new QNetworkAccessManager(this)) {connect(networkManager, &QNetworkAccessManager::finished, this, &HttpRetryHandler::onReplyFinished);}void sendRequest(const QUrl &url) {QNetworkRequest request(url);request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");QNetworkReply *reply = networkManager->get(request);requestMap.insert(reply, {request, 0}); // 记录请求和当前重试次数connect(reply, &QNetworkReply::finished, this, &HttpRetryHandler::onReplyFinished);}private slots:void onReplyFinished() {QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());if (!reply) return;int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();auto it = requestMap.find(reply);if (reply->error() == QNetworkReply::NoError) {qDebug() << "Request successful: " << reply->readAll();requestMap.remove(reply);} else if (statusCode == 500 || statusCode == 502 || statusCode == 503 || statusCode == 429) {// 服务器错误或限流,加入重试队列qDebug() << "Server error" << statusCode << ", adding request to retry queue...";retryQueue.enqueue(it.value());processRetryQueue();  // 处理队列} else {qDebug() << "Request failed permanently: " << reply->errorString();}reply->deleteLater();}void processRetryQueue() {if (retryQueue.isEmpty() || retrying) return;retrying = true;RequestData requestData = retryQueue.dequeue();if (requestData.retryCount < maxRetries) {int delay = qMin(initialRetryInterval * (1 << requestData.retryCount) + getJitter(), maxRetryInterval);qDebug() << "Retrying request in" << delay << "ms...";QTimer::singleShot(delay, this, [=]() {sendRequest(requestData.request.url());retrying = false;processRetryQueue();  // 继续处理下一个请求});requestData.retryCount++;} else {qDebug() << "Max retries reached for request: " << requestData.request.url();retrying = false;}}private:struct RequestData {QNetworkRequest request;int retryCount;};QNetworkAccessManager *networkManager;QMap<QNetworkReply *, RequestData> requestMap;QQueue<RequestData> retryQueue;bool retrying = false;const int maxRetries = 5; // 最大重试次数const int initialRetryInterval = 1000; // 初始重试间隔 1 秒const int maxRetryInterval = 16000; // 最大重试间隔 16 秒int getJitter() { return QRandomGenerator::global()->bounded(500); } // 额外随机延迟 0~500ms
};int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);HttpRetryHandler handler;// 发送多个请求handler.sendRequest(QUrl("https://example.com/api1"));handler.sendRequest(QUrl("https://example.com/api2"));handler.sendRequest(QUrl("https://example.com/api3"));return a.exec();
}#include "main.moc"

  关键点

(1) 多个请求失败后的处理
  • 使用 QMap<QNetworkReply *, RequestData> 记录请求信息
  • QQueue<RequestData> 存储失败的请求
  • processRetryQueue() 依次从队列取出请求并重试
(2) 防止所有请求同时重试
  • 使用 bool retrying 确保一次只处理一个重试请求
  • 指数退避 (2^retryCount) 并增加随机抖动
**(3) 重试失败的请求
  • 如果达到 maxRetries,则放弃重试
  • 否则等待 delay 时间后重试

方案 2:并行重试多个请求

如果你不想让请求 顺序重试,而是 并行重试多个请求,可以 使用多个 QTimer 并行调度,同时让多个请求进行指数退避重试。

void processRetryQueue() {while (!retryQueue.isEmpty()) {RequestData requestData = retryQueue.dequeue();if (requestData.retryCount < maxRetries) {int delay = qMin(initialRetryInterval * (1 << requestData.retryCount) + getJitter(), maxRetryInterval);qDebug() << "Retrying request to " << requestData.request.url() << " in " << delay << "ms...";QTimer::singleShot(delay, this, [=]() {sendRequest(requestData.request.url());});requestData.retryCount++;} else {qDebug() << "Max retries reached for request: " << requestData.request.url();}}
}

区别:

  • 多个请求可以同时重试(不会等待上一个重试完成)。
  • 所有请求仍然使用指数退避时间控制频率

5. 结论

方案优点缺点
顺序重试(队列模式)减少服务器压力、保证请求顺序可能导致某些请求等待较久
并行重试(多定时器)提高吞吐量,适合高并发可能让服务器短时间内收到大量重试请求

 

 

注:本人也在学习中,如果有错误,请指出!!!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词