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
Top Related