Amazon AWS Lambda Java函数在发生未捕获的异常时会花费不合理的时间来完成吗?

Amazon AWS Lambda Java函数在发生未捕获的异常时会花费不合理的时间来完成吗?

问题描述:

任何 Amazon AWS Lambda Java函数在抛出未捕获的异常时会花费不合理的时间来完成吗?请注意,这是一个有关亚马逊兰帕达在Java的一般性问题,因为我以非常通用的方式测试它,并带有一个非常简单的裸骨功能。Amazon AWS Lambda Java函数在发生未捕获的异常时会花费不合理的时间来完成吗?

例如,考虑下面的功能来验证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); 
     } 
    } 

为了检验上述代码:

  • 对于有效的PIN,使用"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)这是正常的,在拉姆达功能异常应该采取太多的时间让亚马逊来处理?为什么?如果没有,为什么我有这个问题?

2)什么是处理异常的推荐方法?我应该永远不让功能完成异常吗?

+3

没有任何代码我只是猜测,但你为什么抛出一个未经检查的异常? [Lambda文档](http://docs.aws.amazon.com/lambda/latest/dg/java-exceptions.html)尚不清楚,但我会尝试一个正常的例外。 – stdunbar

+0

您可能需要编辑问题并添加lambda函数代码,以便理解并回答无效方案。 –

+0

@stdunbar:一个未经检查的异常是一个正常的异常。但是,好的,如果你想让我用一个检查过的异常来测试它,现在我尝试了一个IOException(方法签名中的那个)。问题依然存在。 – MarcG

1) 没关系使用的最大内存。根据我的经验,对于一般的Java函数来说,128 MB是非常少的,而不仅仅是由于JVM开销引起的异常。 你应该增加它至少4倍。但同时,请记住,异常是不是免费的:

见前面的问题:

How slow are Java exceptions?

Which part of throwing an Exception is expensive?

请注意,增加的资源不一定对你来说意味着更多的成本,尤其是当你的功能是CPU限制的。你需要运行实验。

2)您可以根据应用程序可能抛出的异常返回特定的HTTP状态码。但是这种灵活性会为你的代码添加一些样板。请参阅:

http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html?shortFooter=true#api-gateway-proxy-integration-lambda-function-java

+0

例外情况不是免费的,但他们也不花费7秒。这个问题是Amazon Lambda在发生异常时所做的内部事情。我测试了更多的内存,似乎我发现了这个问题。谢谢。 – MarcG

我似乎发现了问题。亚马逊Lambda似乎做了一些内部的“异常初始化”,只有第一次它在容器中得到一个异常(很明显,我的意思是一个异常不被 用户的处理程序捕获,并且被允许冒泡到内部的Amazon Lambda代码)。

因此,假设您有一些很少发出异常的代码,并且它在1秒内运行,并且超时时间为3秒。如果此代码引发未捕获的异常(由于错误或设计原因),Lambda将初始化其内部异常处理,对于128MB的最低内存配置需要大约7秒。 由于超时时间为3秒,因此没有时间完成,并且初始化将不会完成。下次引发异常时,初始化将再次启动并再次超时。

如果您提高内存,异常初始化将运行得更快,并可能在超时之前完成。另一种可能性是将超时限制提高到超过异常初始化完成所需的时间。一旦Lambda能够完成这个异常初始化,它就不必再次初始化(在这个特定的容器中)。随后的例外将会非常快。

所有这一切的含义是,你绝不允许异常冒泡到亚马逊(可能通过在try/catch中包装句柄代码),或者超时应该足以让异常初始化完成(添加7秒超过通常所需的128MB)。