You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# API返回429请求过频错误时重试任务持续失败问题排查

问题原因解析

1. 重试复用Task对象是核心错误

Task对象一旦开始执行,其最终状态(成功/失败/取消)就是永久固定的,你在调用externalAPI.UploadAsync(fileRequest, fileStream)的时候,这个异步方法就已经开始执行,返回的Task已经绑定了第一次执行的所有结果,后续循环里反复调用GetAwaiter().GetResult(),永远只会读取第一次执行的结果,不会重新发起上传请求,自然每次都抛出相同的异常。这种复用Task的做法完全不符合重试逻辑的要求。

2. fileStream的位置确实也会引发后续问题

哪怕你解决了Task复用的问题,第一次上传执行时已经把流的读取位置推进到了末尾,第二次重试时如果直接用同一个流上传,API会从流的当前位置(末尾)开始读取,要么上传空内容,要么直接触发参数异常。就算你没有提前释放流,位置不对也会导致重试失败。


优化方案

第一步:修改重试方法的入参为任务工厂

将入参从Task<T>改成Func<Task<T>>,每次重试都重新调用委托生成新的Task,触发全新的上传逻辑:

public static T RunWithRetries<T>(Func<Task<T>> taskFactory, int retryCount = 3)
{
    for (int i = 0; i < retryCount; i++)
    {
        try 
        {
            // 每次调用工厂生成新的Task,执行全新的逻辑
            return taskFactory().GetAwaiter().GetResult();
        }
        catch (Exception ex)
        {
            // 只有最后一次重试失败才抛出异常
            if (i == retryCount - 1)
                throw;
            // 此处加你的重试等待逻辑,比如429对应等待Retry-After头的时长
            Thread.Sleep(1000 * (i + 1));
        }
    }
    // 理论上不会走到这里,兼容语法
    return default;
}

第二步:调整调用逻辑,处理流位置

调用时不要直接执行UploadAsync,而是把它包在委托里,每次执行前重置流的位置:

// 先确保流支持Seek,不支持的话需要每次重试重新打开流
if (fileStream.CanSeek)
{
    fileStream.Position = 0;
}
SyncHelper.RunWithRetries(() => {
    // 每次重试都重置流位置
    if (fileStream.CanSeek)
    {
        fileStream.Position = 0;
    }
    else
    {
        // 如果流不支持Seek,这里要重新打开文件流再调用上传
        // fileStream = File.OpenRead(你的文件路径);
    }
    return externalAPI.UploadAsync(fileRequest, fileStream);
});

内容的提问来源于stack exchange,提问作者Gab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 05:12:00