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. 结论

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

 

 

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

### RT-DETRv3 网络结构分析 RT-DETRv3 是一种基于 Transformer 的实时端到端目标检测算法,其核心在于通过引入分层密集正监督方法以及一系列创新性的训练策略,解决了传统 DETR 模型收敛慢和解码器训练不足的问题。以下是 RT-DETRv3 的主要网络结构特点: #### 1. **基于 CNN 的辅助分支** 为了增强编码器的特征表示能力,RT-DETRv3 引入了一个基于卷积神经网络 (CNN) 的辅助分支[^3]。这一分支提供了密集的监督信号,能够与原始解码器协同工作,从而提升整体性能。 ```python class AuxiliaryBranch(nn.Module): def __init__(self, in_channels, out_channels): super(AuxiliaryBranch, self).__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1) self.bn = nn.BatchNorm2d(out_channels) def forward(self, x): return F.relu(self.bn(self.conv(x))) ``` 此部分的设计灵感来源于传统的 CNN 架构,例如 YOLO 系列中的 CSPNet 和 PAN 结构[^2],这些技术被用来优化特征提取效率并减少计算开销。 --- #### 2. **自注意力扰动学习策略** 为解决解码器训练不足的问题,RT-DETRv3 提出了一种名为 *self-att 扰动* 的新学习策略。这种策略通过对多个查询组中阳性样本的标签分配进行多样化处理,有效增加了阳例的数量,进而提高了模型的学习能力和泛化性能。 具体实现方式是在训练过程中动态调整注意力权重分布,确保更多的高质量查询可以与真实标注 (Ground Truth) 进行匹配。 --- #### 3. **共享权重解编码器分支** 除了上述改进外,RT-DETRv3 还引入了一个共享权重的解编码器分支,专门用于提供密集的正向监督信号。这一设计不仅简化了模型架构,还显著降低了参数量和推理时间,使其更适合实时应用需求。 ```python class SharedDecoderEncoder(nn.Module): def __init__(self, d_model, nhead, num_layers): super(SharedDecoderEncoder, self).__init__() decoder_layer = nn.TransformerDecoderLayer(d_model=d_model, nhead=nhead) self.decoder = nn.TransformerDecoder(decoder_layer, num_layers=num_layers) def forward(self, tgt, memory): return self.decoder(tgt=tgt, memory=memory) ``` 通过这种方式,RT-DETRv3 实现了高效的目标检测流程,在保持高精度的同时大幅缩短了推理延迟。 --- #### 4. **与其他模型的关系** 值得一提的是,RT-DETRv3 并未完全抛弃经典的 CNN 技术,而是将其与 Transformer 结合起来形成混合架构[^4]。例如,它采用了 YOLO 系列中的 RepNCSP 模块替代冗余的多尺度自注意力层,从而减少了不必要的计算负担。 此外,RT-DETRv3 还借鉴了 DETR 的一对一匹配策略,并在此基础上进行了优化,进一步提升了小目标检测的能力。 --- ### 总结 综上所述,RT-DETRv3 的网络结构主要包括以下几个关键组件:基于 CNN 的辅助分支、自注意力扰动学习策略、共享权重解编码器分支以及混合编码器设计。这些技术创新共同推动了实时目标检测领域的发展,使其在复杂场景下的表现更加出色。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值