Ist Serverless die Ablösung der Microservices-Architektur? – Teil III

Von Spring Boot nach AWS Lambda – das Experiment

Oliver Wronka

(c) Shutterstock / mamanamsai

Eine Spring Boot Microservices-Anwendung nach AWS Lambda migrieren – dieses Experiment wagen wir in dieser Artikelreihe. Nach der Implementierung der Persistenz und der Anpassung der Anwendungslogik geht es in Teil 3 um die Vorkehrungen, um unsere Lambda-Handler aus dem Frontend heraus aufrufen zu können.

Von Spring Boot nach AWS Lambda

In dieser Artikelreihe versuche ich eine Antwort auf die Frage zu geben, ob der Ansatz des Serverless Computing die logische Fortsetzung der Microservice-Architekturen darstellen. Dafür soll ein vollständiges Beispiel für eine auf AWS Lambda betriebene Webanwendung implementiert werden.

Ist AWS Lambda das bessere Spring Boot?
Teil I: Setup und Persistenz
Teil II: Logik
Teil III: Von Lambda zu Lambda
Teil IV: Präsentation
Teil V: Authentisierung und Autorisierung

 

Von Lambda zu Lambda

Nach der Implementierung der Anwendungslogik in AWS Lambda (siehe Teil II) lassen sich unsere Lambdas nun via REST ansprechen. Eigentlich wäre es jetzt an der Zeit, das Frontend anzugehen. Vorher wollten wir uns aber noch Gedanken darüber machen, wie man einen Lambda-Handler aus einem eben solchen heraus aufrufen kann.

In unserem Beispiel wollen wir den Bounded Context einer Person zwischen Registrierung und Login aufrecht erhalten. Bei der Registrierung werden alle möglichen Daten zu einer Person erfasst. Neben Name und Adresse unter anderem auch die Angaben zum Account, also Login sowie Passwort. Diese wollen wir jedoch nicht dem Microservice Registration zuordnen, sondern dem Microservice Login.

Da der Microservice Login bereits einen Handler hat, würde es Sinn ergeben, diesen für das Anlegen des Accounts zu verwenden. Hierfür gibt es zwei typische Umsetzungsszenarien. Entweder nutzt man eine Queue für die asynchrone Anbindung oder ruft den Login Lambda Handler direkt aus dem Handler für die Registrierung auf, allerdings asynchron.

Beginnen wir mit dem direkten Aufruf aus dem Handler für die Registrierung. Hier der Code des Registration Handlers:

switch (e.getHttpMethod()) {
    case "POST":
        long pid;
        String mid;

        
        PersonTO pto = rto.getPerson();
        AccountTO acto = rto.getAccount();
        AddressTO adto = rto.getAddress();
        
        pid = rs.setPerson(pto, acto).getId();
        
        pto.setPersonId(pid);
        
        // Secure random salt
        String salt = Security.generateSalt();
        // hashed password
        String genPassword = Security.getSaltedPwdHash(acto.getPwdHash(), salt);

        acto.setpId(pid);
        acto.setPwdHash(genPassword);
        acto.setPwdSalt(salt);
        
        rs.setAccount(acto);
        
        rs.setAddress(adto, pid);
        
        rs.close();
        
        r.setStatusCode(200);
        r.setHeader("Access-Control-Allow-Methods", "POST");
        r.setHeader("Access-Control-Allow-Origin", "*");
        r.setHeader("Content-Type", "application/json");
        r.setBody(pto.toJSON());
        
        break;
        
    default:
            
        r.setStatusCode(400);
        r.setBody(Message.toJSON(400, "ERROR", "Invalid http method received [" + e.getHttpMethod() + "]"));
        
        break;
}


Im ersten Block werden die Daten des Registrationrequests in die einzelnen Informationsaspekte einer Person zerlegt:

PersonTO pto = rto.getPerson();
AccountTO acto = rto.getAccount();
AddressTO adto = rto.getAddress();
...
rs.setAccount(acto);

Diese bestehen in diesem Fall aus der Person selbst, der Adresse sowie dem Account. Wie schon gesagt, den Account selbst wollen wir dem Login Lambda zuweisen. Als erstes machen wir dies, indem wir den entsprechenden Handler des Login Lambdas direkt, wenn auch asynchron aufrufen.

Hierzu bietet Amazon in seinem SDK die Klasse AWSLambdaAsync an. Diese bietet die Möglichkeit, einen Lambda Handler direkt aufzurufen. Der entsprechende Code ist recht straff:

public void setAccount(AccountTO acto) throws SQLException, JsonProcessingException {

    Event e = new Event ("/login/account", null, null, false,
              "/login/account", "POST", null, null, acto.toJSON());
    InvokeRequest iaRequest = new InvokeRequest(). 
                  withFunctionName("loginFunction").
                  withPayload(e.toJSON());  
    Future<InvokeResult> iaResult = 
                         AmazonService.getLA().invokeAsync(iaRequest);
}


Bei der Klasse Event handelt es sich um unsere eigene Klasse, die den Inputstream, den Amazon beim Aufruf eines Handlers liefert, in eine handhabbare Datenstruktur abbildet. Umgekehrt kann man aber auch eine Instanz dieses Events bilden, entsprechend initialisieren und an die AWS-Lambda-Infrastruktur als Parameter übergeben.

Das scheint erst einmal elegant zu sein, allerdings hat die Sache einen unschönen Haken. Der Login Handler lauscht bereits auf die http-POST-Methode unter der URL /login/account.

Wir müssten also anhand des Inhalts entscheiden, welche Ressource über die URL übergeben wird. Doch dies entspricht nicht den REST-Prinzipien.

Die Alternative ist sauberer, aber auch aufwändiger. Diese besteht darin, dass wir einen eigenen Lambda Handler nur für das Anlegen eines Accounts implementieren. Und wenn wir schon einen dezidierten Handler haben, so sollten wir diesen indirekt mittels Messaging ansprechen. Hierzu bietet Amazon den Simple Notification Service (SNS) an. Dieser stellt eine einfache, aber hoch verfügbare und gut skalierende Messaging-Infrastruktur dar. Eine entsprechende Queue ist sehr schnell konfiguriert. Zuerst legt man ein Topic an, dies stellt sozusagen den Einstiegspunkt in die Queue dar.

Einrichten eines Topic in AWS SNS

Jetzt haben wir also eine Möglichkeit geschaffen, Nachrichten in unsere Queue einzustellen. Nun müssen wir die Queue noch mit unserem neuen Lambda Handler verbinden, sodass mit jeder Nachricht direkt unser Handler aufgerufen wird. Dies geschieht innerhalb von SNS, indem man einen entsprechenden Subscriber konfiguriert:

Einrichten eines Subscribers in AWS SNS

Und ja, es ist wirklich so einfach, wie es hier aussieht. Nun können wir unseren Lamda Handler aus unserem Code wie folgt aufrufen:

public void setAccount(AccountTO acto) throws JsonProcessingException {

    PublishRequest publishRequest = new PublishRequest(TOPIC_ARN, 
                                        acto.toJSON());
    PublishResult publishResult = 
                  AmazonService.getSNS().publish(publishRequest);
}


Zwischenstopp

Persistenz, Anwendungslogik und Lambda Handling sind somit erledigt. Damit hätten wir alles zusammen, uns dem Frontend zuzuwenden. Darum soll es in Teil 4 dieser Serie gehen. Bis dahin viel Spaß beim Nachvollziehen der einzelnen Schritt!

Ergänzende Informationen wie z.B. ausführliche Listings habe ich auf meinem Blog http://java-goes-serverless.blog/ hinterlegt.

Weiter mit Teil IV der Serie:

AWS Lambda versus Spring Boot – wer gewinnt?

 

Geschrieben von
Oliver Wronka
Oliver Wronka
Oliver Wronka befasst sich mit Java seit der Version 1.0. Schwerpunkt in seiner Funktion als Principal-Softwarearchitekt bei der axxessio GmbH mit Sitz in Bonn sind Java-basierte Backend-Systeme.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.