使用AWS Java SDK V2调用的AWS Lambda函数超时

0

【以下的问题经过翻译处理】 我创建了一个使用Python编写的AWS Lambda函数,它从一个S3存储桶中读取一个tar.gz文件,并将其解压,然后将提取的文件写入另一个S3存储桶中。GZ文件中的Tar大小大于1GB,因此Lambda需要更长时间来完成任务。我从Java客户端调用此Lambda函数。我使用AWS SDK V2 for Java(software.amazon.awssdk.*),使用Lambda同步客户端 software.amazon.awssdk.services.lambda.LambdaClient。

尽管Lambda调用有效(lambdaClient.invoke(invokeRequest)),但它会失败,并显示“读取超时”的错误。在后台(在AWS中),Lambda在一段时间后完成执行。

以下是Lambda客户端bean创建代码:

LambdaClient lambdaClient = LambdaClient.builder()
        		.credentialsProvider(awsCredentialsProvider)
                .region(Region.US_EAST_1)
                .overrideConfiguration(ClientOverrideConfiguration.builder()
                		.apiCallTimeout(Duration.ofMinutes(30))
                		.apiCallAttemptTimeout(Duration.ofMinutes(30))
                        .build()
                )
                .build();

以下是Lambda调用代码:

//这是一个自定义的pojo对象,它将映射到Lambda输入json负载
UntarLambdaPayload untarLambdaPayload = UntarLambdaPayload.builder()
                .sourceBucket(lambdaProps.getSourceBucket())
                .destinationBucket(lambdaProps.getDestinationBucket())
                .sourceKey("myTarFile.tar.gz")
                .build();

ObjectMapper mapper = new ObjectMapper();
String jsonRequest = mapper.writeValueAsString(untarLambdaPayload);
SdkBytes payload = SdkBytes.fromUtf8String(jsonRequest);
            
InvokeRequest invokeRequest = InvokeRequest.builder()
                    .functionName(lambdaProps.getFunctionName())
                    .overrideConfiguration(AwsRequestOverrideConfiguration.builder()
                            .apiCallTimeout(Duration.ofMinutes(30))
                            .apiCallAttemptTimeout(Duration.ofMinutes(30)).build())
                    .payload(payload)
                    .build();
            
InvokeResponse res = lambdaClient.invoke(invokeRequest);

我收到以下exception:

software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: Read timed out
	at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:102)
	at software.amazon.awssdk.core.exception.SdkClientException.create(SdkClientException.java:47)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper.setLastException(RetryableStageHelper.java:204)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.execute(RetryableStage.java:83)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.execute(RetryableStage.java:36)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.StreamManagingStage.execute(StreamManagingStage.java:56)
	at software.amazon.awssdk.core.internal.http.StreamManagingStage.execute(StreamManagingStage.java:36)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.executeWithTimer(ApiCallTimeoutTrackingStage.java:80)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.execute(ApiCallTimeoutTrackingStage.java:60)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.execute(ApiCallTimeoutTrackingStage.java:42)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallMetricCollectionStage.execute(ApiCallMetricCollectionStage.java:48)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallMetricCollectionStage.execute(ApiCallMetricCollectionStage.java:31)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ExecutionFailureExceptionReportingStage.execute(ExecutionFailureExceptionReportingStage.java:37)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ExecutionFailureExceptionReportingStage.execute(ExecutionFailureExceptionReportingStage.java:26)
	at software.amazon.awssdk.core.internal.http.AmazonSyncHttpClient$RequestExecutionBuilderImpl.execute(AmazonSyncHttpClient.java:193)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.invoke(BaseSyncClientHandler.java:103)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.doExecute(BaseSyncClientHandler.java:167)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.lambda$execute$1(BaseSyncClientHandler.java:82)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.measureApiCallSuccess(BaseSyncClientHandler.java:175)
	at software.amazon.awssdk.core.internal.handler.BaseSyncClientHandler.execute(BaseSyncClientHandler.java:76)
	at software.amazon.awssdk.core.client.handler.SdkSyncClientHandler.execute(SdkSyncClientHandler.java:45)
	at software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler.execute(AwsSyncClientHandler.java:56)
	at software.amazon.awssdk.services.lambda.DefaultLambdaClient.invoke(DefaultLambdaClient.java:2355)

我的代码的进一步逻辑依赖于 Lambda 函数的成功完成。如果 Lambda 超时,则代码无法继续处理 S3 bucket#2 中的解压文件。我尝试使用 InvokeRequest 中的 overrideConfiguration,包括 apiCallTimeout 和 apiCallAttemptTimeout(以及在 Lambda 客户端中),但没有成功。我将要研究 Lambda 客户端的等待功能,目前还没有找到关于如何与 Lambda 一起使用的帮助。 我该如何让 lambdaClient.invoke(invokeRequest) 在 Lambda 在 AWS 中完成执行之前等待?

profile picture
EXPERTE
gefragt vor 5 Monaten41 Aufrufe
1 Antwort
0

【以下的回答经过翻译处理】 你的代码客户端在哪里运行的(使用Java SDK的应用)?可能正在发生的是你正在遇到TCP Keepalive超时。在SDK等待Lambda回复时,TCP连接没有任何流量传送。可能你途经使用的网络设备在一段时间后关闭空闲连接。例如,如果你的应用程序在VPC环境中运行,并使用NAT网关或VPC终端节点用于对Lambda的出站连接,则空闲连接将在350秒后被关闭。在这种情况下,行为将与你所观察到的一致 - Lambda一侧将继续运行,而客户端一侧最终会超时。

如果上述描述符合你的情况,你可以尝试通过以下两个步骤来解决问题 - (1)减少操作系统级别的TCP Keepalive时间 (2)启用Java SDK中的TCP Keepalive。以下我将详细介绍这两个步骤。

注意:这些步骤使用高级技术,改变了操作系统级别TCP keepalive设置和应用级别HTTP客户端配置的默认行为。请仅将以下描述用作参考,并确保如果您决定将其应用于实际环境中,确保您知道自己在做什么。这是一篇很好的文章,解释了TCP Keepalive

1.默认情况下,Linux环境通常配置为具有7200秒(2小时)的TCP Keepalive间隔,在你的场景中显然过高。你可以通过运行以下命令查看配置:

cat /proc/sys/net/ipv4/tcp_keepalive_time
cat /proc/sys/net/ipv4/tcp_keepalive_intvl

您可以通过将以下属性添加到/etc/sysctl.conf并重新启动系统来更新这些设置。您可能希望使用不同的值。如果您的路径上有NAT网关或VPC端点,请确保您的保持连接时间低于350秒。其他网络设备可能具有不同的超时时间。下面的配置意味着在开始发送保持连接探测之前等待120秒,然后以30秒的间隔发送它们。

net.ipv4.tcp_keepalive_time=120
net.ipv4.tcp_keepalive_intvl=30

重新启动后,运行相同的cat命令以确保应用了新的设置。 2.构建一个自定义的ApacheHttpClient,启用TCP Keepalive,并在构建Lambda客户端时使用它。请参阅下面的参考资料。

ApacheHttpClient.Builder apacheHttpClientBuilder = ApacheHttpClient.builder();
        apacheHttpClientBuilder.connectionMaxIdleTime(Duration.ofSeconds(900));
        apacheHttpClientBuilder.connectionTimeToLive(Duration.ofSeconds(900));
        apacheHttpClientBuilder.socketTimeout(Duration.ofSeconds(900));
        apacheHttpClientBuilder.tcpKeepAlive(true);
        SdkHttpClient sdkHttpClient = apacheHttpClientBuilder.build();

        RetryPolicy retryPolicy = RetryPolicy.builder().numRetries(0).build();
        ClientOverrideConfiguration clientOverrideConfiguration = ClientOverrideConfiguration.builder()
                .apiCallAttemptTimeout(Duration.ofSeconds(900))
                .apiCallTimeout(Duration.ofSeconds(900))
                .retryPolicy(retryPolicy)
                .build();

        LambdaClientBuilder lambdaClientBuilder = LambdaClient.builder();

        lambdaClientBuilder.overrideConfiguration(clientOverrideConfiguration);
        lambdaClientBuilder.httpClient(sdkHttpClient);

        LambdaClient lambdaClient = lambdaClientBuilder.build();
        InvokeRequest invokeRequest = InvokeRequest.builder().functionName(FUNCTION_NAME).build();
        InvokeResponse invokeResponse = lambdaClient.invoke(invokeRequest);

ApacheHttpClient class 来源于 https://mvnrepository.com/artifact/software.amazon.awssdk/apache-client

profile picture
EXPERTE
beantwortet vor 5 Monaten

Du bist nicht angemeldet. Anmelden um eine Antwort zu veröffentlichen.

Eine gute Antwort beantwortet die Frage klar, gibt konstruktives Feedback und fördert die berufliche Weiterentwicklung des Fragenstellers.

Richtlinien für die Beantwortung von Fragen