从Java Lambda

时间:2018-05-21 21:00:00

标签: java performance aws-lambda amazon-dynamodb

如何减少Lambda& amp; DynamoDB?

这是一个Java lambda,使用AWS提供的SDK执行2次DynamoDB操作需要4s。我听说这些通常在<20ms内完成,但对我来说它高出3个数量级。这些长操作是(a)创建DynamoDB对象和(b)在完全空表(下面的代码)上执行表扫描。

我应该怎样做才能减少延迟?

我尝试过的事情

  • lambda和DynamoDB都在同一个区域(eu-west-1)。
  • 该表有5个RCU&amp; WCU。增加这些并没有帮助。
  • lambda使用的最大内存为92MB。如果我分配最小128MB,那么它会在15s后超时。将内存增加到512MB可实现每次呼叫4秒的时间,并再次将其增加到1GB,将其降低到每次呼叫2次。然而,对于一个琐碎的lambda来说,这是一个荒谬的内存量,但仍然让我的预期延迟时间超过200倍。
  • 表指标显示表扫描时间介于12ms和15ms之间。这就是我所期待的。即使有建立网络连接的开销,我仍然期待几十毫秒,而不是几秒钟。
  • 我在AWS控制台中使用测试功能触发lambda。我也试过通过API网关触发(这是我最终会做的)同样的结果。
  • 我已经尝试过几次快速连续调用lambda(以减少我遭受安装费用的可能性)。它没有帮助。
  • 记录显示lambda的所有其他部分运行得非常快(毫秒)。

代码段

创建DynamoDB对象

log("Creating AmazonDynamoDB");
AmazonDynamoDB db = AmazonDynamoDBClientBuilder                        
  .standard()
  .withRegion(Regions.EU_WEST_1)
  .build();

log("Creating DynamoDBMapper");
DynamoDBMapper mapper = new DynamoDBMapper(db);

执行扫描

log("Scanning table");
List<MyItem> items = dbMapper.scan(MyTable.class, new DynamoDBScanExpression());
for (MyItem item : items) {
        // Irrelevant - there aren't any
}
log("Table scan complete");

示例日志

以下是来自跑步的日志。

20:07:41 START RequestId: 9d436db7-5d32-11e8-8555-8564d2094ccc Version: $LATEST
20:07:41 Received request: APIGatewayRequest(path=/data/foo, httpMethod=POST, body=)
20:07:41 Creating AmazonDynamoDB
20:07:45 Creating DynamoDBMapper
20:07:45 Creating DataHandler
20:07:45 Handling request
20:07:45 Scanning table
20:07:49 Table scan complete
20:07:49 Request handled - response object: []
20:07:49 APIGatewayResponse(isBase64Encoded=false, statusCode=200, body=[], headers={})
20:07:49 END RequestId: 9d436db7-5d32-11e8-8555-8564d2094ccc
20:07:49 REPORT RequestId: 9d436db7-5d32-11e8-8555-8564d2094ccc Duration: 8256.47 ms Billed Duration: 8300 ms Memory Size: 512 MB Max Memory Used: 85 MB

3 个答案:

答案 0 :(得分:2)

根据AWS论坛上AWS员工的this post,构建AmazonDynamoDB对象非常昂贵。将构造(后退)移动到静态初始化器中并结合一点额外内存(= CPU)分配基本上可以解决问题。

来自日志的数据仍然显示上面确定的2个慢速步骤中的每一个都花费大约一半的时间。因此,大概AmazonDynamoDB对象的构造和首次使用都很慢。

显然,这对第一个请求仍然没有帮助,这个请求仍然需要与问题相同的时间。但是,一旦lambda变暖,后续请求需要~15ms (远低于最小计费阈值100ms)。解决第一个请求问题是well understood - 例如通过使用CloudWatch Events来安排对lambda的常规调用以使其保持温暖。

答案 1 :(得分:1)

(不是答案,但我希望它可以帮助其他人) 我已经在此处发布了更新,除了这些更新,我还必须对dynamoDb进行“虚拟”查询操作(以打开与之的连接),以防万一有人帮助,我的代码如下:

class MyFunctionHandler : RequestHandler<Map<String, Any>, ApiGatewayResponse> {

//var dbClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder.defaultClient()
var dbClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder
        .standard().withRegion("sa-east-1").build()

override fun handleRequest(input: Map<String, Any>, context: Context): ApiGatewayResponse {
    LOG.info("received input: $input")

    input["wakeup"]?.let {

        if (it == true) {

            with(EmpresaRepository(dbClient)) {
                LOG.info("### Connection was not stablished at this point")

                someDynamoQueryHere("dummyParameter")

                LOG.info("### The Connection was opened and will keep alived for 1 minute")
            }

            return buildResponseForWakeUpReq(input)
        }
    }

    val param = input["queryStringParameters"]?.toString()
...

随后将打开dynamoDb连接的操作将以毫秒为单位!

答案 2 :(得分:0)

所有aws ClientBuilder.build()函数在lambda中都需要花费一些时间,并且依赖于该函数专用的内存。但是,如果容器已经初始化,并且您是第二次或在后续请求中调用ClientBuilder.build(),则它以毫秒为单位而不是秒。