如何对使用 AWS 开发工具包调用 Lambda 函数时出现的重试和超时问题进行故障排查?

上次更新时间:2020 年 5 月 28 日

当我尝试使用 AWS 开发工具包调用 AWS Lambda 函数时,该函数超时、执行挂起,或者 API 操作重复。我如何解决这些问题?

简短描述

当发生以下情况时,会出现这些问题:

  • 您调用的远程 API 响应时间太长或者无法访问。
  • 您的 API 调用未在套接字超时内收到响应。
  • 您的 API 调用未在 Lambda 函数的超时期内收到响应。

注:当发生网络连接问题时,API 调用需要的时间可能比预期的长。网络问题还可能会造成重试和重复的 API 请求。要为这些情况做好准备,您的 Lambda 函数必须始终保持幂等性

如果您使用 AWS 开发工具包进行 API 调用且调用失败,开发工具包会自动重试调用。开发工具包重试的时间和次数由每个开发工具包间不同的设置决定。以下是这些设置的默认值:

注意:对于特定 AWS 服务,某些值可能会有所不同。

AWS 开发工具包 最大重试次数 连接超时 套接字超时
Python (Boto 3) 取决于服务 60 秒 60 秒
JavaScript/Node.js 取决于服务 不适用 120 秒
Java 3 10 秒 50 秒
.NET 4 100 秒 300 秒
Go 3 不适用 不适用

要解决重试和超时问题,请查看 API 调用日志以查找问题。然后,根据需要为每个使用案例更改开发工具包的重试次数和超时设置。要允许足够的时间对 API 调用作出响应,请添加时间至 Lambda 函数超时设置

解决方法

记录开发工具包进行的 API 调用

使用 Amazon CloudWatch Logs,您可以获取有关失败连接和重试次数的详细信息。有关更多信息,请参阅访问 Amazon CloudWatch Logs 了解 AWS Lambda。或者参阅所使用的开发工具包的说明:

在本错误日志示例中,API 调用建立连接失败(套接字超时):

START RequestId: b81e56a9-90e0-11e8-bfa8-b9f44c99e76d Version: $LATEST
2018-07-26T14:32:27.393Z    b81e56a9-90e0-11e8-bfa8-b9f44c99e76d    [AWS ec2 undefined 40.29s 3 retries] describeInstances({})
2018-07-26T14:32:27.393Z    b81e56a9-90e0-11e8-bfa8-b9f44c99e76d    { TimeoutError: Socket timed out without establishing a connection

...

在本错误日志示例中,连接成功,但在响应时间太久后,连接超时(连接超时):

START RequestId: 3c0523f4-9650-11e8-bd98-0df3c5cf9bd8 Version: $LATEST
2018-08-02T12:33:18.958Z    3c0523f4-9650-11e8-bd98-0df3c5cf9bd8    [AWS ec2 undefined 30.596s 3 retries] describeInstances({})
2018-08-02T12:33:18.978Z    3c0523f4-9650-11e8-bd98-0df3c5cf9bd8    { TimeoutError: Connection timed out after 30s

注:如果 API 调用未在您的 Lambda 函数超时内收到响应,则不会生成这些日志。如果执行因为函数超时而结束,则尝试下列各项之一:

  • 更改开发工具包的重试设置,以在超时内进行所有重试。
  • 暂时提高 Lambda 函数超时设置,以留出充足的时间来生成开发工具包日志。

更改开发工具包的设置

开发工具包的重试次数和超时设置应留出充足的时间来让 API 调用接收响应。要确定每个设置的正确值,测试不同的配置并获取以下信息:

  • 成功建立连接的平均时间
  • 完整的 API 请求所需的平均时间(直到它成功返回)
  • 重试应由开发工具包还是由代码进行

有关更改这些设置的更多信息,请参阅开发工具包客户端配置文档:

以下是一些示例,介绍如何为每个运行时更改这些设置:

注意:在使用以下任一示例命令之前,请将每个设置的示例值替换为适用于您的使用案例的值。

Python (Boto 3) 示例:

# max_attempts: retry count / read_timeout: socket timeout / connect_timeout: new connection timeout

from botocore.session import Session
from botocore.config import Config

s = Session()
c = s.create_client('s3', config=Config(connect_timeout=5, read_timeout=60, retries={'max_attempts': 2}))

JavaScript/Node.js 示例:

// maxRetries: retry count / timeout: socket timeout / connectTimeout: new connection timeout

var AWS = require('aws-sdk');

AWS.config.update({

    maxRetries: 2,

    httpOptions: {

        timeout: 30000,

        connectTimeout: 5000

    }

});

Java 示例:

// setMaxErrorRetry(): retry count / setSocketTimeout(): socket timeout / setConnectionTimeout(): new connection timeout

ClientConfiguration clientConfig = new ClientConfiguration(); 

clientConfig.setSocketTimeout(60000); 
clientConfig.setConnectionTimeout(5000);
clientConfig.setMaxErrorRetry(2);

AmazonDynamoDBClient ddb = new AmazonDynamoDBClient(credentialsProvider,clientConfig);

.NET 示例:

// MaxErrorRetry: retry count / ReadWriteTimeout: socket timeout / Timeout: new connection timeout

var client = new AmazonS3Client(

    new AmazonS3Config {
        Timeout = TimeSpan.FromSeconds(5),
        ReadWriteTimeout = TimeSpan.FromSeconds(60),
        MaxErrorRetry = 2
});

Go 重试示例:

// Create Session with MaxRetry configuration to be shared by multiple service clients.
sess := session.Must(session.NewSession(&aws.Config{
    MaxRetries: aws.Int(3),
}))
 
// Create S3 service client with a specific Region.
svc := s3.New(sess, &aws.Config{
    Region: aws.String("us-west-2"),
}) 

Go 请求超时示例:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// SQS ReceiveMessage
params := &sqs.ReceiveMessageInput{ ... }
req, resp := s.ReceiveMessageRequest(params)
req.HTTPRequest = req.HTTPRequest.WithContext(ctx)
err := req.Send()

(可选)更改您的 Lambda 函数超时设置

Lambda 函数超时低可能会导致运行状况良好的连接过早断开。如果您的使用案例发生此情况,请提高函数超时设置,以允许足够的时间让您的 API 调用收到响应。使用此公式估计函数超时所需的基本时间:

Retries * (Connection timeout + Socket timeout)

例如,假如开发工具包配置为进行 3 次重试,连接超时 10 秒且套接字超时 30 秒。在此情况下,您的 Lambda 函数超时应至少为 120 秒:

3 * (10 + 30) = 120 seconds

增加额外的时间界限(例如 20 秒),以处理余下的代码执行:

120 + 20 = 140 seconds

这篇文章对您有帮助吗?

我们可以改进什么?


需要更多帮助?