我用C#编写了一个小型Winforms应用程序,用于对触发Lambda函数的AWS websockets API进行负载测试。应用程序对API进行n调用,调用具有给定的周期,每个调用在请求中提交随机有效负载。不同的有效载荷导致Lambda函数的运行时间不同(在几分之一秒到几分钟之间(。
调用API包括以下步骤:
- 连接
- 发送包含凭据、路由操作和请求有效载荷(包含满足要求(
- 接收结果
- 断开连接
这些步骤在添加到List<Task>
的Task中执行。然后使用Task.WhenAll(taskList)
运行这些任务。简化(修订(代码如下。我已经做好了充分的准备,让比我了解更多的人告诉我这太可怕了。
async Task RunTest()//Triggered by a button.
{
List<Task> taskList = new List<Task>();
for (int i = 0; i < numberOfRequests; i++)
{
//Generate inputPayload string.
taskList.Add(CallAPI(inputPayload, i, i * period));
}
await Task.WhenAll(taskList);
}
public async Task CallAPI(Dictionary<string, double> requestBody, int requestNumber, int delay)
{
if (requestNumber > 0) await Task.Delay(delay);//No need to delay the first one (although 'delay' is 0 on the first one anyway).
using (ClientWebSocket websocketClient = new ClientWebSocket())
{
CancellationToken cancellationToken = new CancellationToken();
await websocketClient.ConnectAsync(new Uri("wss://..."), cancellationToken);//Exception is thrown at this line after a random number of tasks.
InputStructure requestPayload = new InputStructure
{
Action = "RouteThatCallsLambda",
Name = nameTextBox.Text,
ApiKey = apiKeyTextBox.Text,
ApiRequestBody = requestBody
};
while (websocketClient.State == System.Net.WebSockets.WebSocketState.Open)
{
byte[] messageBuffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(requestPayload));
await websocketClient.SendAsync(new ArraySegment<byte>(messageBuffer), System.Net.WebSockets.WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false);
break;
}
//All the 'record' bits do here is write stuff to a text box on the UI, and to a List<LogEntry> that I use to write out to a CSV file at the very end.
ArraySegment<byte> buffer;
System.Net.WebSockets.WebSocketReceiveResult receiveResult;
MemoryStream memoryStream;
while (websocketClient.State == System.Net.WebSockets.WebSocketState.Open)
{
buffer = new ArraySegment<byte>(new byte[8192]);
receiveResult = null;
memoryStream = new MemoryStream();
do
{
receiveResult = await websocketClient.ReceiveAsync(buffer, CancellationToken.None);
memoryStream.Write(buffer.Array, buffer.Offset, receiveResult.Count);
}
while (!receiveResult.EndOfMessage);
memoryStream.Seek(0, SeekOrigin.Begin);
if (receiveResult.MessageType == System.Net.WebSockets.WebSocketMessageType.Text)
{
StreamReader streamReader = new StreamReader(memoryStream, Encoding.UTF8);
string resultPayload = await streamReader.ReadToEndAsync();
//If successful, the payload will contain "validData".
if (resultPayload.Contains("validData"))
{
try
{
//Record the success.
}
catch
{
//Record the error (which in most cases would be a deserialisation exception).
}
await websocketClient.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
}
else if (resultPayload.Contains("ping"))
{
//Ignore - the Lambda function sends a message for long-running requests to keep the connection alive.
}
else //Failed.
{
//Record the error message sent by the Lambda function.
await websocketClient.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
}
}
break;
}
if (websocketClient.State == System.Net.WebSockets.WebSocketState.Closed)
{
//Record the connection closure.
}
}
if (requestNumber == numberOfRequests - 1)
{
//Record process complete.
}
}
我最多将numberOfRequests设置为100,但在websocketClient.ConnectAsync()
引发"无法连接到远程服务器"异常之前,它从未达到过100。在CloudWatch API日志流中,它报告"方法已完成,状态:410",这确实表明存在客户端问题,但我不知道为什么它会随机出现。
通常情况下,它会达到60到80之间,但有时只需要几次。因为它看起来是随机的,有时如果我将请求数设置为更少,它会一直成功运行。当我将它设置为1时,我从未见过任何问题。
有人知道发生了什么事吗?
更新:
[我最初发布以下内容是为了回答我自己的问题,但似乎它所做的只是使例外情况更加罕见。我不知道为什么会出现这种情况。]
看来我已经解决了这个问题。我在几个网站上看到了以下做事方式,但我认为这不会有什么不同。然而,基于我已经知道问题是由一些奇怪的线程问题引起的,我还是尝试了一下。
我将两个while (websocketClient.State == System.Net.WebSockets.WebSocketState.Open)
块移动到它们各自独立的异步任务中,一个用于发送消息,另一个用于接收结果。然后在websocketClient.ConnectAsync()
之后,我立即依次等待对每个人的调用,并传递必要的参数:
await websocketClient.ConnectAsync(new Uri("wss://..."), CancellationToken.None);
await SendMessage(websocketClient, requestBody);
await ReceiveMessage(websocketClient);
TLDR:在AWS上设置websocket API时,使用连接和断开连接路由的模拟端点,而不是Lambda函数,以返回200响应
解决了。"无法连接"错误似乎是我设置websocket API路由的次要结果。
在阅读了一篇关于这方面的文章之后,我设置了两个返回200响应的Lambda函数,它们充当$connect和$disconnect websocket路由的端点。其中包含一行返回{"statusCode":200}
的Javascript。
似乎发生的情况是,当调用"connect"Lambda函数时,它返回的是"速率超过"错误,而不是200响应,在客户端看来,这是"无法连接"。
解决方案:完全放弃Lambda函数,而是使用通过包含200响应的模板的mock端点。
所以现在我总是能够连接,因为我不需要调用Lambda函数来创建websocket连接。相反,当发送消息以调用进行实际数据处理的函数时,会出现"超出速率"错误,而且发生的情况要明显得多
从根本上讲,问题似乎是Lambda并发性仅设置为10,尽管文档中指出默认值为1000。我现在可以更好地证明加薪的请求。