如何解决Spring Webflux和Amazon SDK 2.x:S3AsyncClient超时
我正在使用Spring boot 2.3.1,Webflux,带有响应式mongodb驱动程序和Amazon SDk 2.14.6的Spring Data来实现Reactive项目。
我有一个CRUD,该CRUD可将实体保留在MongoDB上,并且必须将文件上传到S3。我正在使用SDK反应性方法s3Asyncclient.putObject
,但遇到了一些问题。 CompletableFuture 引发以下异常:
java.util.concurrent.CompletionException: software.amazon.awssdk.core.exception.ApiCallTimeoutException: Client execution did not complete before the specified timeout configuration: 60000 millis
at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314) ~[na:na]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Assembly trace from producer [reactor.core.publisher.MonoMapFuseable] :
reactor.core.publisher.Mono.map(Mono.java:3054)
br.com.wareline.waredrive.service.S3Service.uploadFile(S3Service.java:94)
上载方法在我的S3Service.java
类中,该类自动连接到 DocumentoService.java
@Component
public class S3Service {
@Autowired
private final ConfiguracaoService configuracaoService;
public Mono<PutObjectResponse> uploadFile(final HttpHeaders headers,final Flux<ByteBuffer> body,final String fileKey,final String cliente) {
return configuracaoService.findByClienteId(cliente)
.switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND,String.format("Configuração com id %s não enconTrada",cliente))))
.map(configuracao -> uploadFiletoS3(headers,body,fileKey,configuracao))
.doOnSuccess(response -> {
checkResult(response);
});
}
private PutObjectResponse uploadFiletoS3(final HttpHeaders headers,final Configuracao configuracao) {
final long length = headers.getContentLength();
if (length < 0) {
throw new UploadFailedException(HttpStatus.BAD_REQUEST.value(),Optional.of("required header missing: Content-Length"));
}
final Map<String,String> Metadata = new HashMap<>();
final MediaType mediaType = headers.getContentType() != null ? headers.getContentType() : MediaType.APPLICATION_OCTET_STREAM;
final S3Asyncclient s3Asyncclient = getS3Asyncclient(configuracao);
return s3Asyncclient.putObject(
PutObjectRequest.builder()
.bucket(configuracao.getBucket())
.contentLength(length)
.key(fileKey)
.contentType(mediaType)
.Metadata(Metadata)
.build(),AsyncRequestBody.frompublisher(body))
.whenComplete((resp,err) -> s3Asyncclient.close())
.join();
}
public S3Asyncclient getS3Asyncclient(final Configuracao s3Props) {
final SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder()
.readTimeout(Duration.ofMinutes(1))
.writeTimeout(Duration.ofMinutes(1))
.connectionTimeout(Duration.ofMinutes(1))
.maxConcurrency(64)
.build();
final S3Configuration serviceConfiguration = S3Configuration.builder().checksumValidationEnabled(false).chunkedEncodingEnabled(true).build();
return S3Asyncclient.builder()
.httpClient(httpClient)
.region(Region.of(s3Props.getRegion()))
.credentialsProvider(() -> AwsBasicCredentials.create(s3Props.getAccessKey(),s3Props.getSecretKey()))
.serviceConfiguration(serviceConfiguration)
.overrideConfiguration(builder -> builder.apiCallTimeout(Duration.ofMinutes(1)).apiCallAttemptTimeout(Duration.ofMinutes(1)))
.build();
}
我的实现基于Amazon SDK文档和https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/javav2/example_code/s3/src/main/java/com/example/s3/S3AsyncOps.java上的代码示例
我无法弄清楚异步客户端超时问题的原因是什么。奇怪的是,当我使用相同的 S3Asyncclient 从存储桶下载文件时,它可以正常工作。我试图将 S3Asyncclient 中的超时增加到大约5分钟,但没有成功。我不知道我在做什么错。
解决方法
我发现了错误。
在PutObjectRequest.builder().contentLength(length)
中定义 contentLength 时,我使用的是headers.getContentLength()
,它是整个请求的大小。在我的请求中,其他信息一起传递,使得内容长度大于实际文件长度。
我在Amazon文档中发现了这一点
在“ Content-Length”标头中设置的字节数大于 实际文件大小
当您将HTTP请求发送到Amazon S3时,Amazon S3期望 接收在Content-Length标头中指定的数据量。如果 Amazon S3未收到预期的数据量,并且 连接闲置20秒钟或更长时间,则连接为 关闭。请务必确认您所使用的实际文件大小 发送到Amazon S3与在中指定的文件大小一致 Content-Length标头。
https://aws.amazon.com/pt/premiumsupport/knowledge-center/s3-socket-connection-timeout-error/
发生超时错误是因为S3等待,直到发送的内容长度达到客户端通知的大小,文件结束才被发送,直到达到通知的内容长度。然后,连接保持空闲状态,S3关闭套接字。
我将内容长度更改为实际文件大小,并且上传成功。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。