Java 11中的AWS Lambda付诸实践

本文是一个如何通过一个简单示例快速轻松地开始使用AWS Lambda的指南。适合完全不使用Lambda的开发人员,以及知道Cloud可以评估开发无服务器应用程序的另一种愿景的开发人员。

图片


介绍


大家好。
我的名字叫Alexander Gruzdev,我是DINS的Java团队负责人。两年多来,我一直与AWS基础架构紧密合作,并且在编写适用于AWS的应用程序以及部署这些应用程序方面都有经验。在我的工作中,我不得不使用ElasticBeanstalk,ECS,Fargate,EKS,当然还有AWS Lambda。

这些服务中的每一个都以其自己的方式很好,并且在本文的框架内,我不会敦促仅使用AWS Lambda替代所有其他选项。我只想展示开始使用Lambda的过程是多么容易,在什么情况下您会从中受益。

从开发人员到管理人员,每个人都需要一个清晰的过程来为客户交付更改。该路径越透明,需要的工作量越少,利润就越高。大多数情况下,开发人员和测试人员不希望知道应用程序将部署在何处,选择哪种硬件,要将副本放置在哪个区域等。在下图中,您可以看到“ aaS”服务类(作为服务),其中AWS Lambda代表FaaS类别。

图片

概述
FaaS , . , , CRUD C, R, U, D Create Read. , — .


理念


为了演示无服务器解决方案的发展,我决定举一个平庸的例子。我认为您已经在许多站点上看到了“与我们联系”反馈表,您可以在其中留下您的邮件或电话并提出问题以待日后回答。在下面的屏幕上,表单不是超级,但本文的主题不是材料设计。


通过填写此表格,潜在客户将等待您的来电或来信。
从技术方面,您需要:
  • 将数据从表单保存到数据库;
  • 寄信给将回答问题的代理商;
  • 也许编写另一个服务,代理将在该服务上标记基于同一数据库完成的工作(不包括在本文范围内)。


这个想法的实现可以在GitHub找到

建筑


在实现过程中首先想到的是我们需要的:
  • 机器背面,正面,图像等下方,
  • 带底座车
  • 机带有电子邮件服务。




需要很好地复制背面和底面,但是假设我们是在我们的迷你商店中这样做的,那么此表单上的负载是最小的。因此,所有这些机器都可以组合为一个,并在单个集合中部署。
但是,由于我们正在考虑无服务器,因此让我们完全在无服务器组件上构建体系结构。
无论Serverless听起来多么有前途,我们都知道服务器仍在这里,它们只是从您的视野中消失了。现在,您可以完成云工作,而您可以自己完成琐事。但是,让我们尝试仅使用此类无服务器组件:


该图显示了每个组件功能的非常清晰的分离。
  • AWS Lambda是包含代码/逻辑的主要组件,
  • S3负责存储静态资源和JS脚本,
  • CloudFront-用于缓存机制和多区域支持,
  • SES-电子邮件服务,
  • DynamoDB-用于存储表单数据的基础(从谁那里,什么问题,在哪里发送答案),
  • 网关API-用于我们的lambda的HTTP API,
  • Route53-如果要添加漂亮的域名,则需要。


并非所有这些组件都将在我们的下一个指南中使用,只是为了避免拉伸本文。
Route53和CloudFront是相当简单的组件,您可以分别阅读。
一些破坏者将为我们提供这样的解决方案:
  • 我们不再支持EC2机器,不再通过ssh进行调试,
  • 轻松配置:一键配置限制/缓存/访问,
  • 支持访问策略:限制权限并授予访问权限,
  • 开箱即用地记录/监视,
  • 只为已使用的资源/请求付费。


演示版


训练


要开始开发我们的无服务器解决方案,您必须满足以下要求:


安装完上述所有工具后,您需要配置aws-cli以远程访问AWS。请按照说明进行操作这将需要创建一个新用户并卸载其访问密钥和秘密密钥。

项目组装


1.从模板创建项目


打开未来项目的目录,然后从中启动SAM CLI。请按照说明进行操作:


注意:根据SAM-CLI版本,命令可能会略有不同,但是它们都保持直观。只需选择与上面使用的最相似的那些即可。如果Gradle不适合您,您还可以选择其他构建工具。

项目“你好,世界!” 准备。现在,您可以处理项目的名称以及程序包,依赖项和源代码。

2.让我们应对成瘾


将以下依赖项添加到build.gradle:
dependencies {
    // AWS
    implementation group: 'com.amazonaws.serverless', name: 'aws-serverless-java-container-core', version: '1.4'
    implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.0'
    implementation group: 'com.amazonaws', name: 'aws-java-sdk-ses', version: '1.11.670'
    implementation group: 'com.amazonaws', name: 'aws-java-sdk-dynamodb', version: '1.11.670'
    implementation group: 'com.amazonaws', name: 'aws-lambda-java-log4j2', version: '1.1.0'

    // Utils
    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.10.0'
    implementation group: 'commons-io', name: 'commons-io', version: '2.6'

    // Test
    testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.1.0'
    testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: '2.0.4'
    testImplementation group: 'org.powermock', name: 'powermock-module-junit4', version: '2.0.4'
    testImplementation group: 'junit', name: 'junit', version: '4.12'
}

主要的是AWS开发工具包。它们将允许您使用特定的服务,例如SES,DynamoDB等。

3.我们写一个lambda


  • 我们将RequestHandler的模板请求和响应类更改为AwsProxyRequest和ContactUsProxyResponse。
    public class App implements RequestHandler<AwsProxyRequest, ContactUsProxyResponse>
    ...
    public ContactUsProxyResponse handleRequest(AwsProxyRequest request, Context context) 

  • AwsClientFactory AWS SDK-.
    /**
     * Just an util class for an eager initialization of sdk clients.
     */
    public class AwsClientFactory {
    
        private static final Logger LOG = LogManager.getLogger(AwsClientFactory.class);
    
        private final AmazonSimpleEmailService sesClient;
        private final DynamoDB dynamoDB;
    
        /**
         * AWS regions should be env variables if you want to generalize the solution.
         */
        AwsClientFactory() {
            LOG.debug("AWS clients factory initialization.");
            sesClient = AmazonSimpleEmailServiceClient.builder().withRegion(Regions.EU_WEST_1).build();
            AmazonDynamoDB dynamoDBClient = AmazonDynamoDBClientBuilder.standard().withRegion(Regions.EU_WEST_1).build();
            dynamoDB = new DynamoDB(dynamoDBClient);
        }
    
        DynamoDB getDynamoDB() {
            return dynamoDB;
        }
    
        AmazonSimpleEmailService getSesClient() {
            return sesClient;
        }
    
    }
    

  • ObjectMapper .
    
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final AwsClientFactory AWS_CLIENT_FACTORY = new AwsClientFactory();
    

  • .
    
     private SendEmailResult sendEmail(ContactUsRequest contactUsRequest) {
            String emailTemplate = getEmailTemplate();
            String email = fillTemplate(emailTemplate, contactUsRequest);
    
            SendEmailRequest sendEmailRequest =
                    new SendEmailRequest(
                            System.getenv("SENDER_EMAIL"),
                            new Destination(List.of(System.getenv("RECIPIENT_EMAIL"))),
                            new Message()
                                    .withSubject(
                                            new Content()
                                                    .withCharset(UTF_8.name())
                                                    .withData(contactUsRequest.getSubject()))
                                    .withBody(new Body()
                                            .withHtml(new Content()
                                                    .withCharset(UTF_8.name())
                                                    .withData(email))));
            LOG.info("Email template is ready");
            return AWS_CLIENT_FACTORY.getSesClient().sendEmail(sendEmailRequest);
    }
    
    private String fillTemplate(String emailTemplate, ContactUsRequest contactUsRequest) {
            return String.format(
                    emailTemplate,
                    contactUsRequest.getUsername(),
                    contactUsRequest.getEmail(),
                    contactUsRequest.getPhone(),
                    contactUsRequest.getQuestion());
    }
    
    private String getEmailTemplate() {
            try {
                return IOUtils.toString(
                        Objects.requireNonNull(this.getClass().getClassLoader()
                                                   .getResourceAsStream("email_template.html")),
                        UTF_8);
            } catch (IOException e) {
                throw new RuntimeException("Loading an email template failed.", e);
            }
    }
    
    private void addEmailDetailsToDb(ContactUsRequest contactUsRequest, SendEmailResult sendEmailResult) {
            AWS_CLIENT_FACTORY.getDynamoDB().getTable("ContactUsTable")
                              .putItem(new Item()
                                      .withPrimaryKey("Id", sendEmailResult.getMessageId())
                                      .withString("Subject", contactUsRequest.getSubject())
                                      .withString("Username", contactUsRequest.getUsername())
                                      .withString("Phone", contactUsRequest.getPhone())
                                      .withString("Email", contactUsRequest.getEmail())
                                      .withString("Question", contactUsRequest.getQuestion()));
    }
    

  • .
    
    private ContactUsProxyResponse buildResponse(int statusCode, String body) {
            ContactUsProxyResponse awsProxyResponse =
                    new ContactUsProxyResponse();
            awsProxyResponse.setStatusCode(statusCode);
            awsProxyResponse.setBody(getBodyAsString(body));
            awsProxyResponse.addHeader(CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
            awsProxyResponse.addHeader("Access-Control-Allow-Origin", "*");
            return awsProxyResponse;
    }
    
     private String getBodyAsString(String body) {
            try {
                return OBJECT_MAPPER.writeValueAsString(new ContactUsResponseBody(body));
            } catch (JsonProcessingException e) {
                throw new RuntimeException("Writing ContactUsResponseBody as string failed.", e);
            }
    }
    

  • . «// WARMING UP».
    
    if (Optional.ofNullable(request.getMultiValueHeaders()).map(headers -> headers.containsKey("X-WARM-UP")).orElse(FALSE)) {
                LOG.info("Lambda was warmed up");
                return buildResponse(201, "Lambda was warmed up. V1");
    }
    

  • handleRequest()
    
    @Override
    public ContactUsProxyResponse handleRequest(AwsProxyRequest request, Context context) {
            LOG.info("Request was received");
            LOG.debug(getAsPrettyString(request));
    
            if (Optional.ofNullable(request.getMultiValueHeaders()).map(headers -> headers.containsKey("X-WARM-UP")).orElse(FALSE)) {
                LOG.info("Lambda was warmed up");
                return buildResponse(201, "Lambda was warmed up. V1");
            }
    
            ContactUsRequest contactUsRequest = getContactUsRequest(request);
    
            SendEmailResult sendEmailResult = sendEmail(contactUsRequest);
            LOG.info("Email was sent");
    
            addEmailDetailsToDb(contactUsRequest, sendEmailResult);
            LOG.info("DB is updated");
    
            return buildResponse(200,
                    String.format("Message %s has been sent successfully.", sendEmailResult.getMessageId()));
    }
    



基本逻辑很简单,因此我认为不值得对其进行详细描述。有几点需要注意。

首先是日志记录。上下文lambda方法的参数之一包含许多辅助信息以及一个记录器。因此,您不能创建单独的记录器,而是使用提供的lambda上下文。为此,只需在使用前调用即可:
LambdaLogger logger = context.getLogger();


第二点是热身。由于为lambda创建可执行环境是很懒的,因此启动JVM,加载类路径并执行代码需要一些时间。第一次调用可能需要花费几秒钟的时间,如果您编写不同的同步API,则效果不佳。对于此类情况,您自己可以告诉AWS您需要保持几个lambda实例处于警报状态。但这需要有人调用lambda。如果我们使用基本版本的代码执行此操作,那么实际上,我们将发送一封信并将一些不正确的数据写入数据库。
为避免这种情况,我们可以添加某种请求处理,以区分实际请求和预热请求。例如,我们可以向请求添加特殊的标头。在我们的例子中,“ X-WARM-UP”头将与任何值一起使用-为了了解这是一个预热请求,我们只需要返回某种响应即可,而无需执行业务逻辑。

我要引起注意的最后一件事是使用静态变量。在我们的例子中,这不会产生有状态的lambda实例,而只是允许重用现有和初始化的对象。如果要初始化的对象在代码执行过程中发生静态更改,请尝试再次考虑这是否会干扰后续对lambda的调用的操作。另外,如果这些对象在初始化期间使用环境变量,则不应将其设置为静态。

4.我们编写测试


Lambda代码可以被大多数情况下使用的相同类型的测试所覆盖。但是,为了防止文章进一步发展,我将在下一篇有关AWS Lambda测试和本地开发的文章中介绍测试无服务器应用程序。尽管在存储库中已经可以使用单元测试

5.我们编写了SAM资源模板


在本地编写并测试了lambda之后,我们需要编写SAM模板以在AWS中部署所有这些资源。

关于SAM的一点
Serverless Application Model – serverless . , CloudFormation DSL, – Lambda, Gateway, DynamoDB . .
SAM, CloudFormation, Terraform – IaC-. SAM Serverless-, Terraform .
Terraform DINS.

实际上,让我们看一下需要在SAM-template中声明哪些资源

AWS :: Serverless ::函数
如果我们使用SAM初始化来创建项目,则主要功能已经为我们填充。但是在我的示例中,它们是这样的:
CodeUri: contact-us-function
Handler: com.gralll.sam.App::handleRequest
Runtime: java8
MemorySize: 256

在这里,我认为一切都足够清楚。
CodeUri是我们的目录
处理器拉姆达-的完整路径
运行方法-什么
MemorySize拉姆达写 -不言自明

说到内存:高达3GB的内存可用在lambda,这相当于2个CPU的资源。也就是说,您不能单独调整CPU,而只能增加/减少内存量。

需要下一个框来选择部署方法。
AutoPublishAlias: live
DeploymentPreference:
  Type: Canary10Percent10Minutes

AutoPublishAlias-允许您为每个新的部署版本添加别名。为了实现金丝雀部署,这是必需的。
Canary10Percent10Minutes-一种部署类型,它允许您同时容纳lambda的两个版本:旧版本和新版本,但仅将10%的流量重定向到新版本。如果十分钟之内没有问题,其余的流量也将被重定向到新版本。
您可以在SAM页面上阅读有关使用高级功能的更多信息

接下来是将在代码中使用的环境变量。之后,另一个lambda块:
Events:
  ContactUs:
    Type: Api
    Properties:
      Path: /contact
      Method: post

在其中,我们必须描述调用lambda的触发器。在我们的情况下,这些将是API网关请求
这是描述API的一种相当简化的形式,但是对于新创建的Gateway API而言,足以将所有POST /联系请求重定向到我们的lambda。

当然,我们需要描述安全性方面。开箱即用,创建的lambda将无法访问数据库或电子邮件服务。因此,我们需要明确规定允许的内容。有几种方法可以在AWS内部进行访问。我们将使用基于资源的策略:
Policies:
  - AWSLambdaExecute
  - Version: '2012-10-17'
    Statement:
      - Effect: Allow
        Action:
          - ses:SendEmail
          - ses:SendRawEmail
        Resource: 'arn:aws:ses:eu-west-1:548476639829:identity/aleksandrgruzdev11@gmail.com'
      - Effect: Allow
        Action:
          - dynamodb:List*
        Resource: '*'
      - Effect: Allow
        Action:
          - dynamodb:Get*
          - dynamodb:PutItem
          - dynamodb:DescribeTable
        Resource: 'arn:aws:dynamodb:*:*:table/ContactUsTable'

请注意,对于数据库表,我们指定了一个特定的名称,因此,我们将需要创建一个具有相同名称的表。
关于SES:您会看到我的电子邮件地址。就您而言,它必须是您自己确认的地址。如何执行此操作,请参见此处
之后,您可以通过单击创建的地址找到该资源的身份ARN,并在上面的示例中将其替换为电子邮件。
他们有点想出了lambda。现在让我们进入数据库。

AWS ::无服务器:: SimpleTable
对于我们的任务,我们将仅创建一个ContactUsTable表:
ContactUsTable:
  Type: AWS::Serverless::SimpleTable
  Properties:
    PrimaryKey:
      Name: Id
      Type: String
    TableName: ContactUsTable
    ProvisionedThroughput:
      ReadCapacityUnits: 2
      WriteCapacityUnits: 2

在必填字段中-仅是ID,还指示ReadCapacityUnits和WriteCapacityUnits。我们不会详细介绍选择什么值,因为这也是一个相当广泛的话题。您可以在这里阅读对于测试应用程序,较小的1-2数量级就足够了。

全局
参数例如,如果您声明了几个Function或API类型的资源,则可以在此块中取出General参数。
Globals:
  Function:
    Timeout: 15
  Api:
    Cors:
      AllowOrigin: "'*'"
      AllowHeaders: "'Content-Type,X-WARM-UP,X-Amz-Date,Authorization,X-Api-Key'"

我用它来设置函数的超时时间和一些Cors设置,以便稍后通过ContactUs表单从我的静态页面调用Gateway API。

输出
此块使您可以在全局AWS CloudFormation上下文中动态定义一些变量。
Outputs:
  ContactUsApi:
    Description: "API Gateway endpoint URL for Prod stage for ContactUs function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/contact/"
  ContactUsFunction:
    Description: "ContactUs Lambda Function ARN"
    Value: !GetAtt ContactUsFunction.Arn
  ContactUsFunctionIamRole:
    Description: "Implicit IAM Role created for ContactUs function"
    Value: !GetAtt ContactUsFunctionRole.Arn

例如,我们声明了一个ContactUsApi变量,该变量将被设置为一个值,作为我们创建的API端点的公共地址。
由于我们使用的是$ {ServerlessRestApi},AWS将在字符串中插入新网关API的唯一标识符。结果,任何有权访问CloudFormation的应用程序都将能够获得该地址-因此,您无法对服务的URL进行硬编码。好吧,另一个好处是,查看输出列表非常方便-有关堆栈的一些元信息。
此处可以找到功能的完整列表以及可以使用的参数

参数
除了上述所有以外,您还可以添加一个Parameters这些选项将有助于使模板更具通用性。在模板的任何其他块中,您都可以引用这些参数,从而不必对某些值进行硬编码。例如,在我的模板中,它可以是电子邮件,SES ARN,内存量等。

这就是整个模板。没有人禁止在附近添加更多资源,例如S3存储桶,任何其他CloudFormation资源或一般的自定义资源

6.我们继续进行部署


为了组装我们的项目,我们将不使用Gradle,而是使用SAM。
部件
F:\aws\projects\contact-us-sam-app>sam build
Building resource 'ContactUsFunction'
Running JavaGradleWorkflow:GradleBuild
Running JavaGradleWorkflow:CopyArtifacts

Build Succeeded

Built Artifacts  : .aws-sam\build
Built Template   : .aws-sam\build\template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy –guided


从项目的根目录 运行sam build命令将自动将所有必需的文件收集到.aws-sam文件夹中:类,依赖项,SAM模板。
接下来,您需要创建一个S3存储桶,SAM随后将在其中上传所有收集的工件。
可以通过基于浏览器的AWS控制台或使用以下命令来完成此操作
aws s3 mb s3://bucket-name

注意:所有存储桶都是在全局上下文中创建的,并且在所有帐户之间都经过了翻版。因此,如果有人已经在您的帐户中创建了存储桶,则无法创建该存储桶。

存储桶准备就绪后,执行以下命令:
sam package --output-template-file packaged.yaml --s3-bucket <YOUR_BACKET>

包装结果
F:\aws\projects\contact-us-sam-app>sam package --output-template-file packaged.yaml --s3-bucket contact-us-sam-app
Uploading to ea0c122c06a50d9676fbf9000a80a3bf  9212768 / 9212768.0  (100.00%)

Successfully packaged artifacts and wrote output template to file packaged.yaml.
Execute the following command to deploy the packaged template
sam deploy --template-file F:\aws\projects\contact-us-sam-app\packaged.yaml --stack-name <YOUR STACK NAME>


我指向了我的contact-us-sam-app存储桶,SAM将所有资源上传到了指定位置。接下来,SAM已经告诉您使用资源创建堆栈的命令,从而嵌入您的决定。我们执行了一下命令,并完成了一点:
sam deploy --template-file packaged.yaml --region eu-west-1 --capabilities CAPABILITY_IAM --stack-name contact-us-sam-app

如您所见,我添加了--capabilities CAPABILITY_IAM这将使CloudFormation能够创建IAM资源。否则,创建时会收到InsufficientCapabilities错误。
以下是此命令的工作日志(图片可单击)。每个资源的状态和输出值等详细信息仅在SAM CLI的最新版本中可用。


7.检查CloudFormation的状态


我们可以等待控制台中的部署完成,直到出现一条消息,指出已部署了堆栈(deploy命令记录在上一段中):
Successfully created/updated stack - contact-us-sam-app in eu-west-1

但是在我们的情况下,由于金丝雀部署模式,您只会在十分钟后看到此消息。因此,打开浏览器控制台并查看其中的堆栈会更加容易。

一段时间后,状态将更改为CREATE_COMPLETE,这表示成功完成。
事件选项卡中,您可以查看所有资源的状态。如果您的堆栈出现故障,则可以在这里找到详细的错误消息。
例如,这:UPDATE_FAILED-如果您在模板中错误地配置了网关API。


在资源选项卡中,您可以找到所有创建的资源。不要惊讶于其数量。尽管我们仅在SAM模板中声明了一个函数和一个数据库表,但是CloudFormation为我们创建了许多其他资源。如果查看它们的类型,就可以了解它们所属的服务。
对于Api Gateway,它是隐式创建的:
  • ServerlessRestApi
  • ServerlessRestApi部署
  • ServerlessRestApiProdStage

同样为lambda创建了几个其他对象。
现在打开“ 输出”并找到我们API的URL。复制它,它将很快派上用场。

8. HTML ContactUs表单


如您所记得,我决定制作一个ContactUs表单,现在我们需要以某种方式使它不仅在本地计算机上可用。

配置
例如,对于表单本身,我决定采用最简单的HTML表单,并通过ajax添加了Gateway API调用。

除了表单本身之外,我还添加了一些用于调试和简化填充的按钮:
设置默认值 -可以预期地替换HTML内部指定的默认参数。
$("#url-input").val("https://4ykskscuq0.execute-api.eu-west-1.amazonaws.com/Prod/contact");
$("#name-input").val("Mike");
$("#email-input").val("Mike@somemail.com");
$("#phone-input").val("+79999999999");
$("#description-input").val("How much does it cost?");

如果要使用此功能,请将url-input更改为从Outputs复制的Gateway API的路径

代管
  • 创建一个新的S3存储桶。
  • 通过选择将文件公开的选项,我们将HTML文件加载到存储桶中。
  • 我们进入上载文件的存储桶。
  • 转到“属性”,然后打开“静态”网站托管,然后查看新的公共可用端点。


9.健康检查


标准请求
现在,您可以单击表单的链接,单击“设置默认值”和“发送”。
这样,您将发出将通过网关API到达AWS Lambda的请求。

如果过一会儿您正确配置了所有内容,您将收到以下消息:
成功:消息0102016f28b06243-ae897a1e-b805-406b-9987-019f21547682-000000已成功发送。

这意味着邮件已成功传递。检查您在SAM模板中指定的邮箱。如果尚未更改模板,则字母将采用以下格式:

您还可以打开DynamoDB并确保已出现新条目。

冷启动的功能
我想您已经注意到有关成功发送的消息是在相当长的一段时间之后发出的。这是由于以下事实:AWS Lambda服务已收到处理请求,并开始引发Java应用程序的实例,这包括使用操作系统和JRE提升容器,加载类路径,初始化所有变量,然后才是handleRequest( )。这称为冷启动。

尝试填写并再次提交表单。这次答案几乎立刻就来了,对吗?如果在第一个请求和第二个请求之间经过了20-30分钟,则结果可能会有所不同。
这是什么原因呢?事实是,AWS Lambda缓存已使用带有lambda的容器进行重复使用。这减少了用于启动该方法的整个上下文的初始化时间。
lambdas的缓存时间长短并没有明显的相关性,但是有人通过实验发现这直接取决于所选择的RAM值。也就是说,具有128MB内存的lambda将比3GB可用的时间更长。也许还有其他参数,例如,执行lambda的区域的平均负载,但这是不准确的。
因此,如果您使用同步请求,请尝试一下并计划缓存时间。

热身
作为附加选项,你可以使用拉姆达加热。在代码中,我添加了X-WAMP-UP标头检查。如果有一个,则lambda仅返回响应而不执行任何业务逻辑,但容器将为后续调用做好准备。
例如,您可以使用CloudWatch自己通过皇冠计时器调用lambda。如果您不希望客户加入冷启动,这将有所帮助。
在HTML表单中,您可以找到WarmUp模式按钮,该按钮将此特殊标头添加到请求中。您可以验证既不会发送信件也不会写入数据库,但是来自lambda的响应到达了,随后对真实请求的调用将不会花费很多时间。

总结


在本文中,我们经历了从应用程序设计到所谓的生产发布的所有主要阶段
我希望那些已经听说过Serverless和AWS Lambda但又没有实际经验的人能够使用本指南,并感到这样做不仅在某些软件解决方案的部署速度上具有显着的优势。

好处
对于我自己,我已经确定了最宝贵的好处:
  • Free Tier , , .
  • 1-2 . /.
  • , , .
  • Serverless . , — . SAM AWS. AWS , , .
  • Lambda , .
  • Serverless , , . ELK/Promethes/Grafana .
  • , API. API Key, IAM , .
  • 好吧,最基本的东西可能是无服务器。无需考虑需要为哪个EC2实例付费,如何配置对其的访问权限,在应用程序崩溃时如何配置警报,配置自动缩放等。


缺点
不幸的是,这并不意味着现在您可以采用此解决方案并将其用作企业系统的一部分,而不是您在cuber中最喜欢的微服务。很多时候,您需要深入研究以确定在特定情况下更合适的解决方案。
我也有评论,选择Lambda时应注意:
  • 冷启动。在使用同步查询以及Java或C#等语言时特别重要。通过预热可以适当地解决此问题,但最好事先考虑一下这种解决方案,然后比较成本和可能的收益
  • . 3GB , 2 , , - Fargate, , .
  • API, , .
  • / 1-2 Serverless, .
  • 如果您已经拥有在Coober中开发微服务和CI / CD的基础结构,那么(特别是对于管理人员)争辩是否需要支持另一个CI / CD流程将再次成为问题。
  • 好吧,这里没有测试。而且,测试lambda的吞吐量非常困难,因为它仍然与通常的性能测试不同,并且必须考虑许多因素。


用法
通常,AWS Lambda和其他无服务器服务的使用非常适合异步和事件驱动的开发原则。通过使用异步处理,可以在性能和成本上获得最佳结果。这是Serveress解决方案将在其中发挥重要作用的解决方案列表:


聚苯乙烯


由于这篇文章的内容相当广泛,并且我不想再夸大其词,因此我很可能会准备一个续集,其中将展示如何使用AWS控制台,SAM框架甚至IntelljIDEA的所有功能更有效地进行开发。好吧,由于我省略了测试部分,因此我将尝试更详细地描述开发的这一方面。另外,如果您有任何要添加到下一篇文章或问题的愿望,请随时在评论或私人消息中写。

补充: AWS Lambda的实际应用。第2

部分文章中的一些重要和有用的链接:

Source: https://habr.com/ru/post/undefined/


All Articles