任何亚马逊AWS Lambda Java函数在抛出未捕获的异常时花费不合理的长时间是否正常?请注意,这是关于Java中的Amazon Lambdas的一般性问题,因为我以非常通用的方式测试它,并且具有非常简单的裸骨功能。
例如,请考虑以下函数验证PIN。如果PIN有效,则返回文本:PIN is OK: "A"
。否则,它会抛出IOException
:
public class Hello implements RequestStreamHandler {
private static final int BUFFER_SIZE = 65_536;
private static final int MAX_SIZE = 262_144;
private static final String CHARSET_UTF8 = "UTF-8";
private static final byte[] buffer = new byte[BUFFER_SIZE];
private static final ByteArrayOutputStream baos = new ByteArrayOutputStream();
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
String input = readInputStreamToString(inputStream);
// PIN is valid.
if (input.equals("\"A\""))
writeStringToOutputStream(outputStream, "PIN is OK: " + input);
// PIN is not valid.
else
throw new IOException("PIN is wrong: " + input);
}
private String readInputStreamToString(InputStream inputStream) throws IOException {
baos.reset();
int length, total = 0;
while ((length = inputStream.read(buffer)) != -1) {
total += length;
if (total > MAX_SIZE) throw new IllegalStateException("InputStream bigger than " + MAX_SIZE + ".");
baos.write(buffer, 0, length);
}
return baos.toString(CHARSET_UTF8);
}
private void writeStringToOutputStream(OutputStream outputStream, String info) throws IOException {
byte[] chars = info.getBytes(CHARSET_UTF8);
outputStream.write(chars, 0, chars.length);
}
}
测试上面的代码:
要获得有效的个人识别码,请使用"A"
作为测试数据。
对于无效的PIN,请使用任何其他输入,例如:"B"
。
内存大小为128 MB,最大内存为48 MB。当PIN有效时,该功能非常快,并且在不到1 ms的时间内退出。但是,当PIN无效时,该功能在3秒内超时,我得到了这个:
{
"errorMessage": "2017-10-15T21:35:58.744Z *** Task timed out after 3.00 seconds",
"errorType": "java.lang.RuntimeException"
}
然后我将超时时间增加到10秒,现在它实际上完成了大约7.5秒并给了我一个堆栈跟踪:
{
"errorMessage": "PIN is wrong: \"B\"",
"errorType": "java.io.IOException",
"stackTrace": [ "example.Hello.handleRequest(Hello.java:83)" ]
}
我的问题:
1)lambda函数中的异常应该花费那么多时间让Amazon处理,这是正常的吗?为什么?如果没有,为什么我有这个问题?
2)处理异常的推荐方法是什么?我不应该让一个函数以异常结束吗?
答案 0 :(得分:1)
1) 别介意使用的最大内存。根据我的经验,一般来说,128 MB对于Java函数来说非常少,而不仅仅是由于JVM开销导致的异常。 你应该把它增加到至少4倍。但另外,请记住异常不是免费的:
见前面的问题:
Which part of throwing an Exception is expensive?
请注意,增加资源可能不一定意味着您需要更多成本,尤其是当您的功能受CPU限制时。你需要进行实验。
2)您可以根据应用程序可能抛出的异常返回特定的HTTP状态代码。但是这种灵活性将为您的代码添加一些样板。参见:
答案 1 :(得分:1)
我似乎发现了问题。亚马逊Lambda似乎做了某种内部的“异常初始化”,只有第一次它在容器中得到一个异常(要清楚,我的意思是一个异常,它没有被捕获 用户的处理程序,并允许冒泡到内部的Amazon Lambda代码。)
假设您有一些很少发出异常的代码,并且它会在1秒内运行,超时为3秒。如果此代码抛出未捕获的异常(由于错误或设计),Lambda将初始化其内部异常处理,对于128MB的最低内存配置大约需要7秒。 由于超时为3秒,因此没有时间完成,初始化将无法完成。下次抛出异常时,初始化将再次开始并再次超时。
如果提高内存,异常初始化将运行得更快,并可能在超时之前完成。另一种可能性是将超时限制提高到超过异常初始化完成所需的时间。一旦Lambda能够完成此异常初始化,就不必再次初始化(在此特定容器中)。随后的例外将非常快。
所有这一切的含义是你绝不允许异常泡到亚马逊(可能是通过在try / catch中包装句柄代码),否则超时应该足以让异常初始化完成(再增加7秒)通常需要,在128MB)。