AWS Lambda em ação. Parte 2: conhecendo as ferramentas de desenvolvimento e teste



Este guia é o resultado da experiência pessoal no desenvolvimento e teste de aplicativos sem servidor, além de manobras entre muletas e bicicletas ao tentar testá-los. Quando comecei a desenvolver aplicativos sem servidor, tive que descobrir tudo com minhas mãos, não havia guias claros ou ferramentas convenientes para desenvolvimento e teste local.

Agora a situação está mudando: as ferramentas começaram a aparecer, mas não é fácil encontrar manuais sobre o uso deles. Neste artigo, mostrarei com um exemplo simples como trabalhar com a nova funcionalidade. Você pode dominá-lo facilmente e contornar os problemas e bugs que tive a “sorte” de encontrar.

Você aprenderá como desenvolver usando o console do navegador AWS, SAM-CLI e IntelljIDEA. Também falarei sobre testes: integração, E2E e testes de unidade. E, finalmente, discutiremos quanto custará uma solução (spoiler: você pode economizar muito).

Este artigo será útil para aqueles que começam a desenvolver aplicativos sem servidor e ainda não estão familiarizados com suas ferramentas e abordagens.


O que será discutido


Olá a todos! Meu nome é Alexander Gruzdev e tenho o prazer de apresentar a segunda parte do artigo sobre o uso e desenvolvimento de soluções sem servidor baseadas no AWS Lambda. Na sequência, falaremos sobre as maneiras de desenvolver e testar a solução desde a primeira parte ( AWS Lambda in Action em Java 11. Visitamos o Serverless em "Produção" ).

Desde a publicação do último artigo, o aplicativo de teste foi ligeiramente redesenhado, mas o significado principal permanece o mesmo. Este é um formulário HTML trivial "Fale conosco", que envia dados do formulário para a API do Gateway por meio de solicitações HTTP e, por sua vez, proxies a solicitação de processamento para o AWS Lambda. Durante o processamento, o lambda grava dados na tabela do DynamoDB e envia uma carta pelo AWS SES. O diagrama do componente é apresentado abaixo.

Diagrama de componentes


Conteúdo


  1. Desenvolvimento usando o AWS Browser Console
  2. Desenvolvimento usando SAM-CLI
  3. Desenvolvimento usando IntelljIDEA
  4. Tipos de teste
  5. Custos sem servidor
  6. Links Úteis



1. Desenvolvimento usando o console do navegador AWS



Console da AWS


O console da AWS fornece acesso a qualquer serviço da AWS, seja seu servidor virtual EC2, IAM ou configurações de política de acesso / função do AWS Lambda com todos os parâmetros que podem ser alterados em tempo real sem reimplementação pelos sistemas de Entrega Contínua / Implantação Contínua.

Isso acelera bastante o processo de desenvolvimento e alguns tipos de teste. Também ajuda a controlar visualmente os parâmetros de inicialização e operação de seus aplicativos.

Console do Gateway de API


O console permite monitorar o estado atual do aplicativo, gerenciar seus ambientes e fornecer novas versões. Vou mostrar como isso pode ser usado para verificar a saúde do meio ambiente.

Vamos ver como esse console "mágico" se parece na captura de tela da guia Recursos do aplicativo Fale conosco: A captura de tela mostra como a solicitação do cliente através da API do Gateway entra no lambda e como a resposta será retornada ao cliente quando o recurso / contact for chamado pelo método POST.

Recursos de gateway



  • Solicitação de método - a primeira etapa no processamento da solicitação. No nosso caso, ele não contém nenhuma configuração, mas pode conter, por exemplo, configurações para autorização.
  • Solicitação de integração significa que a solicitação HTTP será transmitida ao lambda em um formato especial: contém todas as informações enviadas pelo cliente e detalhes sobre o contexto chamado.
  • A resposta de integração diz que o cliente HTTP receberá a resposta diretamente do lambda. Portanto, você mesmo é responsável pela formação do corpo de resposta em lambda.


Desativar integração de proxy
API Gateway , Use Lambda Proxy integration Integration Request. . , , .


Amostra de solicitação de integração
SAM init events, , Integration request. , AWS , . AWS Lambda.


Pressione o botão TESTE para verificar se o aplicativo está funcionando. Isso lhe dará acesso às chamadas da API do Gateway HTTP.

Na captura de tela a seguir, enviei uma solicitação para aquecer o lambda: Além de responder à solicitação, a API do Gateway exibe logs detalhados. Da mesma forma, você pode fazer uma solicitação real substituindo o corpo da solicitação JSON no formato:
Solicitação de teste do gateway


{
    "subject": "Email subject",
    "question": "Your question…",
    "username": "Your name…",
    "phone": "Your phone…",
    "email": "Your email…"
} 


Portanto, você pode testar manualmente sua API sem formulários de interface do usuário e aplicativos de front-end, separando assim o desenvolvimento de front-end e back-end. Como alternativa ao console interno, você pode usar o Postman ou qualquer outro cliente HTTP.

Carteiro como alternativa
HTTP- Postman. . , API Gateway AWS , API. , , AWS .


Lambda Console


Para teste e desenvolvimento, além das solicitações da API do Gateway, você pode usar o console do navegador AWS Lambda.

Sua tela é dividida em vários blocos:
  • O Designer mostra gatilhos e camadas existentes. No nosso caso, o gatilho é o API Gateway. Se clicarmos nele, todas as informações necessárias serão abertas: URL, estágio, caminho do recurso.
  • Aliases (disponíveis ao selecionar um alias lambda específico) permite configurar a distribuição do tráfego por versão do lambda. Mais sobre isso no próximo parágrafo.
  • Function code — . zip- , . , Function code . Python/NodeJs.
  • Environment variables. , . , , . , . , , .
  • Tags — , , , , .
  • Execution role — ( ) , .
  • Basic settings — .
  • Network: - , VPC. — .
  • AWS X-Ray . AWS-.
  • Reserve concurrency , . .
    Provisioned concurrency
  • Provisioned concurrency — . , . , — . Free Tier .
  • A chamada assíncrona permite configurar o tratamento especial de solicitações assíncronas. Indicamos quantas chamadas repetidas devem ser feitas em caso de erro e por quanto tempo a solicitação pode travar na fila. Opcionalmente, você pode especificar uma fila para processar solicitações que falham.

  • Proxies do banco de dados é outro novo recurso que permite configurar o acesso ao banco de dados por meio de proxies na forma de API Gateway + Lambda.


A seguir, vamos ver como desenvolver a partir deste console. Além de alterar os parâmetros, a funcionalidade Configurar Eventos de Teste pode ser usada para criar uma solicitação de teste. Uma lista bastante extensa está disponível, mas aproveitaremos o que já anexei no projeto:
Pedido de ContactUs
{
  "body": "{\"subject\": \"Question\",\"question\": \"How much does it cost\",\"username\": \"Alex\",\"phone\": \"+79999999999\",\"email\": \"alex@gmail.com\"}",
  "resource": "/{proxy+}",
  "path": "/path/to/resource",
  "httpMethod": "POST",
  "isBase64Encoded": false,
  "queryStringParameters": {
    "foo": "bar"
  },
  "pathParameters": {
    "proxy": "/path/to/resource"
  },
  "stageVariables": {
    "baz": "qux"
  },
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, sdch",
    "Accept-Language": "en-US,en;q=0.8",
    "Cache-Control": "max-age=0",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-Mobile-Viewer": "false",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Tablet-Viewer": "false",
    "CloudFront-Viewer-Country": "US",
    "Host": "1234567890.execute-api.us-east-1.amazonaws.com",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Custom User Agent String",
    "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
    "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
    "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "requestContext": {
    "accountId": "123456789012",
    "resourceId": "123456",
    "stage": "prod",
    "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "requestTime": "09/Apr/2015:12:34:56 +0000",
    "requestTimeEpoch": 1428582896000,
    "identity": {
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "accessKey": null,
      "sourceIp": "127.0.0.1",
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "Custom User Agent String",
      "user": null
    },
    "path": "/prod/path/to/resource",
    "resourcePath": "/{proxy+}",
    "httpMethod": "POST",
    "apiId": "1234567890",
    "protocol": "HTTP/1.1"
  }
}


Após incluir a solicitação, clique em Testar e veja o resultado do lambda: Lambda retorna JSON, que contém o código de status e o corpo da resposta. O valor do corpo irá diretamente para o cliente que chamou a API do Gateway. Além disso, a captura de tela possui as características da chamada: o tempo de execução, o tempo de execução pelo qual pagaremos, a quantidade máxima de memória que o lambda consumiu. Contando com essas métricas, podemos configurar a quantidade ideal de memória para que o lambda corresponda aos seus requisitos de preço / taxa de resposta.
Recursos de gateway



Usando o console da AWS para testar a implantação de canárias


Agora, vamos tocar em um ponto importante relacionado à implantação de uma nova versão do lambda. Especifiquei esses parâmetros quando escrevi o modelo SAM:
AutoPublishAlias: live
DeploymentPreference:
  Type: Canary10Percent10Minutes

O primeiro parâmetro adiciona automaticamente um alias ativo a cada nova versão do lambda .
Um pouco sobre aliases
, . , , API Gateway. 1, 2, 3 . LATEST, . 2 3 API Gateway (Stage AWS), Gateway-Lambda. API Gateway (ARN ), LATEST.

— ( ), , , ARN . , Stage API Gateway . , API Gateway: dev, qa prod .

, , . , . , live, . live- , , , , CloudWatch.


Para testar a implantação do canary, você pode usar o console da API do Gateway e o console do Lambda.

Primeiro, você precisa alterar o código lambda para que ele retorne uma resposta diferente. Por exemplo, no processo de aquecimento, especifique a Versão 2 em vez da Versão 1. Na resposta, este será o nosso marcador.

Agora, usando a funcionalidade da API do Teste de Gateway, podemos garantir que, dentro de dez minutos, ao enviar solicitações de AQUECIMENTO, apenas 10% das respostas terão a Versão 2 no corpo da resposta. Após esse período, a versão 2. retornará 100% das solicitações.Além

do console da API do Gateway, podemos usar o console Lambda, onde, após selecionar o alias necessário, vemos uma política de distribuição de tráfego que pode ser controlada.
Alias ​​do Lambda



2. Desenvolvimento local usando SAM-CLI


Há algum tempo, uma das principais desvantagens do uso do lambda era considerada inconveniente no desenvolvimento e teste na máquina local. Agora a situação mudou bastante: a estrutura SAM apareceu, o que permite não apenas coletar e implantar soluções, mas também simplifica o desenvolvimento local.

Por exemplo, você pode chamar seu lambda diretamente do console:
sam local invoke -e ./events/warm_up_request.json ContactUsFunction

Este comando é ativado a partir do diretório com o modelo SAM e transfere o conteúdo do arquivo JSON para a entrada lambda ContactUsFunction (este é o nome lógico no modelo).

Ou seja, usando este comando, o SAM gera a imagem lambci / lambda: java11 na janela de encaixe e executa seu código nela. Para acessar serviços remotos, como o SES, sua configuração da AWS com uma chave de segredo / acesso é usada e, portanto, deve estar atualizada. Outro ponto importante: se você não adicionar um cabeçalho para o modo de aquecimento, os serviços reais do AWS SES e DynamoDB serão chamados.

Registro de chamadas aqui
F:\aws\projects\contact-us-sam-app>sam local invoke -e ./events/warm_up_request.json ContactUsFunction
Invoking com.gralll.sam.App::handleRequest (java11)
Fetching lambci/lambda:java11 Docker container image......
Mounting F:\aws\projects\contact-us-sam-app\.aws-sam\build\ContactUsFunction as /var/task:ro,delegated inside runtime container
?[32mSTART RequestId: a86d65fa-1f19-15c5-93b8-d87631c80ee2 Version: $LATEST?[0m
2020-01-06 19:09:17 a86d65fa-1f19-15c5-93b8-d87631c80ee2 INFO  App - Request was received
2020-01-06 19:09:17 a86d65fa-1f19-15c5-93b8-d87631c80ee2 DEBUG App - {
  "body" : "{\"subject\": \"Question\",\"question\": \"How much does it cost\",\"username\": \"Alex\",\"phone\": \"+79999999999\",\"email\": \"alex@gmail.com\"}",
  "resource" : "/{proxy+}",
  "requestContext" : {
    "resourceId" : "123456",
    "apiId" : "1234567890",
    "resourcePath" : "/{proxy+}",
    "httpMethod" : "POST",
    "requestId" : "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "extendedRequestId" : null,
    "accountId" : "123456789012",
    "identity" : {
      "apiKey" : null,
      "apiKeyId" : null,
      "userArn" : null,
      "cognitoAuthenticationType" : null,
      "caller" : null,
      "userAgent" : "Custom User Agent String",
      "user" : null,
      "cognitoIdentityPoolId" : null,
      "cognitoIdentityId" : null,
      "cognitoAuthenticationProvider" : null,
      "sourceIp" : "127.0.0.1",
      "accountId" : null,
      "accessKey" : null
    },
    "authorizer" : null,
    "stage" : "prod",
    "path" : "/prod/path/to/resource",
    "protocol" : "HTTP/1.1",
    "requestTime" : "09/Apr/2015:12:34:56 +0000",
    "requestTimeEpoch" : 1428582896000,
    "elb" : null
  },
  "multiValueQueryStringParameters" : { },
  "multiValueHeaders" : {
    "X-WARM-UP" : [ "13" ]
  },
  "pathParameters" : {
    "proxy" : "/path/to/resource"
  },
  "httpMethod" : "POST",
  "stageVariables" : {
    "baz" : "qux"
  },
  "path" : "/path/to/resource",
  "isBase64Encoded" : false,
  "requestSource" : "API_GATEWAY"
}
2020-01-06 19:09:17 a86d65fa-1f19-15c5-93b8-d87631c80ee2 INFO  App - Lambda was warmed up
?[32mEND RequestId: a86d65fa-1f19-15c5-93b8-d87631c80ee2?[0m
?[32mREPORT RequestId: a86d65fa-1f19-15c5-93b8-d87631c80ee2     Init Duration: 1984.55 ms       Duration: 42.22 ms      Billed Duration: 100 ms Memory Size: 256 MB     Max Memory Used: 102 MB ?[0m
{"statusCode":201,"multiValueHeaders":{"Access-Control-Allow-Origin":["*"]},"body":{"response":"Lambda was warmed up. V1"},"isBase64Encoded":false}


Além do log lambda, as informações de serviço no status da chamada e uma resposta no formato JSON são gravadas no console.

A API do Gateway, como a lambda, pode ser executada localmente.
Nós executamos o comando:
sam local start-api

Log do console na inicialização
F:\aws\projects\contact-us-sam-app>sam local start-api
Mounting ContactUsFunction at http://127.0.0.1:3000/contact [POST, OPTIONS]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2020-01-06 22:20:30  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)


Depois de iniciar o servidor, podemos executar solicitações. Vou usar o Postman e enviar uma solicitação para aquecer o lambda.

No console, vemos
Invoking com.gralll.sam.App::handleRequest (java11)
Fetching lambci/lambda:java11 Docker container image......
Mounting F:\aws\projects\contact-us-sam-app\.aws-sam\build\ContactUsFunction as /var/task:ro,delegated inside runtime container
?[32mSTART RequestId: 27868750-3637-1f80-3d80-53a724da16ef Version: $LATEST?[0m
2020-01-06 19:28:09 27868750-3637-1f80-3d80-53a724da16ef INFO  App - Request was received
2020-01-06 19:28:09 27868750-3637-1f80-3d80-53a724da16ef DEBUG App - {
  "body" : "{\r\n  \"subject\": \"Question\",\r\n  \"question\": \"How much does it cost\",\r\n  \"username\": \"Alex\",\r\n  \"phone\": \"+79999999999\",\r\n  \"email\": \"alex@gmail.com\"\r\n}",
  "resource" : "/contact",
  "requestContext" : {
    "resourceId" : "123456",
    "apiId" : "1234567890",
    "resourcePath" : "/contact",
    "httpMethod" : "POST",
    "requestId" : "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "extendedRequestId" : null,
    "accountId" : "123456789012",
    "identity" : {
      "apiKey" : null,
      "apiKeyId" : null,
      "userArn" : null,
      "cognitoAuthenticationType" : null,
      "caller" : null,
      "userAgent" : "Custom User Agent String",
      "user" : null,
      "cognitoIdentityPoolId" : null,
      "cognitoIdentityId" : null,
      "cognitoAuthenticationProvider" : null,
      "sourceIp" : "127.0.0.1",
      "accountId" : null,
      "accessKey" : null
    },
    "authorizer" : null,
    "stage" : "Prod",
    "path" : "/contact",
    "protocol" : null,
    "requestTime" : null,
    "requestTimeEpoch" : 0,
    "elb" : null
  },
  "multiValueQueryStringParameters" : null,
  "multiValueHeaders" : {
    "Accept" : [ "application/json" ],
    "Accept-Encoding" : [ "gzip, deflate, br" ],
    "Accept-Language" : [ "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7" ],
    "Cache-Control" : [ "no-cache" ],
    "Connection" : [ "keep-alive" ],
    "Content-Length" : [ "150" ],
    "Content-Type" : [ "text/plain;charset=UTF-8" ],
    "Host" : [ "127.0.0.1:3000" ],
    "Origin" : [ "chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop" ],
    "Postman-Token" : [ "5cadaccd-6fae-5bc2-b4ef-63900a1725ff" ],
    "Sec-Fetch-Mode" : [ "cors" ],
    "Sec-Fetch-Site" : [ "cross-site" ],
    "User-Agent" : [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" ],
    "X-Forwarded-Port" : [ "3000" ],
    "X-Forwarded-Proto" : [ "http" ],
    "X-Warm-Up" : [ "123" ]
  },
  "pathParameters" : null,
  "httpMethod" : "POST",
  "stageVariables" : null,
  "path" : "/contact",
  "isBase64Encoded" : false,
  "requestSource" : "API_GATEWAY"
}
2020-01-06 19:28:09 27868750-3637-1f80-3d80-53a724da16ef INFO  App - Lambda was warmed up
?[32mEND RequestId: 27868750-3637-1f80-3d80-53a724da16ef?[0m
?[32mREPORT RequestId: 27868750-3637-1f80-3d80-53a724da16ef     Init Duration: 1951.87 ms       Duration: 42.91 ms      Billed Duration: 100 ms Memory Size: 256 MB     Max Memory Used: 102 MB ?[0m
2020-01-06 22:28:11 127.0.0.1 - - [06/Jan/2020 22:28:11] "POST /contact HTTP/1.1" 201 -


Logs, é claro, não são formatados da melhor maneira. Espero que isso seja corrigido no futuro.

Importante: ao alterar o código lambda, não é necessário reiniciar a API. É suficiente reconstruir o lambda com o comando sam build .
Um pouco sobre bugs
Java- aws-serverless-java-container-core, : 502 . :
Invalid API Gateway Response Keys: {'base64Encoded'} in {'statusCode': 200, 'headers': {'Content-Type': 'text/plain'}, 'body': 'Hello World', 'base64Encoded': False} 

- , API Gateway AWS JsonProperty. 'isBase64Encoded' 'base64Encoded', , , . , Java- ContactUsProxyResponse AwsProxyRequest.



3. Chamadas locais da IDEA


Tudo descrito no parágrafo acima é a funcionalidade da ferramenta SAM. É controlado a partir da linha de comando e pode ser integrado a qualquer interface. Foi exatamente isso que foi feito no plug-in do IntelljIDEA AWS Toolkit.

Este plug-in adiciona configurações adicionais para iniciar um lambda diretamente do IDE. É assim que a janela de configuração se parece. Você deve selecionar um modelo com uma descrição do lambda ou definir as configurações manualmente. No primeiro caso, a quantidade de memória e idioma para compilação será obtida automaticamente do modelo. No segundo caso, você pode configurar a quantidade de RAM e fazer um cálculo aproximado do limite. Outro plug-in permite executar a configuração diretamente da janela do editor de código Java ou do yaml-template. Imagens abaixo:











Isso, de fato, é tudo. Ao executar a configuração customizada, você simplesmente automatiza chamadas lambda frequentes no console. Concorda, bastante confortável?


4. Teste


Aqui falamos sobre os tipos de testes para lambdas. Como usei Java no exemplo, os testes também incluirão ferramentas bastante padrão para testes de unidade e integração: Junit 4, Mockito e PowerMockito.

Testes unitários


Para cobrir o código lambda com testes de unidade, você não precisa ter conhecimentos específicos. É suficiente aplicar práticas Java padrão: bloqueie todas as dependências da classe e tente testar todos os casos possíveis de processamento de solicitações.

Não adicionei todos os cenários de teste e me limitei a dois cenários positivos e dois negativos.

O primeiro teste positivo verifica se, se houver um cabeçalho X-WARM-UP, não haverá solicitações no DbService e no EmailService. O segundo caso verifica se esses serviços serão chamados se a solicitação for real. Estes são os testes mais simples, e eu omiti parte dos cheques neles.

Cenários negativos são uma verificação de resposta no caso de um erro de processamento do cliente ou servidor.

Testes de integração


Quanto aos testes de integração, decidi verificar o DbService e seu trabalho com a tabela DynamoDB.

Nesse caso, usei a ferramenta Localstack . Esta solução fornece mokas gratuitos para serviços da AWS que são executados como contêineres de docker.

Para instalar esta solução, basta executar os comandos:
pip install localstack
localstack start

Ou você pode usar o arquivo docker-compose .
docker-compor para elevar apenas o DynamoDB
version: '2.1'
services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
    image: localstack/localstack
    ports:
      - "4567-4599:4567-4599"
      - "${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}"
    environment:
      - SERVICES=dynamodb
      - DEBUG=${DEBUG- }
      - DATA_DIR=${DATA_DIR- }
      - PORT_WEB_UI=${PORT_WEB_UI- }
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
      - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
      - DOCKER_HOST=unix:///var/run/docker.sock
    volumes:
      - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"


O comando start gera todos os serviços da AWS disponíveis por padrão. Para aumentar o DynamoDB especificamente, basta definir a variável de ambiente:
set SERVICES=dynamodb


Existe um LocalstackTestRunner for Junit especial que permite iniciar os serviços necessários usando a configuração @LocalstackDockerProperties .
Como resultado, escrever um teste para o DbService se parece com o seguinte:
  • Adicione sobre a classe de teste.
    @RunWith(LocalstackTestRunner.class)
    @LocalstackDockerProperties(services = { "dynamodb" })
    

  • Crie uma tabela
    Java
    @Before
    public void setUp() {
        AmazonDynamoDB clientDynamoDB = TestUtils.getClientDynamoDB();
        dynamoDB = new DynamoDB(clientDynamoDB);
        dbService = new DbService(dynamoDB);
    
        dynamoDB.createTable(
                new CreateTableRequest()
                        .withTableName("ContactUsTable")
                        .withKeySchema(new KeySchemaElement("Id", KeyType.HASH))
                        .withAttributeDefinitions(new AttributeDefinition("Id", ScalarAttributeType.S))
                        .withProvisionedThroughput(new ProvisionedThroughput(10L, 10L)));
    }
                    


  • Nós descrevemos o cenário de teste
    Java
    // given
    ContactUsRequest contactUsRequest = new ContactUsRequest("subject", "name", "+79991234545", "123@mail.ru", "Qeustion");
    
    // when
    dbService.putContactUsRequest("123", contactUsRequest);
    
    // then
    Item item = dynamoDB.getTable("ContactUsTable").getItem(new PrimaryKey("Id", "123"));
    assertEquals(contactUsRequest.getSubject(), item.get("Subject"));
    assertEquals(contactUsRequest.getUsername(), item.get("Username"));
    assertEquals(contactUsRequest.getPhone(), item.get("Phone"));
    assertEquals(contactUsRequest.getEmail(), item.get("Email"));
    assertEquals(contactUsRequest.getQuestion(), item.get("Question"));
                  


  • Não se esqueça de excluir a tabela após o teste.
    Java
    @After
    public void tearDown() {
        dynamoDB.getTable("ContactUsTable").delete();
    }
                   



Como resultado, usando o Localstack, obtemos uma gama bastante ampla de simulações da AWS que permitem emular o trabalho desses serviços.

Testes E2E


Para reproduzir os cenários E2E, é melhor aumentar os serviços de teste da AWS especificamente para executar testes, se, é claro, houver essa oportunidade. Isso pode ser feito usando o modelo CloudFormation opcional, no qual os recursos serão cópias do modelo principal.

O teste pode ser feito chamando o lambda por meio da AWS-CLI e processando o resultado com qualquer ferramenta disponível. Você ainda pode fazer uma chamada em testes de unidade, mas sem criar mobs.

Testes de performance


Como o teste de desempenho merece um artigo separado ou mesmo um livro, não tentarei colocar todas as informações aqui. Em resumo, ao falar sobre teste de estresse, lembre-se de que quase todos os serviços da AWS não são gratuitos e você deve estar preparado para estimar os possíveis custos com antecedência.

Se você deseja avaliar os recursos necessários para um lambda, use o console da interface do usuário e faça algumas chamadas de teste. Em seguida, usando a lógica e a matemática comuns, avalie quanto o desempenho da sua lambda aumenta dependendo da quantidade de recursos disponíveis.

Não se esqueça do CloudWatch. Com sua ajuda, você pode criar painéis bastante indicativos e, com base neles, determinar quais parâmetros de lambda e gastos você pode pagar.


5. O custo de soluções sem servidor


A avaliação de uma solução é sempre específica, porque você precisa considerar não apenas o custo de uma chamada, mas também o tráfego de entrada / saída, o custo de armazenamento em cache, registro e monitoramento. Se você estiver pensando em usar o lambda, tente prever a carga que seu serviço manipulará.

Se forem chamadas únicas ou um processo ETL que é executado a cada poucas horas / dias, é mais provável que o lambda pague. Se você prever uma carga de várias consultas por segundo, faça uma estimativa precisa.

Abaixo, forneço estimativas aproximadas do custo do lambda e do servidor EC2 mais barato, para que você entenda quais valores estão em questão: Os seguintes parâmetros para o lambda foram usados ​​para calcular:




  • Tempo de espera: 150 milissegundos
  • Preço: $ 0,00001667 para 1GB-s
  • Memória: 256 MB

Para EC2 t2.nano:

  • Preço: $ 4.75 por mês
  • Memória: 512 MB
  • vCPU: 1
  • Carga máxima: 5% de CPU por mês

Peguei aqui os dados de carregamento do EC2, de acordo com esses cálculos, o servidor especificado pode suportar uma carga constante de 0,3 solicitações por segundo. À medida que a carga aumenta, o tempo de resposta aumenta, o que reduzirá a eficiência desse servidor.

Todos esses cálculos são bastante abstratos e não levam em consideração muitos fatores: descontos em um adiantamento ou em um grande número de solicitações, tempo de execução do lambda e assim por diante. Mas eles mostram que, com uma carga de menos de 1 solicitação por segundo, o lambda custa menos que o servidor EC2 mais barato.

A propósito, não esqueça de adicionar o preço do ALB ao preço do EC2 se você precisar manter várias réplicas de aplicativos ou IPs elásticos.


Os testes e o desenvolvimento local de soluções sem servidor são menos populares que os mesmos microsserviços. No entanto, mais e mais pessoas estudam e aplicam essas tecnologias e oferecem a chance de vários produtos de código aberto se tornarem melhores e mais eficientes. Essas soluções simplificam bastante a vida das empresas que precisam lançar um produto o mais rápido possível e não precisam configurar toda a infraestrutura. Mas mesmo em produtos já desenvolvidos, acho que existe um lugar para bancos de dados sem servidor ou lambdas.

Não pretendo ser um especialista na área de teste de soluções sem servidor, mas, com base na minha experiência pessoal positiva, decidi destacar os pontos principais deste artigo. Espero que sejam úteis para alguém, e talvez alguns de vocês possam me complementar ou até me corrigir, pelo que ficarei muito feliz.


Links Úteis



All Articles