亚马逊AWS官方博客

Lambda 预配置并发介绍

在使用Lambda之前,大家经常会顾虑的一个问题就是Lambda的冷启动的问题,本文将从Lambda内部结构以及Lambda的执行过程出发,首先介绍冷启动产生的原因以及发生的环节,进一步介绍Lambda预配置并发如何来解决冷启动的问题以及如何配置,最后通过一个例子延时启动预配置并发前后的区别。

Lambda 内部剖析

在介绍Lambda预配置并发之前,我们先来看下Lambda 的内部结构,通过内部结构的了解我们可以更好的理解为什么要使用预定配置并发
图1 我们从外往里来看:

  1. 首先是Lambda运行的计算资源环境,也就是其运行的物理资源环境。
  2. Lambda服务层,是Lambda对外的统一入口,用于管理工作负载的调度和组织,Lambda服务层和Lambda计算资源层都是由AWS完全管理的,对于使用者来讲是不可见的。
  3. 进一步是执行环境,每一个客户的AWS账户对应独立的执行环境,执行环境是用于运行客户代码的安全空间,同时每个执行环境都有一个语言运行环境。
  4. 语言运行环境,提供了一系列的语言运行时,包括 Node.js,Python,Java,.NET 核心,Go 和 Ruby。 同时您也可以自定义语言运行时比如PHP环境。
  5. 最后才是运行的具体代码,可以是一段简单的代码,也可以是一个项目包含业务代码,依赖并通过压缩包的方式发布。

函数生命周期

下来我们在从函数的生命周期角度了解一下,lambda函数的执行过程,同时了解启动过程中的一个关键概念“冷启动”,这个是我们后续介绍预配置并发以及VPC网络优化两个功能要解决的核心问题。
图2 下面介绍一下,当一个用户第一次请Lambda 的过程:

  1. 当第一次请求到达时,Lambda 服务首先下载代码,然后启动一个用于运行Lambda函数的执行环境,其中包含了指定的语言运行时环境(这个过程称为全冷启动)
  2. 然后代码在运行时环境中开始初始化,比如代码引入的依赖,数据库连接的创建等(这个过程称为部分冷启动)。
  3. 最终进入函数的Handler方法开始执行代码逻辑(这个过程称为热启动)

首次运行函数时,所有这些过程都会执行 ,这个被称为完全”冷启动”。如果再次调用时后续的请求仅仅执行代码,这个过程速度要快很多,这个过程被称为“热启动”。 针对前半部分的全冷启动优化由AWS负责,针对初始化代码部分的优化由您负责。

Lambda 预配置并发介绍

预制并发是通过在使用之前,提前创建好执行环境的方式来运行初始化代码的,从而通过缩短冷启动时间来提高性能。Lambda 预配置并发的使用也非常简单,进入Lambda 页面,然后选中要配置的函数,下面是配置界面配置了50个预配置并发:
图3 您只需要指定容量,也就是说指定执行环境的数量,您可以通过lambda 控制台,Cloudformation,SAM来实现预配置并发的配置。预制并发对于延迟非常敏感的交互式工作负载非常有用,将运行函数处理程序的时间缩短到两位数毫秒。 下来我们看下启动预配置并发后的Lambda函数执行过程:
图4 从上图可以看出相较于图2,启动预配置并发后,执行过程没有了下载代码,启动执行环境,初始化代码等部分的耗时,程序运行将更高效。

代码验证

下面我们通过一个例子来对比一下使用Lambda 预配置并发前后的对比,同时通过X-Ray监控深入的看下执行时间是否有所变化。

测试步骤:

  1. 编写java lambda函数,通过 sleep(200) 模拟程序执行过程耗时为200ms
  2. 使用apache ab 实现压力测试,来模拟用户请求压力
  3. 添加预配置并发,然后再次测试
  4. 对比ab测试数据,分析X-Ray监控数据

准备工作

  • 安装AWS CLI:用于通过命令行方式使用AWS服务
  • 安装AWS SAM(Serverless Application Modle):用于自动化部署lambda
  • 安装 apache ab :用于模拟压力测试
  • 了解X-Ray:用于实现Lambda函数调用的分布式跟踪

Apache ab介绍

Apache ab: HTTP server benchmarking tool ab是Apache提供的一款小巧的压力测试工具,用来测试服务能力很方便。

ab -n 50 -c 50 http://127.0.0.1:8080/setUrl  -n 表示执行多少个请求 ( -n requests  Number of requests to perform)  -c 表示同一时间有多少个并发。( -c concurrency  Number of multiple requests to make at a time)

Sam使用流程

https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started-hello-world.html #步骤 1 – 初始化项目 sam init

#步骤 2 – 构建应用程序 cd sam-app //程序名称在步骤1中设定 sam build sam build

#步骤 3 -部署应用程序 sam deploy —guided

Lambda测试代码编写

代码通过sleep模拟程序 执行200ms时间

        try {
            long startTime = System.currentTimeMillis();
            Thread.sleep(200);
            long elapsTime = System.currentTimeMillis()-startTime;

            String output = String.format("{ \"elapsTime\": "+elapsTime+" }");

            return response
                    .withStatusCode(200)
                    .withBody(output);
        } catch (InterruptedException e) {
            return response
                    .withBody("{}")
                    .withStatusCode(500);
        }

SAM 模版配置:

配置函数发布到别名live,sam中添加AutoPublishAlias:live

Resources:
  PCTestFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: PCTestFunction
      Handler: pctest.App::handleRequest
      Runtime: java8
      MemorySize: 512
      Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
        Variables:
          PARAM1: VALUE
      Events:
        PCTest:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /pctest
            Method: get
      AutoPublishAlias: live

压力测试

下来我们通过Apache ab 来进行压力测试 ,下面我们模拟 执行1000个请求,并发50个请求

启用PC之前

测试脚本:

ab -n 1000 -c 50 https://xxxxx.xxx.amazonaws.com/Prod/pctest //api地址需要根据实际替换

下面通过X-Ray 监控查看细节:

响应时间分布:

从响应时间分布可以看出部分响应时间是超过1s的。

查看延时高的请求:

下来我们查看具体超过1s的请求,点击第二条记录

查看延时细节:

下来我们具体看下是哪些环节,引起总耗时超过1s的,下图是通过X-Ray获取的分布式跟踪监控图,展示了当前请求从调用lambda到lambda内部的执行耗时细节。

  • 从上图可以看出 AWS:Lambda 执行使用了1.2秒,这个时间是Lambda 服务的消耗时间(这里包含了图2中的全冷启动时间)。
  • AWS:Lambda:Function 是Lambda 函数级别的耗时,其中initialization 代码初始化,这个是在handler函数执行前的静态代码初始时间(也就是图2中的部分冷启动时间)

启用PC之后

测试脚本:

ab -n 1000 -c 50 https://xxxx.execute-api.xxx.amazonaws.com/Prod/pctest

下面通过X-Ray 监控查看细节:

响应时间分布:

查看延时高的请求:

查看延时细节:

  • 从上图可以看出 AWS:Lambda 执行使用了383毫秒,这个时间是Lambda 服务的消耗时间(与启动预配置并发之前相比时缩短了近800ms)。
  • AWS:Lambda:Function 是Lambda 函数级别的耗时,与启动预配置并发之前相比少了 initialization 的时间(也就是部分冷启动时间)

预配并发与Autoscaling集成

下来我们看下如何通过结合Autoscaling,实现更高效的使用预配置并发,比如对于未来的并发设置多大并不确定的情况下可以结合使用Autoscaling,采用目标跟踪或者定期扩展能力来实现自动扩所预配置并发。 例如,我想将预配并发的利用率保持在 70% 左右。使用 AWS 无服务器应用程序模型 (SAM) 和 SAM CLI 来测试、部署和管理应用程序。下面是集成Autoscaling后的SAM模版:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  lambda-pc-test

  Sample SAM Template for lambda-pc-test

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 20

Resources:
  PCTestFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: PCTestFunction
      Handler: pctest.App::handleRequest
      Runtime: java8
      MemorySize: 512
      Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
        Variables:
          ENVIRONMENT: test
      Events:
        PCTest:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /pctest
            Method: get
      AutoPublishAlias: live
      DeploymentPreference:
        Type: AllAtOnce # Or Canary10Percent5Minutes, Linear10PercentEvery1Minute, ...
        ProvisionedConcurrencyConfig:
          ProvisionedConcurrentExecutions: 1

  MyScalableTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Properties:
      MaxCapacity: 100
      MinCapacity: 1
      ResourceId: !Sub function:${PCTestFunction}:live # You need to specify an alis or version here
      RoleARN: !Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/lambda.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency
      ScalableDimension: lambda:function:ProvisionedConcurrency
      ServiceNamespace: lambda
    DependsOn: PCTestFunctionAliaslive # This is your function logical ID + "Alias" + what you use for AutoPublishAlias

  MyTargetTrackingScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: utilization
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref MyScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 0.70 # Any value between 0.1 and 0.9 can be used here
        PredefinedMetricSpecification:
          PredefinedMetricType: LambdaProvisionedConcurrencyUtilization

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  PCTestApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  PCTestFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt PCTestFunction.Arn
  PCTestFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt PCTestFunction.Arn

SAM中增加了Lambda 部署规则(DeploymentPreference),这里采用一次性发布也可以使用灰度发布(Lambda codeDeplo集成参考https://docs.amazonaws.cn/codedeploy/latest/userguide/tutorial-lambda-sam.html)。同时初始化设置预配置并为1,设置目标跟踪的目标值为0.7,跟踪指标为LambdaProvisionedConcurrencyUtilization。 更新sam文件后,在命令行窗口执行 sam build 构建应用,然后通过sam deploy —guided 部署新版本应用,查看Lambda应用程序发现此次部署新增了ServerlessDeploymentApplication资源,如下图:
点击如上红框链接,查看部署进展:

测试自动扩展

测试脚本:

ab -n 10000 -c 50 https://xxx.execute-api.xx.amazonaws.com/Prod/pctest

为了便于模拟持续调用,用于监控利用率 这里调用次数调整为10000。

查看Lambda的预配置并发监控

注意这里需要选择别名为live的函数才会有ProvisionedConcurrencyUtilization 指标,这个是因为预配置并发不能做用于最新版本,默认显示的是最新版本的Lambda。

查看Lambda并发数量

通过如上监控可以看到 预配置并发大利用率(ProvisionedConcurrencyUtilization)指标一直处于100%,超出我们配置的70%因此会触发扩展,切换到Lambda页面查看当前预配置并发,如下图展示已经在扩展了:
持续一段时间后再次查看由于利用率还是100%,继续出发扩展下面是已经扩展到3个并发,调用持续并发扩展到5个,具体见如下截图:

总结

本文从Lambda的内部结构开始,介绍了Lambda的执行过程以及冷启动,同时介绍了冷启动产生的原因。在知道冷启动的原因后进一步介绍了预配置并发如何解也冷启动的问题,以及预配置并发的运行原理,最后通过一个示例程序结合ab测试工具以及X-ray分布式跟踪工具,深入的分析启动预配置并发前后的区别,通过X-ray监控查看调用内部的延时来观察PC的效果,希望通过本文的介绍大家可以了解预配置并发的作用,以及如何配置预配置并发来提高系统运行效率。 测试代码地址:https://github.com/VerRan/lambda-pc-test

本篇作者

刘恒涛

AWS 解决方案架构师,负责基于AWS的云计算方案架构咨询和设计。同时致力于AWS云服务在国内的应用和推广。