如何通过签名S3上传实现ACC/APS/Forge的可靠文件上传?
针对ACC批量签名S3上传的可靠性优化方案
问题梳理
向ACC批量上传文件时,频繁遭遇502 BadGateway和429 Too Many Requests错误,使用Polly实现的重试机制因APS主动终止连接,无法有效捕获异常并重试;同时存在CreateVersion重试时返回409 Conflict的幂等性问题,导致Design Automation运行结束后的批量上传失败需重启整个流程,极大浪费计算资源。
可行优化方案
1. 调整Polly重试策略,适配APS连接特性
APS在返回502/429时可能直接关闭连接,导致客户端抛出TaskCanceledException,需修改Polly策略:
- 同时捕获
HttpRequestException(包含502场景)、TaskCanceledException(超时/连接终止)和状态码429 - 针对429错误,严格遵循响应头
Retry-After指定的时间间隔重试,避免加剧限流 - 增加重试前的连接重置逻辑,比如重新创建HttpClient实例,避免复用已被终止的连接
示例策略代码:
var retryPolicy = Policy .Handle<HttpRequestException>() .Or<TaskCanceledException>() .OrResult<HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.TooManyRequests || r.StatusCode == HttpStatusCode.BadGateway) .WaitAndRetryAsync( retryCount: 5, sleepDurationProvider: (retryAttempt, response, context) => { if (response?.Result?.Headers.RetryAfter != null) { return response.Result.Headers.RetryAfter.Delta ?? TimeSpan.FromSeconds(10); } return TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)); // 指数退避 }, onRetryAsync: async (outcome, timespan, retryAttempt, context) => { // 重试前重置HttpClient(若使用单例客户端) if (outcome.Exception is TaskCanceledException) { _httpClient = new HttpClient(); // 或从HttpClientFactory重新获取实例 } });
2. 批量上传的并发控制
并行上传易触发限流,需主动控制并发压力:
- 将并发数限制在5-10以内(根据实际测试调整),避免短时间内发送过多请求
- 采用分批上传策略,比如每批上传20个文件,完成一批后再启动下一批,降低服务器负载
3. 处理CreateVersion的幂等性问题
当CreateVersion返回失败但实际已成功时,重试会触发409冲突,可通过以下方式处理:
- 在重试前调用
GetVersion接口检查文件是否已存在,若存在则跳过当前上传流程 - 为每个上传请求生成唯一的请求ID,利用APS的幂等性支持(部分接口支持
Idempotency-Key请求头),避免重复创建版本
4. 优化签名S3上传流程
- 获取签名URL时设置合理过期时间(如30分钟),避免重试时URL失效
- 上传文件到S3时单独添加重试逻辑,处理S3侧的临时错误
- 完成S3上传后,调用APS确认接口时增加状态检查,确保文件已同步到ACC
示例错误日志
... successfully uploaded a couple of files [09/25/2023 11:37:33] [11:37:33 DBG] Http request POST https://developer.api.autodesk.com/oss/v2/buckets/wip.dm.emea.2/objects/myfile.pdf/signeds3upload [09/25/2023 11:37:33] [11:37:33 WRN] Retry after Transient Error BadGateway https://developer.api.autodesk.com/oss/v2/buckets/wip.dm.emea.2/myfile.pdf/signeds3upload [09/25/2023 11:38:33] [11:38:33 WRN] Retry after Timeout A task was canceled. [09/25/2023 11:38:43] [11:38:43 WRN] Retry after Timeout A task was canceled. [09/25/2023 11:38:53] [11:38:53 WRN] Retry after Timeout A task was canceled. [09/25/2023 11:39:03] [11:39:03 WRN] Retry after Timeout A task was canceled. [09/25/2023 11:39:13] System.Threading.Tasks.TaskCanceledException: A task was canceled. [09/25/2023 11:39:13] at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) [09/25/2023 11:39:13] at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) ... more stacktrace [09/25/2023 11:39:14] [11:39:14 INF] Shutdown [09/25/2023 11:39:17] RESULT: Failure - Result of running user app is failure [09/25/2023 11:39:17] Finished running. Process will return: TestError [09/25/2023 11:39:17] ====== Revit finished running: revitcoreconsole ======
内容的提问来源于stack exchange,提问作者user2025261
相关产品推荐
相关产品推荐

