LAGOM...Lagom sorgt für Implementierung für Service-Zugriff …inclusive „Service“ wie Circuit...

Preview:

Citation preview

LAGOMDIE RICHTIGE DOSIS MICROSERVICE

Dr. Stefan Schlott und Armin Bauer ( )BeOne Stuttgart GmbH

ABOUT.TXTStefan Schlott, BeOne Stuttgart GmbH

Advisory Consultant, Architekt, Java-Entwickler, Scala-Enthusiast, Linux-Jünger

Begeistert für Skalierbarkeit, Security, Privacy - undandere skurile Techniken ;-)

ABOUT.TXTArmin Bauer, BeOne Stuttgart GmbH

Senior Consultant, Architekt, Java-Entwickler, OpenSource - Fan, …

Begeistert für Technologie, MaschinelleSprachverarbeitung und Software Engineering

WIR WOLLENMICROSERVICES!(UND ANDERE MODERNE

TECHNOLOGIEN)

ZUR ERINNERUNG: WELCHEERWARTUNGEN STELLEN WIR AN DIE

EIGENSCHAFTEN VONMICROSERVICES?

EIGENSCHAFTEN: GRÖSSE... sollte von einem Team gut zu bearbeiten sein... sollte als ganzes neu implementiert werden... sollte für neue Entwickler leicht zu verstehen sein

■■■

EIGENSCHAFTEN: DOMÄNEJeder Microservice bearbeitet einen abgetrennten Teil der DomäneJeder Microservice hat eine eigene Datenstruktur die er verwaltetDie Microservices kommunizieren um miteinander zumInformationsaustausch

■■■

UND WIE WAR DAS MIT REACTIVE?!

EIGENSCHAFTEN: REACTIVE SYSTEMSScalable

Resilient

Responsive

Message Driven

(siehe )Reactive Manifesto

WEITERE BUZZWORDSCQRSEvent SourcingAsynchronous

■■■

FÜR ALLES GIBT ES EINE LÖSUNG…

FERTIG, ODER?

WILLKOMMEN IM ZOO!

DER TECHNOLOGIE-ZOOWie reden die Systeme miteinander?

Austauschbarkeit?

Muss ich das jetzt alles ansprechen?

Was muss ich jetzt händisch machen?

Wie bringe ich das auf meiner Entwicklermaschine zum laufen?

Setupzeit für neue Teammember?

Wer administriert das denn jetzt alles?

WAS IST LAGOM UND WASWILL ES IN MEINEM TEAM?

WORTHERKUNFT

Schwedisch: Genau die richtige Menge

WAS BRINGT LAGOM MIT?Gemeinsamer Deckel und API über die Tools und TechnologienGeschmackvolle Vorauswahl an Technologien„Opinionated“ FrameworkGemeinsamer Start aller ServicesHot Code Reload

■■■■■

FÜR WEN IST LAGOM?Neue Microservice ProjekteRefactoring Tool für MonolithenJava API (und seit Lagom 1.3 auch eine Scala API)

■■■

BEISPIELPROJEKT:GESCHENKE-DOODLE

BESCHREIBUNGEs soll eine Software entwickelt werden, mit der mehrere Leute ein

gemeinsames Geschenk kaufen können

Der Initiator legt das Geschenk webbasiert anDie Mit-schenker können einen Betrag zum Geschenk dazugebenWenn das Ziel erreicht ist, erhält der Initiator eine Mail und weissnun, dass er das Geschenk kaufen kann

■■■

DOMÄNENMODELL UND ARCHITEKTUROrganize-Service: Verwaltung und Status der Geschenke

Contribute-Service: Entgegennehmen der einzelnen Beiträge

Mail-Service: Versand der Info-Mails

NA DANN MAL LOS.

DIE IMPLEMENTIERUNG DESGESCHENK-DOODLE

DEFINITION VON SERVICES

WAS GEHÖRT ZURSERVICEDEFINITION?

Definition der Methoden die der Service anbietetDefinition der Datenstruktur für Request und Response für denService

■■

BEISPIEL-SERVICEEin Java - Interface definiert die Methoden die der Service anbietet.

public interface OrganizeService extends Service { ServiceCall<NotUsed, PresentBaseInformation> getBaseInfo(String id); ServiceCall<PresentCreateRequest, PresentCreateResponse> organizePresent(); ServiceCall<NotUsed, PresentAdminInfo> getAdminInfo(String id);

@Override default Descriptor descriptor() { return Service.named("organizeservice").withCalls( Service.pathCall("/api/organize/create-present", this::organizePresent), Service.pathCall("/api/organize/admin/:id", this::getAdminInfo), Service.pathCall("/api/organize/get/:id", this::getBaseInfo) ).withAutoAcl(true); }}

WAS IST EIGENTLICH...Service.pathCall ... Mapping von Pfadelementen auf Methodenparametergenerics von ServiceCall ... Mapping von RequestBody auf ResponseBody TypNotUsed ... Bedeutet das es keinen Requestbody gibt.

Die verwendeten Typen sind einfache JSON-serialisierbare Objekte

ANGENEHM:Die gewählte Art von Abstraktion ist sehr weit von

HTTP als Transportschicht entfernt

DATENSTRUKTURAm gewohntesten als Beans mit Gettern und Settern, aber eleganter

wenn man es Immutable macht

@Immutable@JsonDeserializepublic class PresentAdminInfo { public final String organisator; public final String description; public final LocalDate deadline;

@JsonCreator public PresentAdminInfo(String orga, String description, LocalDate deadline) { this.organisator = orga; this.description = description; this.deadline = deadline; }}

IMPLEMENTIERUNG VONSERVICES

INTERFACEIMPLEMENTIEREN

Der Service implementiert zunächst das Interface

public class OrganizeServiceImpl implements OrganizeService { … @Override public ServiceCall<NotUsed, PresentBaseInformation> getBaseInfo(final String id) { return request -> persistentEntityRegistry.refFor(Present.class, id) .ask(GetPresentData.INSTANCE) .thenApplyAsync(PresentBaseInformation::of); } …}

DEPENDENCY INJECTIONMit Guice als Constructor Injection

public class OrganizeServiceImpl implements OrganizeService { private final PersistentEntityRegistry persistentEntityRegistry;

@Inject public OrganizeServiceImpl(PersistentEntityRegistry per) { persistentEntityRegistry = per; persistentEntityRegistry.register(Present.class); } …}

SERVICE REGISTRIERENpublic class OrganizeServiceModule extends AbstractModule implements ServiceGuiceSupport { @Override protected void configure() { bindServices(serviceBinding(OrganizeService.class, OrganizeServiceImpl.class)); }}

KONSUMIEREN VONSERVICES

BINDEN DES INTERFACESpublic class FrontendModule extends AbstractModule implements ServiceClientGuiceSupport { @Override protected void configure() { bindClient(OrganizeService.class); … }}

NUTZUNG DES INTERFACESAls Client: Interface per Injection anfordern

Lagom sorgt für Implementierung für Service-Zugriff

…inclusive „Service“ wie Circuit Breaker, etc.

CQRS IN A NUTSHELL

WAS IST EVENT SOURCINGDie alte Welt:

Es gibt eine Datenbank mit dem aktuellen Zustand, ein Objektmodellmit dem aktuellen Zustand und ein ORM, das die beiden abgleicht

Die neue Welt: Speichere nicht den Zustand der Objekte sondern Events, die

diesen Zustand verändern

WOZU?Es ist nachvollziehbar, durch welche Änderungen der aktuelle

Zustand zustande gekommen ist.

Es können inkrementelle Snapshots der Events erzeugt werden.

Zustandsänderungen sind leicht zwischen Instanzen zu syncen.

Sehr gute Performance-Charakteristik.

WIE IST ES IN LAGOMUMGESETZT?

Zu jeder Entity gibt es einen World-State.

Sendet man ein Command an eine Entity, so löst dieser einen Eventaus, der den World State verändert.

Die Events werden persistent gespeichert.

Der World State kann zu jedem Zeitpunkt rekonstruiert werden.

EINE PERSISTENT ENTITYDEFINIEREN

Entity besteht immer aus Commands, Events und einem World-State

public class Present extends PersistentEntity< PresentCommand, PresentEvent, PresentWorldState> {

@Override public Behavior initialBehavior( final Optional<PresentWorldState> snapshotState) { final BehaviorBuilder builder = newBehaviorBuilder( snapshotState .orElse(new PresentWorldState(null, null, null, null, null))); // Command Handler: … // Event Handler: … // Readonly Commands: … return builder.build(); }}

REAGIEREN AUF READONLY-COMMANDS

Ein Readonly Command ändert den Worldstate nicht

// Readonly Commands: get Present Databuilder.setReadOnlyCommandHandler(GetPresentData.class, (cmd, ctx) -> ctx.reply(state()));

state() ... gibt den aktuellen World-State des Objekts zurück

REAGIEREN AUF ANDERECOMMANDS

Ein Command ändert den Worldstate durch Aufrufen eines Events:

// Command Handler: Neues Geschenk erzeugenbuilder.setCommandHandler(CreatePresent.class, (cmd, ctx) -> { final PresentCreatedEvent event = new PresentCreatedEvent(cmd.id, cmd.organisatorName, cmd.organisatorMail, cmd.deadline, cmd.description); return ctx.thenPersist(event, evt -> ctx.reply(state()));});

Auch hier stünde für ändernde Events der World-State mit state()zur Verfügung.

REAGIEREN AUF EVENTSEin Event ersetzt den World-State durch einen anderen. Der World-

State an sich ist Immutable.

Vorteile: Keine Nebenläufigkeit, nur Zustandsübergänge

builder.setEventHandler( PresentCreatedEvent.class, event -> new PresentWorldState(event.id, event.organisatorName, event.organisatorMail, event.deadline, event.description));

MESSAGE QUEUE / EVENTS

ABGRENZUNGBisher:

Ein Service der Daten braucht, die einem anderen Service gehörenfragt diesen an. User erlebt die Antwort synchron.

Neu:

Ein Service dem Daten gehören stellt diese einem anderen zurdurch einen Message Broker Verfügung.

(Publish / Subscribe Pattern)

ANWENDUNGSBEISPIELContribute-Service: Interessiert an jedem Einzelbeitrag (wer wieviel)

Organize-Service: Interessiert an Gesamtsumme (kommt dasGeschenk zustande?)

→ Entkopplung durch Messages

RÜCKBLENDE: PUB/SUBPublisher senden Nachrichten an Kanäle (Topics). Topics sind

meistens als Warteschlangen definiert

Subscriber holen die Nachrichten aus den Topics ab, verarbeitensie und schicken ggf. neue Nachrichten oder Antworten

Nachrichten werden aus dem Topic gelöscht, wenn…

…eine vorgegebene Zeit die sie bestehen sollen abgelaufen ist…eine eingestellte Anzahl Subscriber sie entfangen hat…ein Subscriber sie bearbeitet und explizit gelöscht hat

■■■

EIGENSCHAFTEN: PUB/SUBlose KopplungSkalierbarkeitSemantik nicht flexibelZustellung der Nachricht kann unter Umständen nicht garantiertwerden

■■■■

DEFINITION IM SERVICEDie Definition eines solchen Service-Listeners erfolgt analog zu der

von Web-Aufrufen:

public interface ContributeService extends Service { (…)

@Override default Descriptor descriptor() { return named("contributeservice").withCalls( (…) ).publishing( topic("contributions", this::contributionsTopic) ).withAutoAcl(true); }

Topic contributionsTopic();}

PUBLISHBeispielsweise auf Events aus dem CQRS hören um zu publishen.

public Topic contributionsTopic() { return TopicProducer.singleStreamWithOffset(offset -> persistentEntityRegistry .eventStream(ContribEvent.EVENT_TAG, offset) .map(this::convertEvent) );}

SUBSCRIBERegistrieren als Subscriber, sodass wenn eine Nachricht eingeht

eine Aktion ausgeführt wird.

contributeService.contributionsTopic() .subscribe() .atLeastOnce(Flow.fromFunction((contributionEvent) -> { // Handle event return Done.getInstance(); }));

UNTERSTÜTZTE BROKERLagom schreibt nicht einen Broker vor, unterstützt werden

Beispielsweise:

Google Cloud Pub/SubKafkaRabbitMQ

■■■

DINGE DIE WIRAUSGELASSEN HABEN...

SERVICE DISCOVERY UNDDEPLOYMENT

Lightbend bietet ConductR an, was sich um scaling und servicediscovery kümmert.

Es gibt ein Plugin für Consul als Service Discovery und der Serviceläuft auch in einem Docker Container.

DIE ANDEREN ARBEITEN ABER MITFRAMEWORK X...

Der Vorteil von Microservices ist, dass jeder Service mit deroptimalen Technologie arbeiten kann

Daher ist es in Lagom vorgesehen, auch andere Serviceskonsumieren zu können

ICH BRAUCHE DIESE LIBRARY GANZDRINGEND...

Libraries generell integrierbar, zB via Dependency Injection.

Vorsicht: Requests sollten nicht blockieren.

ICH HAB ABER SCHON EINE SERVICEDISCOVERY...

Prinzipiell steht nichts im Wege eine andere Art von ServiceDiscovery zu benutzen

UNSER FAZITSehr mächtiges Framework

Deckt mit generischer API quasi alle Aspekte modernerMicroservice-Entwicklung ab

Sehr angenehm: Kurze Setup-Zeit für Entwickler-Maschinen, Code-Reloading

Gefühlt (noch?) etwas schwierig, wenn man vom Standardwegabweichen will (Beispiel: Message Queues)

…aber: Noch jung, da dürfen wir noch mehr erwarten!

BILDQUELLEN

by Microsoft■ Lagom Wortherkunft■ CQRS pattern

Recommended