AWS Lambda in Aktion. Teil 2: Kennenlernen von Entwicklungs- und Testwerkzeugen



Dieser Leitfaden ist das Ergebnis persönlicher Erfahrungen beim Entwickeln und Testen von Anwendungen ohne Server sowie beim Manövrieren zwischen Krücken und Fahrrädern beim Versuch, diese zu testen. Als ich anfing, Serverless-Anwendungen zu entwickeln, musste ich alles mit meinen Händen herausfinden. Es gab keine klaren Anleitungen oder praktischen Tools für die lokale Entwicklung und das Testen.

Jetzt ändert sich die Situation: Es tauchten Werkzeuge auf, aber es ist nicht einfach, Handbücher zu ihrer Verwendung zu finden. In diesem Artikel werde ich anhand eines einfachen Beispiels zeigen, wie mit der neuen Funktionalität gearbeitet wird. Sie können es leicht meistern und die Probleme und Fehler umgehen, auf die ich „Glück“ hatte.

Sie lernen, wie Sie mit der AWS-, SAM-CLI- und IntelljIDEA-Browserkonsole entwickeln. Ich werde auch über das Testen sprechen: Integration, E2E und Unit-Tests. Und schließlich werden wir diskutieren, wie viel eine solche Lösung kosten wird (Spoiler: Sie können viel sparen).

Dieser Artikel ist nützlich für diejenigen, die mit der Entwicklung von Anwendungen ohne Server beginnen und noch nicht mit den Tools und Ansätzen vertraut sind.


Was wird besprochen


Hallo alle zusammen! Mein Name ist Alexander Gruzdev und ich freue mich, den zweiten Teil des Artikels über die Verwendung und Entwicklung von Serverless-Lösungen auf Basis von AWS Lambda vorstellen zu können. In der Folge werden wir im ersten Teil über die Möglichkeiten zum Entwickeln und Testen der Lösung sprechen ( AWS Lambda in Aktion in Java 11. Wir besuchen Serverless in „Produktion“ ).

Seit der Veröffentlichung des letzten Artikels wurde die Testanwendung leicht überarbeitet, die Hauptbedeutung bleibt jedoch gleich. Dies ist ein triviales HTML-Formular „Kontakt“, das Daten aus dem Formular über HTTP-Anforderungen an die Gateway-API sendet und die Verarbeitungsanforderung wiederum an AWS Lambda weiterleitet. Während der Verarbeitung schreibt das Lambda Daten in die DynamoDB-Tabelle und sendet einen Brief über AWS SES. Das Komponentendiagramm ist unten dargestellt.

Komponentendiagramm


Inhalt


  1. Entwicklung mit der AWS Browser Console
  2. Entwicklung mit SAM-CLI
  3. Entwicklung mit IntelljIDEA
  4. Arten von Tests
  5. Serverlose Kosten
  6. Nützliche Links



1. Entwicklung mit der AWS-Browserkonsole



AWS-Konsole


Die AWS-Konsole bietet Zugriff auf jeden AWS-Service, sei es auf Ihren virtuellen EC2-Server, IAM- oder AWS Lambda-Rollen- / Zugriffsrichtlinieneinstellungen, mit allen Parametern, die in Echtzeit ohne erneute Bereitstellung über Continuous Delivery- / Continuous Deployment-Systeme geändert werden können.

Dies beschleunigt den Entwicklungsprozess und einige Arten von Tests erheblich. Es hilft auch, die Start- und Betriebsparameter Ihrer Anwendungen visuell zu steuern.

API Gateway Console


Über die Konsole können Sie den aktuellen Status der Anwendung überwachen, Ihre Umgebungen verwalten und neue Versionen bereitstellen. Ich werde zeigen, wie dies verwendet werden kann, um den Zustand der Umwelt zu überprüfen.

Wie diese „magische“ Konsole aussieht, sehen Sie im Screenshot der Registerkarte Ressourcen der Anwendung Kontakt: Der Screenshot zeigt, wie die Anforderung vom Client über die Gateway-API in das Lambda gelangt und wie die Antwort an den Client zurückgegeben wird, wenn die Ressource / contact von der POST-Methode aufgerufen wird.

Gateway-Ressourcen



  • Methodenanforderung - der erste Schritt bei der Verarbeitung der Anforderung. In unserem Fall enthält es keine Einstellungen, kann aber beispielsweise Einstellungen für die Autorisierung enthalten.
  • Integrationsanforderung bedeutet, dass die HTTP-Anforderung in einem speziellen Format an das Lambda übertragen wird: Sie enthält alle vom Client gesendeten Informationen und Details zum aufgerufenen Kontext.
  • Die Integrationsantwort besagt, dass der HTTP-Client die Antwort direkt vom Lambda erhält. Daher sind Sie selbst für die Bildung des Reaktionskörpers in Lambda verantwortlich.


Deaktivieren Sie die Proxy-Integration
API Gateway , Use Lambda Proxy integration Integration Request. . , , .


Beispiel für eine Integrationsanforderung
SAM init events, , Integration request. , AWS , . AWS Lambda.


Drücken Sie die TEST-Taste, um zu überprüfen, ob die Anwendung funktioniert. Dadurch erhalten Sie Zugriff auf die HTTP-Gateway-API-Aufrufe.

Im folgenden Screenshot habe ich eine Anfrage zum Aufwärmen des Lambda gesendet: Zusätzlich zur Antwort auf die Anfrage zeigt die Gateway-API detaillierte Protokolle an. Auf die gleiche Weise können Sie eine echte Anfrage stellen, indem Sie den JSON-Anfragetext in der folgenden Form ersetzen:
Gateway-Testanforderung


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


So können Sie Ihre API manuell ohne UI-Formulare und Front-End-Anwendungen testen und so die Front-End- und Back-End-Entwicklung trennen. Alternativ zur integrierten Konsole können Sie Postman oder einen anderen HTTP-Client verwenden.

Postbote als Alternative
HTTP- Postman. . , API Gateway AWS , API. , , AWS .


Lambda-Konsole


Zum Testen und Entwickeln können Sie zusätzlich zu Anforderungen über die Gateway-API die AWS Lambda-Browserkonsole verwenden.

Sein Bildschirm ist in mehrere Blöcke unterteilt:
  • Designer zeigt vorhandene Trigger und Ebenen an. In unserem Fall ist der Auslöser API Gateway. Wenn wir darauf klicken, werden alle erforderlichen Informationen geöffnet: URL, Stufe, Ressourcenpfad.
  • Mit Aliasen (verfügbar bei Auswahl eines bestimmten Lambda-Alias) können Sie die Verteilung des Datenverkehrs nach Lambda-Version konfigurieren. Mehr dazu im nächsten Absatz.
  • 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 .
  • Mit dem asynchronen Aufruf können Sie die spezielle Behandlung von asynchronen Anforderungen konfigurieren. Wir geben an, wie viele wiederholte Anrufe im Fehlerfall getätigt werden müssen und wie lange die Anforderung in der Warteschlange hängen bleiben kann. Optional können Sie eine Warteschlange für die Verarbeitung fehlgeschlagener Anforderungen angeben.

  • Datenbank-Proxys sind eine weitere neue Funktion, mit der Sie den Zugriff auf die Datenbank über Proxys in Form von API Gateway + Lambda konfigurieren können.


Als nächstes wollen wir sehen, wie man aus dieser Konsole entwickelt. Zusätzlich zum Ändern der Parameter kann mit der Funktion " Testereignisse konfigurieren" eine Testanforderung erstellt werden. Eine ziemlich umfangreiche Liste ist verfügbar, aber wir werden das nutzen, was ich bereits im Projekt angehängt habe:
ContactUs Anfrage
{
  "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"
  }
}


Klicken Sie nach dem Hinzufügen der Anforderung auf Test, um das Ergebnis des Lambda anzuzeigen: Lambda gibt JSON zurück, das den Statuscode und den Antworttext enthält. Der Body-Wert geht direkt an den Client, der die Gateway-API aufgerufen hat. Darüber hinaus weist der Screenshot die Merkmale des Aufrufs auf: die Ausführungszeit, die Ausführungszeit, für die wir bezahlen, die maximale Speichermenge, die vom Lambda verbraucht wurde. Basierend auf diesen Metriken können wir die optimale Speichermenge so konfigurieren, dass das Lambda Ihren Preis- / Antwortratenanforderungen entspricht.
Gateway-Ressourcen



Verwenden der AWS-Konsole zum Testen der kanarischen Bereitstellung


Lassen Sie uns nun einen wichtigen Punkt im Zusammenhang mit der Bereitstellung einer neuen Lambda-Version ansprechen. Ich habe solche Parameter angegeben, als ich die SAM-Vorlage schrieb:
AutoPublishAlias: live
DeploymentPreference:
  Type: Canary10Percent10Minutes

Der erste Parameter fügt jeder neuen Version des Lambda automatisch einen Live- Alias ​​hinzu .
Ein bisschen über Aliase
, . , , 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.


Zum Testen der kanarischen Bereitstellung können Sie sowohl die Gateway-API-Konsole als auch die Lambda-Konsole verwenden.

Zuerst müssen Sie den Lambda-Code ändern, damit er eine andere Antwort zurückgibt. Geben Sie beispielsweise in der Aufwärmverarbeitung in der Antwort Version 2 anstelle von Version 1 an. Dies ist unser Marker.

Mithilfe der API-Funktionalität des Gateway-Tests können wir jetzt sicherstellen, dass beim Senden von WARM-UP-Anforderungen innerhalb von zehn Minuten nur 10% der Antworten Version 2 im Antworttext enthalten. Nach dieser Zeit gibt Version 2 100% der Anforderungen zurück.

Zusätzlich zur Gateway-API-Konsole können wir die Lambda-Konsole verwenden, bei der nach Auswahl des erforderlichen Alias ​​eine Richtlinie zur Verkehrsverteilung angezeigt wird, die gesteuert werden kann.
Lambda alias



2. Lokale Entwicklung mit SAM-CLI


Vor einiger Zeit wurde einer der Hauptnachteile bei der Verwendung von Lambda als unangenehm bei der Entwicklung und dem Testen auf dem lokalen Computer angesehen. Jetzt hat sich die Situation stark verändert: Das SAM-Framework ist erschienen, mit dem nicht nur Lösungen gesammelt und bereitgestellt werden können, sondern auch die lokale Entwicklung vereinfacht wird.

Sie können Ihr Lambda beispielsweise direkt von der Konsole aus aufrufen:
sam local invoke -e ./events/warm_up_request.json ContactUsFunction

Dieser Befehl wird aus dem Verzeichnis mit der SAM-Vorlage gestartet und überträgt den Inhalt der JSON-Datei an das Eingabe-Lambda ContactUsFunction (dies ist der logische Name in der Vorlage).

Das heißt, mit diesem Befehl löst SAM das Image lambci / lambda: java11 im Docker aus und führt Ihren Code darin aus. Für den Zugriff auf Remotedienste wie SES wird Ihre AWS-Konfiguration mit einem Geheim- / Zugriffsschlüssel verwendet. Sie muss daher auf dem neuesten Stand sein. Ein weiterer wichtiger Punkt: Wenn Sie keinen Header für den Aufwärmmodus hinzufügen, werden die echten AWS SES- und DynamoDB-Dienste aufgerufen.

Rufen Sie hier an
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}


Zusätzlich zum Lambda-Protokoll werden Serviceinformationen zum Anrufstatus und eine Antwort im JSON-Format in die Konsole geschrieben.

Die Gateway-API kann wie das Lambda lokal ausgeführt werden.
Wir führen den Befehl aus:
sam local start-api

Konsolenprotokoll beim Start
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)


Nach dem Start des Servers können wir Anfragen ausführen. Ich werde Postman benutzen und eine Anfrage senden, um das Lambda aufzuwärmen.

In der Konsole sehen wir
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 -


Protokolle werden natürlich nicht optimal formatiert. Hoffentlich wird dies in Zukunft behoben.

Wichtig: Wenn Sie den Lambda-Code ändern, ist ein Neustart der API nicht erforderlich. Es reicht aus, das Lambda mit dem Befehl sam build neu zu erstellen .
Ein bisschen über 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. Ortsgespräche von IDEA


Alles, was im obigen Absatz beschrieben wurde, ist die Funktionalität des SAM-Tools. Es wird über die Befehlszeile gesteuert und kann in jede Schnittstelle integriert werden. Genau dies wurde im Plugin für IntelljIDEA AWS Toolkit getan.

Dieses Plugin fügt zusätzliche Konfigurationen zum Starten eines Lambda direkt von der IDE hinzu. So sieht das Konfigurationsfenster aus. Sie müssen entweder eine Vorlage mit einer Beschreibung des Lambda auswählen oder die Einstellungen manuell konfigurieren. Im ersten Fall wird der Speicherplatz und die Sprache für die Kompilierung automatisch aus der Vorlage entnommen. Im zweiten Fall können Sie die RAM-Größe selbst konfigurieren und eine ungefähre Berechnung des Grenzwerts vornehmen. Mit einem anderen Plugin können Sie die Konfiguration direkt aus dem Fenster des Java-Code-Editors oder der Yaml-Vorlage ausführen. Screenshots unten:











Das ist in der Tat alles. Durch Ausführen der benutzerdefinierten Konfiguration automatisieren Sie einfach häufige Lambda-Aufrufe von der Konsole aus. Einverstanden, ganz bequem?


4. Testen


Hier sprechen wir über die Arten von Tests für Lambdas. Da ich im Beispiel Java verwendet habe, enthalten die Tests auch Standardtools für Unit- und Integrationstests: Junit 4, Mockito und PowerMockito.

Unit-Tests


Um den Lambda-Code mit Unit-Tests abzudecken, benötigen Sie keine spezifischen Kenntnisse. Es reicht aus, Standard-Java-Praktiken anzuwenden: Sperren Sie alle Abhängigkeiten in der Klasse und versuchen Sie, alle möglichen Fälle der Anforderungsverarbeitung zu testen.

Ich habe nicht alle Testszenarien hinzugefügt und mich auf zwei positive und zwei negative Szenarien beschränkt.

Der erste positive Test bestätigt, dass bei einem X-WARM-UP-Header keine Anforderungen in DbService und EmailService vorhanden sind. Der zweite Fall prüft, ob diese Dienste aufgerufen werden, wenn die Anforderung echt ist. Dies sind die einfachsten Tests, und ich habe einen Teil der darin enthaltenen Prüfungen weggelassen.

Negative Szenarien sind eine Antwortprüfung im Falle eines Client- oder Serververarbeitungsfehlers.

Integrationstests


Für Integrationstests habe ich mich entschlossen, DbService und seine Arbeit mit der DynamoDB-Tabelle zu testen.

In diesem Fall habe ich das Localstack- Tool verwendet . Diese Lösung bietet kostenlose Mokas für AWS-Services, die als Docker-Container ausgeführt werden.

Führen Sie zum Installieren dieser Lösung einfach die folgenden Befehle aus:
pip install localstack
localstack start

Oder Sie können die Docker-Compose-Datei verwenden .
Docker-Compose zum Erhöhen nur von 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"


Der Befehl start löst standardmäßig alle verfügbaren AWS-Services aus. Um DynamoDB spezifisch zu erhöhen, legen Sie einfach die Umgebungsvariable fest:
set SERVICES=dynamodb


Es gibt einen speziellen LocalstackTestRunner für Junit , mit dem Sie die erforderlichen Dienste mithilfe der Konfiguration @LocalstackDockerProperties starten können .
Das Schreiben eines Tests für DbService sieht daher folgendermaßen aus:
  • Über die Testklasse hinzufügen.
    @RunWith(LocalstackTestRunner.class)
    @LocalstackDockerProperties(services = { "dynamodb" })
    

  • Erstellen Sie eine Tabelle
    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)));
    }
                    


  • Wir beschreiben das Testszenario
    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"));
                  


  • Vergessen Sie nicht, die Tabelle nach dem Test zu löschen.
    Java
    @After
    public void tearDown() {
        dynamoDB.getTable("ContactUsTable").delete();
    }
                   



Als Ergebnis erhalten wir mit Localstack eine ziemlich umfangreiche Auswahl an AWS-Mocks, mit denen Sie die Arbeit dieser Services emulieren können.

E2E-Tests


Um E2E-Szenarien abzuspielen, ist es besser, AWS-Testdienste speziell für die Ausführung von Tests zu erstellen, wenn natürlich eine solche Möglichkeit besteht. Dies kann mithilfe der optionalen CloudFormation-Vorlage erfolgen, in der die Ressourcen Kopien der Hauptvorlage sind.

Der Test kann durchgeführt werden, indem das Lambda über AWS-CLI aufgerufen und das Ergebnis mit einem beliebigen verfügbaren Tool verarbeitet wird. Sie können weiterhin in Unit-Tests anrufen, ohne jedoch Mobs zu erstellen.

Leistungstests


Da Leistungstests einen separaten Artikel oder sogar ein Buch verdienen, werde ich nicht versuchen, alle Informationen hier zu platzieren. Kurz gesagt, wenn Sie über Stresstests sprechen, müssen Sie bedenken, dass die Nutzung fast aller AWS-Services nicht kostenlos ist, und Sie sollten bereit sein, die möglichen Kosten im Voraus abzuschätzen.

Wenn Sie die erforderlichen Ressourcen für ein Lambda auswerten möchten, verwenden Sie die UI-Konsole und führen Sie einige Testaufrufe durch. Bewerten Sie anschließend anhand der gängigen Logik und Mathematik, wie stark sich die Leistung Ihres Lambda in Abhängigkeit von der Menge der verfügbaren Ressourcen erhöht.

Vergessen Sie CloudWatch nicht. Mit seiner Hilfe können Sie sehr aussagekräftige Dashboards erstellen und anhand dieser bestimmen, welche Lambda-Parameter und Ausgaben Sie sich leisten können.


5. Die Kosten für serverlose Lösungen


Die Bewertung einer Lösung ist immer spezifisch, da Sie nicht nur die Kosten eines Anrufs, sondern auch den eingehenden / ausgehenden Datenverkehr sowie die Kosten für das Zwischenspeichern, Protokollieren und Überwachen berücksichtigen müssen. Wenn Sie die Verwendung von Lambda in Betracht ziehen, versuchen Sie, die Last vorherzusagen, die Ihr Dienst bewältigen wird.

Wenn es sich um Einzelanrufe oder einen ETL-Prozess handelt, der alle paar Stunden / Tage beginnt, zahlt sich das Lambda eher aus. Wenn Sie eine Belastung von mehreren Abfragen pro Sekunde vorhersagen, müssen Sie eine genaue Schätzung vornehmen.

Im Folgenden gebe ich ungefähre Schätzungen der Kosten für das Lambda und den billigsten EC2-Server an, damit Sie verstehen, um welche Werte es sich handelt: Die folgenden Parameter für das Lambda wurden zur Berechnung herangezogen:




  • Vorlaufzeit: 150 Millisekunden
  • Preis: $ 0.00001667 für 1 GB-s
  • Speicher: 256 MB

Für EC2 t2.nano:

  • Preis: 4,75 USD pro Monat
  • Speicher: 512 MB
  • vCPU: 1
  • Maximale Auslastung: 5% CPU pro Monat

Ich habe von hier EC2-Lastdaten genommen . Nach diesen Berechnungen kann der angegebene Server einer konstanten Last von 0,3 Anforderungen pro Sekunde standhalten. Mit zunehmender Last erhöht sich die Antwortzeit, was die Effizienz dieses Servers verringert.

Alle diese Berechnungen sind recht abstrakt und berücksichtigen nicht viele Faktoren: Rabatte auf eine Vorauszahlung oder eine große Anzahl von Anfragen, Lambda-Ausführungszeit usw. Sie zeigen jedoch, dass Lambda mit einer Last von weniger als 1 Anforderung pro Sekunde weniger kostet als der billigste EC2-Server.

Vergessen Sie übrigens nicht, den Preis von ALB zum EC2-Preis hinzuzufügen, wenn Sie mehrere Anwendungsreplikate oder elastische IPs aufbewahren müssen.


Das Testen und die lokale Entwicklung von Serverless-Lösungen sind weniger beliebt als dieselben Microservices. Dennoch studieren und wenden immer mehr Menschen diese Technologien an und geben verschiedenen Open-Source-Produkten die Möglichkeit, besser und effizienter zu werden. Solche Lösungen vereinfachen das Leben von Unternehmen erheblich, die ein Produkt so schnell wie möglich veröffentlichen müssen und nicht die gesamte Infrastruktur konfigurieren müssen. Aber selbst in bereits entwickelten Produkten gibt es meiner Meinung nach einen Platz für serverlose Datenbanken oder Lambdas.

Ich behaupte nicht, ein Experte auf dem Gebiet des Testens von Serverless-Lösungen zu sein, aber aufgrund meiner persönlichen positiven Erfahrung habe ich beschlossen, die wichtigsten Punkte in diesem Artikel hervorzuheben. Ich hoffe, dass sie jemandem nützlich sein werden, und vielleicht können einige von Ihnen mich ergänzen oder sogar korrigieren, worüber ich mich sehr freuen werde.


Nützliche Links



All Articles