AWS Lambda与SQS的连接超时



我正在执行一项任务,该任务涉及在VPC内运行的Lambda函数

此函数应将消息推送到SQS,lambda执行角色具有策略:AWSLambdaSQSQueExecutionRoleAWSLambdaVPCAccessExecutionRole已添加。

Lambda函数:

# Create SQS client
sqs = boto3.client('sqs')
queue_url = 'https://sqs.ap-east-1a.amazonaws.com/073x08xx43xx37/xyz-queue'
# Send message to SQS queue
response = sqs.send_message(
QueueUrl=queue_url,
DelaySeconds=10,
MessageAttributes={
'Title': {
'DataType': 'String',
'StringValue': 'Tes1'
},
'Author': {
'DataType': 'String',
'StringValue': 'Test2'
},
'WeeksOn': {
'DataType': 'Number',
'StringValue': '1'
}
},
MessageBody=(
'Testing'
)
)
print(response['MessageId'])

在测试时,执行结果如下:

{
"errorMessage": "2020-07-24T12:12:15.924Z f8e794fc-59ba-43bd-8fee-57f417fa50c9 Task timed out after 3.00 seconds"
}

我将超时从基本设置增加到5秒&10秒。但错误不断出现。

如果有人过去遇到过类似的问题,或者知道如何解决这个问题,请帮助我。

提前谢谢你。

当AWS Lambda函数被配置为使用亚马逊专有网络时,它会连接到该专有网络的指定子网。这允许Lambda功能与VPC内的其他资源进行通信。但是,它无法与Internet通信。这是一个问题,因为AmazonSQS公共端点位于互联网上,而该功能由于无法访问互联网而超时。

因此,您有3个选项:

选项1:不连接到VPC

如果您的Lambda函数不需要与VPC中的资源通信(例如您上面提供的简单函数),请简单地不要将其连接到VPC。当Lambda函数连接到VPC时,它可以与互联网和Amazon SQS公共端点进行通信。

选项2:使用VPC端点

VPC端点提供了一种访问AWS服务的方式,而无需通过互联网。您可以为Amazon SQS配置一个VPC端点。然后,当Lambda函数希望与SQS队列连接时,它可以通过端点而不是通过互联网访问SQS。如果Lambda函数需要与VPC中的其他资源进行通信,这通常是一个不错的选择。

选项3:使用NAT网关

如果Lambda功能配置为使用私有子网,则如果在公共子网中设置了NAT网关并且私有子网的路由表指向NAT网关,则它将能够访问Internet。这涉及到额外的费用,并且只有在对NAT网关有额外需求的情况下才是值得的。

如果您在VPC的lambda中使用boto3 python库,并且它无法通过VPC端点连接到sqs队列,则在创建sqs客户端时必须设置endpoint_url。第1900期描述了这背后的背景。

解决方案如下(对于us-east-1中的sqs-vpc端点):

sqs_client = boto3.client('sqs',
endpoint_url='https://sqs.us-east-1.amazonaws.com')

然后正常调用send_message或send_message_batch。

您需要将lambda放置在VPC内,然后为SQS或NAT网关设置VPC端点。当您将lambda函数添加到子网时,请确保仅将其添加到私有子网,否则将无法工作。

参考

https://docs.aws.amazon.com/lambda/latest/dg/vpc.html

https://aws.amazon.com/premiumsupport/knowledge-center/internet-access-lambda-function/

我非常确信,您不能使用SQS端点使用Lambda从VPC内调用SQS队列。我认为这是一个错误,但也许Lambda团队这样做是有原因的。在任何情况下,您都会收到一个消息超时。我做了一个简单的测试Lambda

import json
import boto3
import socket
def lambda_handler(event, context):
print('lambda-test SQS...')
sqsDomain='sqs.us-west-2.amazonaws.com'

addr1 = socket.gethostbyname(sqsDomain)
print('%s=%s' %(sqsDomain, addr1))

print('Creating sqs client...')
sqs = boto3.client('sqs')

print('Sending Test Message...')
response = sqs.send_message(
QueueUrl='https://sqs.us-west-2.amazonaws.com/1234567890/testq.fifo',
MessageBody='Test SQS Lambda!',
MessageGroupId='test')

print('SQS send response: %s' % response)
return {
'statusCode': 200,
'body': json.dumps(response)
}

我创建了一个VPC、子网等-配置一个Lambda函数来访问VPC中的资源。根据本教程,本示例中的EC2实例通过CLI的专用端点调用SQS没有问题。

如果我将上面的简单Lambda放入同一VPC和子网,具有SQS发布权限等,并调用测试功能,它将正确解析子网内SQS端点的IP地址,但调用会超时(确保您的Lambda超时超过60秒,以使boto失败)。启用boto调试日志记录可以进一步确认IP已正确解析,并且对SQS的HTTP请求超时。

我没有在非FIFO队列中尝试过,但由于HTTP调用在连接请求时失败,这应该无关紧要。这肯定是Lambda的路由问题,因为同一子网中的EC2可以工作。

我修改了我的简单Lambda,添加了一个SNS端点,并进行了同样的测试。这个问题似乎是我所能说的SQS特有的。

import json
import boto3
import socket
def testSqs():
print('lambda-test SQS...')
sqsDomain='sqs.us-west-2.amazonaws.com'

addr1 = socket.gethostbyname(sqsDomain)
print('%s=%s' %(sqsDomain, addr1))

print('Creating sqs client...')
sqs = boto3.client('sqs')

print('Sending Test Message...')
response = sqs.send_message(
QueueUrl='https://sqs.us-west-2.amazonaws.com/1234567890/testq.fifo',
MessageBody='Test SQS Lambda!',
MessageGroupId='test')

print('SQS send response: %s' % response)
return {
'statusCode': 200,
'body': json.dumps(response)
}

def testSns():
print('lambda-test SNS...')
print('Creating sns client...')
sns = boto3.client('sns')

print('Sending Test Message...')
response = sns.publish(
TopicArn='arn:aws:sns:us-west-2:1234567890:lambda-test',
Message='Test SQS Lambda!'
)

print('SNS send response: %s' % response)
return {
'statusCode': 200,
'body': json.dumps(response)
}

def lambda_handler(event, context):
#return testSqs()
return testSns()

我认为你唯一的选择是NAT(根据上面的John),从本地EC2上注销你的呼叫(NAT会更简单、更便宜、更可靠),或者在VPC之外使用Lambda代理。其他人在类似的帖子中建议。你也可以为一个SNS主题订阅一个SQS队列(我对它进行了原型化,它很有效),并以这种方式发送出去,但这似乎很愚蠢,除非你出于某种模糊的原因必须拥有SQS。

我转到了SNS。我只是希望能在SQS方面获得更多的经验。希望有人能证明我错了,但我称之为bug。

最新更新