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)

我尝试上传文件大约有34kb,这是一个简单的文本文件

上载方法在我的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关闭套接字。

我将内容长度更改为实际文件大小,并且上传成功。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...