DynamoDB może być wyzwalaczem dla Lambdy w momencie dodawania, aktualizacji oraz usuwania danych z tabeli. W tym wpisie przygotujemy kolejny prosty przykład wykorzystując język C# - tym razem wraz z dodaniem danych do tabeli dojdzie do uruchomienia funkcji, która odczyta wprowadzone dane a następnie wyśle je w wiadomości e-mail.
Kolejne kroki, które wykonamy w ramach tego wpisu:
utworzenie tabeli DynamoDB z kluczem głównym;
utworzenie roli, która będzie miała uprawnienia do pracy z DynamoDB oraz Lambdą;
utworzenie funkcji Lambda;
dodanie wyzwalacza na funkcji w celu wysłania wiadomości e-mail;
dodanie danych do tabeli - akcja, która będzie wyzwalaczem dla całej naszej logiki.
Dokładny opis działania
Zanim przejdziemy do właściwej implementacji poświęcimy kilka chwil na omówienie powyższych kroków w nieco bardziej szczegółowy sposób skupiając się na podstawowej interakcji pomiędzy DynamoDB i Lambdą:
W pierwszym kroku użytkownik dodaje dane do utworzonej tabeli DynamoDB;
Dodanie danych do DynamoDB spowoduje uruchomienie w tle funkcji Lambda, która wykorzystując usługę SNS wyśle wiadomość e-mail ze szczegółami operacji przeprowadzonej na tabeli;
Dodamy również logi, aby mieć pewność, że dana operacja została zarejestrowana w naszym "systemie" - do podglądu dziennika zdarzeń wykorzystamy CloudWatch.
Spójrzcie jeszcze jak prezentuje się "architektura" takiej aplikacji:
Tworzenie tabeli w DynamoDB
Początkowe kroki znacie już doskonale z poprzedniej serii. Przechodzimy do ekranu zarządzania usługą DynamoDB a następnie klikamy przycisk Create table. Na nowo otwartym ekranie konfiguracyjnym wprowadzamy nazwę tabeli oraz ustawiamy klucz główny tabeli:
Po kliknięciu przycisku Create musimy odczekać parę sekund. Po pomyślnie zakończonym procesie tworzenia zobaczymy ekran podsumowujący zawierający utworzoną konfigurację:
Skoro już jesteśmy na tym ekranie spójrzcie na zakładkę Items oraz akcję Create item - na poźniejszym etapie wpisu z tego miejsca dodamy nowy obiekt do naszej tabeli dzięki czemu wyzwolimy całą logikę aplikacji. Najpierw jednak musimy utworzyć odpowiednią rolę z dostępem do DynamoDB oraz Lambda.
Tworzenie roli
Nowe role tworzymy z poziomu Identity and Access Management (IAM). Tym razem musimy pamiętać, żeby wskazać DynamoDB jako serwis. Dodatkowo będziemy zobligowani o wskazanie przypadku użycia. Na potrzeby testu wybieramy Amazon DynamoDB Accelerator (DAX) - DynamoDB access i klikamy przycisk Next: Permissions:
Zanim pójdziemy dalej: Czym jest DAX? Jest to usługa pozwalająca na cachowanie danych dzięki czemu możemy uzyskać znacznie lepszą wydajność.
Na ekranie konfiguracyjnym roli możemy zobaczyć, że automatycznie została wskazana polityka AmazonDynamoDBFullAccess. W tym momencie nie musimy robić nic więcej ponieważ zawiera ona wszystkie uprawnienia, których potrzebujemy:
Klikamy przycisk Next: Tags a następnie od razu robimy Review:
Tworzenie roli dla DynamoDB i Lambda
Korzystając z wyszukiwarki usług przechodzimy do Identity and Access Management (IAM) wybierając z panelu nawigacyjnego po lewej stronie Roles:
Klikamy przycisk Create role a następnie wybieramy Lambdę jako usługę, która będzie korzystać z tej roli. Następnie wybieramy Next: Permissions:
Z poziomu widoku dostępnych polityk wybieramy: AmazonSNSFullAccess, AmazonLambda_FullAccess, CloudWatchFullAccess oraz AmazonDynamoDBFullAccess. Kilikamy przycisk Next: Tags oraz od razu robimy Review:
Ostatni krok w tym punkcie to sprawdzenie czy cztery powyższe polityki zostały przypisane do roli, następnie zdefiniowane nazwy dla roli oraz kliknięcie przycisku Create role.
Tworzenie funkcji Lambda
W tej części wpisu dodamy Lambdę a następnie ustawimy na niej wyzwalacz (trigger) na DynamoDB.
Dodajemy nową Lambdę, definiujemy nazwę, wskazujemy środowisko .NET Core 3.1 oraz wybieramy utworzoną w poprzednim kroku rolę:
Wraz z pomyślnym utworzeniem funkcji dodajemy trigger na DynamoDB:
Zanim przejdziemy do właściwej implementacji musimy jeszcze dokonać drobnych konfiguracji, żeby być w stanie wysyłać wiadomości e-mail. Korzystając z wyszukiwarki usług w konsoli AWS przechodzimy do Amazon Simple Email Service, gdzie wybieramy z panelu nawigacyjnego Email Addresses a następnie klikamy przycisk Verify a New Email Address. Na wskazany adres SNS będzie wysyłał powiadomienia e-mail. Przed tym jednak musimy dokonać wspomnianej powyższej weryfikacji:
Implementacja
Podobnie jak w poprzednim wypadku przechodzimy do naszego IDE ponieważ w momencie przygotowywania tego wpisu AWS, z poziomu konsoli, nie udostępnia nam edytora dla naszego języka/technologii. Implementacja naszej funkcji może przyjąć poniższą formę:
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Amazon;
using Amazon.Lambda.Core;
using Amazon.Lambda.DynamoDBEvents;
using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace awsLambdaDynamoEmail
{
public class Function
{
/// <summary>
/// Prosta funkcja reagująca na dodanie nowych danych do DynamoDB
/// Wykonujemy prostą analizę + wysłanie wiadomości email
/// </summary>
/// <param name="dynamoEvent"></param>
/// <param name="context"></param>
/// <returns></returns>
public async Task FunctionHandler(DynamoDBEvent dynamoEvent, ILambdaContext context)
{
context.Logger.LogLine($"Beginning to process {dynamoEvent.Records.Count} records...");
StringBuilder sb = new StringBuilder();
foreach (var record in dynamoEvent.Records)
{
context.Logger.LogLine($"Event ID: {record.EventID}");
context.Logger.LogLine($"Event Name: {record.EventName}");
// TODO: Dodać logikę biznesową przetwarzania obiektu (w ramach Waszych testów)
// W naszym przypadku pobieramy identyfikator klucza oraz jego wartość
foreach (var data in record.Dynamodb.NewImage)
{
context.Logger.LogLine($"Key: {data.Key}");
// Dynamiczne mapowanie używane w DynamoDB
// N jest symbolem pola numerycznego
// S jest symbolem pola tekstowego
if (data.Value.N != null)
{
context.Logger.LogLine($"Value: {data.Value.N}");
sb.AppendLine($"Klucz: {data.Key}, Wartosc: {data.Value.N}");
}
else
{
context.Logger.LogLine($"Value: {data.Value.S}");
sb.AppendLine($"Klucz: {data.Key}, Wartosc: {data.Value.S}");
}
}
}
// Ustawcie adres email zweryfikowany w obrębie konkretnego regionu
string senderAddress = "podaj_zweryfikowany_adres@gmail.com";
// Pamiętajcie o ustawieniu regionu w którym dokonaliście weryfikacji adresu email
// W przeciwnym wypadku zobaczycie poniższy błąd:
// Email address is not verified.
// The following identities failed the check in region EU-WEST-2: adres_email@gmail.com
using (var client = new AmazonSimpleEmailServiceClient(RegionEndpoint.USEast1))
{
var sendRequest = new SendEmailRequest
{
Source = senderAddress,
Destination = new Destination
{
ToAddresses =
new List<string> { "podaj_zweryfikowany_adres@gmail.com" }
},
Message = new Message
{
Subject = new Content("Wpisz temat wysyłanej wiadomości"),
Body = new Body
{
Html = new Content
{
Charset = "UTF-8",
Data = sb.ToString()
},
Text = new Content
{
Charset = "UTF-8",
Data = "body"
}
}
}
};
var response = await client.SendEmailAsync(sendRequest);
}
context.Logger.LogLine("Stream processing complete.");
}
}
}
Publikacji funkcji dokonamy używając zainstalowanej wtyczki z drobnymi zmianami pozwalającymi podpiąć się pod istniejącą funkcję:
Testy
Pora na ostatnie kroki w tym wpisie. Nasza Lambda zostanie uruchomiona pod wpływem dodania nowych danych do tabeli. W tym celu wykorzystamy konsolę AWS definiując prosty obiekt:
Wraz z poprawnym dodaniem obiektu do tabeli DynamoDB możemy przejść do dziennika zdarzeń naszej funkcji w celu sprawdzenia czy Lambda wykonała swoje zadanie:
Możecie również zajrzeć do swojej skrzynki mailowej w celu sprawdzenia czy wysłana wiadomość dotarła na docelowy adres:
Wszystko działa poprawnie chodź sam przykład jest niezwykle trywialny. Głównym założeniem jest jednak natychmiastowa akcja w postaci wywołania funkcji, która może dokonać dalszego przetwarzania czy analizy danych, powiadomienia osób trzecich bądź powiadomienia innego (mikro)serwisu o nadejściu nowego zamówienia i natychmiastowym wdrożeniu kolejnych kroków niezbędnych do realizacji usługi.