【发布时间】:2021-09-07 05:53:48
【问题描述】:
我的琐碎的 .net C# AWS Lambda 函数,由 HTTP GET 触发到 API GW 运行,但在发送到 AWS SQS 时返回“连接被拒绝”
responseSendMsg = await sqsClient.SendMessageAsync(sendMessageRequest);
似乎对 SQS 的调用是问题所在。
这里是完整代码,90% 由 Visual Studio 2019 生成,AWS Toolkit for Visual Studio,新的 AWS 项目模板“AWS Serverless Application (.NET Core - C#)”,我只有:
- 将 lambda 函数处理程序方法签名更新为异步 通过一个任务,我遵循https://docs.aws.amazon.com/lambda/latest/dg/csharp-handler.html#csharp-handler-async 中记录的模式
public async Task<APIGatewayProxyResponse> Get(APIGatewayProxyRequest request, ILambdaContext context)
- 添加到写入 SQS 的代码中
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Amazon.Lambda.Core;
using Amazon.Lambda.APIGatewayEvents;
using Amazon.SQS;
using TEST.SQS;
using Amazon.SQS.Model;
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace TestAsyncFromLambda
{
public class Functions
{
public Functions()
{
}
public async Task<APIGatewayProxyResponse> Get(APIGatewayProxyRequest request, ILambdaContext context)
{
Console.WriteLine("Get Request\n");
// Call a local async method to prove that works, leaving SQS out of the situation for the moment
await Method1();
// Create the Amazon SQS client
var clientConfig = new AmazonSQSConfig
{
ServiceURL = SQSConstants.AWS_SERVICE_URL,
};
var sqsClient = new AmazonSQSClient(clientConfig);
// Create and initialize a SendMessageRequest instance
SendMessageRequest sendMessageRequest = new SendMessageRequest();
sendMessageRequest.QueueUrl = SQSConstants.MyQueueUrl;
sendMessageRequest.MessageBody = "this is a test message";
Console.WriteLine($"About to send message to queue: {sendMessageRequest.QueueUrl}");
// Send the SQS message using "await"
SendMessageResponse responseSendMsg = null;
try
{
responseSendMsg =
await sqsClient.SendMessageAsync(sendMessageRequest);
//responseSendMsg =
//sqsClient.SendMessageAsync(sendMessageRequest).GetAwaiter().GetResult(); ;
}
catch (Exception e)
{
Console.WriteLine(e);
throw e;
}
Console.WriteLine("SendMessage() complete");
var response = new APIGatewayProxyResponse
{
StatusCode = (int)HttpStatusCode.OK,
Body = "Hello AWS Serverless",
Headers = new Dictionary<string, string> { { "Content-Type", "text/plain" } }
};
return response;
}
public static async Task Method1()
{
await Task.Run(() =>
{
int iterations = 3;
for (int i = 1; i <= iterations; i++)
{
Console.WriteLine(" Method 1, i=" + i + " of " + iterations);
// Do something
Task.Delay(100).Wait();
}
});
}
}
}
显然这是一个简化的示例,我计划在函数中进行大量 SQS 处理,并期望使用 async 会有所帮助。
云监控日志显示该函数已被触发,到达发送到 SQS 调用,然后抛出异常:
System.Net.Http.HttpRequestException: Connection refused
2021-06-24T16:13:54.751+10:00 START RequestId: MyID-91a3-f1e61c3c3f7a Version: $LATEST
2021-06-24T16:13:55.240+10:00 Get Request
2021-06-24T16:13:55.261+10:00 Method 1, i=1 of 3
2021-06-24T16:13:55.380+10:00 Method 1, i=2 of 3
2021-06-24T16:13:55.481+10:00 Method 1, i=3 of 3
2021-06-24T16:13:56.160+10:00 About to send message to queue: https://sqs.ap-southeast-2.amazonaws.com/MyID/MyQueue.fifo
2021-06-24T16:14:32.503+10:00 System.Net.Http.HttpRequestException: Connection refused
2021-06-24T16:14:32.503+10:00 ---> System.Net.Sockets.SocketException (111): Connection refused
2021-06-24T16:14:32.503+10:00 at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
2021-06-24T16:14:32.503+10:00 --- End of inner exception stack trace ---
2021-06-24T16:14:32.503+10:00 at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
2021-06-24T16:14:32.503+10:00 at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
2021-06-24T16:14:32.503+10:00 at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
2021-06-24T16:14:32.503+10:00 at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
2021-06-24T16:14:32.503+10:00 at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
2021-06-24T16:14:32.503+10:00 at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
2021-06-24T16:14:32.503+10:00 at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.HttpWebRequestMessage.GetResponseAsync(CancellationToken cancellationToken)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.HttpHandler`1.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.Unmarshaller.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.SQS.Internal.ValidationResponseHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.ErrorHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.ErrorHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.EndpointDiscoveryHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.EndpointDiscoveryHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.CredentialsRetriever.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.RetryHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.RetryHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.ErrorCallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at Amazon.Runtime.Internal.MetricsHandler.InvokeAsync[T](IExecutionContext executionContext)
2021-06-24T16:14:32.503+10:00 at TestAsyncFromLambda.Functions.Get(APIGatewayProxyRequest request, ILambdaContext context) in
我还尝试了 C# async await 语法的其他一些排列,结果相同。
responseSendMsg = sqsClient.SendMessageAsync(sendMessageRequest).GetAwaiter().GetResult(); ;
API GW、Lambda 或 SQS 不涉及 VPC,一切都在同一个 AWS 账户中,只是一个基本的简单设置。
我还做了一个测试,我将 Lambda 发送到的 SQS 中的队列 URL 更改为不存在的无效队列名称,并且我得到了相同的行为,因此这意味着 SQS 发送请求可能永远不会得到到 AWS。
对于适用于 SQS 的 AWS .net 开发工具包,在使用 SQS 的同步和异步方法之间没有选择,只有异步:https://docs.aws.amazon.com/sdk-for-net/latest/developer-guide/SendMessage.html。
Lambda 函数附加到具有这些权限的角色,我认为这应该足够了: 我什至添加了“AdministratorAccess”角色,所以我假设只要 Lambda 和 SQS 在同一个 AWS 账户上,Lambda 就可以访问 SQS。
文件 aws-lambda-tools-defaults.json 有:
{
"Information" : [
"This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
"To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",
"dotnet lambda help",
"All the command line options for the Lambda command can be specified in this file."
],
"profile" : "myProfile",
"region" : "ap-southeast-2",
"configuration" : "Release",
"framework" : "netcoreapp3.1",
"s3-prefix" : "TestAsyncFromLambda/",
"template" : "serverless.template",
"template-parameters" : "",
"s3-bucket" : "awsserverless2stack-bucket-myID",
"stack-name" : "TestAsyncFromLambda"
}
文件 serverless.template 是:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Description": "An AWS Serverless Application by matt.",
"Resources": {
"Get": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "TestAsyncFromLambda::TestAsyncFromLambda.Functions::Get",
"Runtime": "dotnetcore3.1",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 187,
"Role": null,
"Policies": [
"AWSLambdaBasicExecutionRole",
"AmazonSQSFullAccess",
"AdministratorAccess"
],
"Events": {
"RootGet": {
"Type": "Api",
"Properties": {
"Path": "/",
"Method": "GET"
}
}
}
}
}
},
"Outputs": {
"ApiURL": {
"Description": "API endpoint URL for Prod environment",
"Value": {
"Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
}
}
}
}
我已尝试通过 AWS 控制台 Web UI 和 Cloud Formation 脚本手动创建队列,示例取自 here:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"MyQueue": {
"Properties": {
"QueueName": "MyQueue.fifo",
"FifoQueue": true,
"ContentBasedDeduplication": true
},
"Type": "AWS::SQS::Queue"
}
},
"Outputs": {
"QueueName": {
"Description": "The name of the queue",
"Value": {
"Fn::GetAtt": [
"MyQueue",
"QueueName"
]
}
},
"QueueURL": {
"Description": "The URL of the queue",
"Value": {
"Ref": "MyQueue"
}
},
"QueueARN": {
"Description": "The ARN of the queue",
"Value": {
"Fn::GetAtt": [
"MyQueue",
"Arn"
]
}
}
}
}
我将 lambda 超时增加到一个长自定义值:
- 将该持续时间内的超时与其他超时区分开来
- 给 SQS 发送更多时间重试和/或超时
我通过更改 serverless.template 来增加超时:
"Timeout": 187,
在增加超时之前,当它是默认的 30 秒时,我在云观察日志中没有异常,就像 Lambda 超时一样。
【问题讨论】:
-
您的 Lambda 是否在 VPC 中运行?
-
如果不是等待,你
.GetAwaiter().GetResult();?这应该迫使它同步运行。 -
我认为问题不在于
await。我一直在运行asyncC# Lambda 处理程序。 IME 90% 以上的超时都与网络相关,这就是我询问 VPC 的原因。为了获得更多帮助,我建议显示有关 Lambda 函数配置的所有信息。 -
这个 url
https://sqs.ap-southeast-2.amazonaws.com/MyID/MyQueue.fifo是 SQS 的正确端点吗? -
SQSConstants.AWS_SERVICE_URL和SQSConstants.MyQueueUrl中有哪些值?
标签: c# .net amazon-web-services aws-lambda amazon-sqs