Hé Habr!
Wij herinneren u eraan dat het volgen van het boek over
Voorlopig leert de gemeenschap alleen maar de grenzen van dit krachtige hulpmiddel kennen. Er is dus onlangs een artikel gepubliceerd waarvan we de vertaling graag aan u willen voorstellen. Uit eigen ervaring vertelt de auteur hoe je van Kafka Streams een gedistribueerde gegevensopslag kunt maken. Veel plezier met lezen!
Apache-bibliotheek
In dit artikel zal ik u vertellen hoe ons bedrijf deze mogelijkheid op winstgevende wijze heeft weten te benutten bij de ontwikkeling van een product voor de beveiliging van cloudapplicaties. Met behulp van Kafka Streams hebben we gedeelde statusmicroservices gecreëerd, die elk dienen als een fouttolerante en zeer beschikbare bron van betrouwbare informatie over de status van objecten in het systeem. Voor ons is dit een stap voorwaarts, zowel qua betrouwbaarheid als qua ondersteuningsgemak.
Als u geïnteresseerd bent in een alternatieve aanpak waarmee u één centrale database kunt gebruiken om de formele status van uw objecten te ondersteunen, lees deze dan, het zal interessant zijn...
Waarom we dachten dat het tijd was om de manier waarop we met gedeelde staten werken te veranderen
We moesten de status van verschillende objecten bijhouden op basis van agentrapporten (bijvoorbeeld: werd de site aangevallen)? Voordat we naar Kafka Streams migreerden, vertrouwden we voor statusbeheer vaak op één centrale database (+ service-API). Deze aanpak heeft zijn nadelen:
Figuur 1: Een typisch split-state-scenario van vóór de transitie naar
Kafka- en Kafka-streams: agenten communiceren hun standpunten via API, de bijgewerkte status wordt berekend via een centrale database
Maak kennis met Kafka Streams, waarmee u eenvoudig gedeelde staatsmicroservices kunt creëren
Ongeveer een jaar geleden besloten we om onze gedeelde staatsscenario's onder de loep te nemen om deze problemen aan te pakken. We besloten onmiddellijk om Kafka Streams te proberen - we weten hoe schaalbaar, zeer beschikbaar en fouttolerant het is, welke rijke streamingfunctionaliteit het heeft (transformaties, inclusief stateful). Precies wat we nodig hadden, om nog maar te zwijgen van hoe volwassen en betrouwbaar het berichtensysteem in Kafka is geworden.
Elk van de stateful microservices die we hebben gemaakt, is gebouwd bovenop een Kafka Streams-instantie met een vrij eenvoudige topologie. Het bestond uit 1) een bron 2) een processor met een persistente sleutelwaardeopslag 3) een sink:
Figuur 2: De standaardtopologie van onze streaminginstanties voor stateful microservices. Merk op dat er hier ook een repository is die planningsmetagegevens bevat.
Bij deze nieuwe aanpak stellen agenten berichten op die in het brononderwerp worden ingevoerd, en ontvangen consumenten (bijvoorbeeld een e-mailmeldingsservice) de berekende gedeelde status via de sink (uitvoeronderwerp).
Figuur 3: Nieuw voorbeeld van een taakstroom voor een scenario met gedeelde microservices: 1) de agent genereert een bericht dat arriveert bij het Kafka-brononderwerp; 2) een microservice met gedeelde status (met behulp van Kafka Streams) verwerkt deze en schrijft de berekende status naar het uiteindelijke Kafka-onderwerp; waarna 3) consumenten de nieuwe staat accepteren
Hé, deze ingebouwde sleutelwaardewinkel is eigenlijk heel handig!
Zoals hierboven vermeld, bevat onze gedeelde statustopologie een sleutelwaardeopslag. We hebben verschillende opties gevonden om het te gebruiken, en twee daarvan worden hieronder beschreven.
Optie 1: gebruik een sleutelwaardearchief voor berekeningen
Onze eerste sleutelwaardeopslag bevatte de aanvullende gegevens die we nodig hadden voor berekeningen. In sommige gevallen werd de gedeelde staat bijvoorbeeld bepaald door het principe van "meerderheidsstemmen". De repository zou alle laatste agentrapporten over de status van een object kunnen bevatten. Als we vervolgens een nieuw rapport van de ene of andere agent ontvingen, konden we het opslaan, rapporten van alle andere agenten over de status van hetzelfde object uit de opslag halen en de berekening herhalen.
Figuur 4 hieronder laat zien hoe we het sleutel/waarde-archief hebben blootgesteld aan de verwerkingsmethode van de processor, zodat het nieuwe bericht vervolgens kon worden verwerkt.
Illustratie 4: We openen de toegang tot het sleutelwaardearchief voor de verwerkingsmethode van de processor (hierna moet elk script dat met gedeelde status werkt de methode implementeren doProcess
)
Optie #2: Een CRUD API bovenop Kafka Streams maken
Nadat we onze basistaakstroom hadden vastgesteld, begonnen we te proberen een RESTful CRUD API te schrijven voor onze gedeelde staatsmicroservices. We wilden de status van sommige of alle objecten kunnen ophalen, en de status van een object kunnen instellen of verwijderen (handig voor backend-ondersteuning).
Om alle Get State API's te ondersteunen, hebben we, telkens wanneer we de status tijdens de verwerking opnieuw moesten berekenen, deze lange tijd opgeslagen in een ingebouwde sleutel/waarde-opslag. In dit geval wordt het vrij eenvoudig om een dergelijke API te implementeren met behulp van een enkele instantie van Kafka Streams, zoals weergegeven in de onderstaande lijst:
Afbeelding 5: Het ingebouwde sleutelwaardearchief gebruiken om de vooraf berekende status van een object te verkrijgen
Ook het updaten van de status van een object via de API is eenvoudig te implementeren. Kortom, het enige dat u hoeft te doen, is een Kafka-producent maken en deze gebruiken om een plaat te maken die de nieuwe staat bevat. Dit zorgt ervoor dat alle berichten die via de API worden gegenereerd, op dezelfde manier worden verwerkt als de berichten die van andere producenten (bijvoorbeeld agenten) worden ontvangen.
Figuur 6: U kunt de status van een object instellen met behulp van de Kafka-producent
Kleine complicatie: Kafka heeft veel partities
Vervolgens wilden we de verwerkingslast verdelen en de beschikbaarheid verbeteren door per scenario een cluster van gedeelde microservices aan te bieden. De installatie was een fluitje van een cent: zodra we alle instances hadden geconfigureerd om onder dezelfde applicatie-ID (en dezelfde bootstrap-servers) te draaien, werd bijna al het andere automatisch gedaan. We hebben ook gespecificeerd dat elk brononderwerp uit verschillende partities zou bestaan, zodat aan elke instantie een subset van dergelijke partities kon worden toegewezen.
Ik zal ook vermelden dat het gebruikelijk is om een reservekopie van de staatsopslag te maken, zodat u deze kopie bijvoorbeeld, in geval van herstel na een fout, naar een andere instantie kunt overbrengen. Voor elke statusopslag in Kafka Streams wordt een gerepliceerd onderwerp gemaakt met een wijzigingslogboek (waarin lokale updates worden bijgehouden). Kafka ondersteunt dus voortdurend de staatswinkel. Daarom kan, in het geval van een storing van een of andere Kafka Streams-instantie, de statusopslag snel worden hersteld op een andere instantie, waar de overeenkomstige partities naartoe zullen gaan. Uit onze tests is gebleken dat dit binnen enkele seconden gebeurt, zelfs als er miljoenen records in de winkel staan.
Door van een enkele microservice met gedeelde status naar een cluster van microservices te gaan, wordt het minder triviaal om de Get State API te implementeren. In de nieuwe situatie bevat het statusarchief van elke microservice slechts een deel van het totaalbeeld (de objecten waarvan de sleutels zijn toegewezen aan een specifieke partitie). We moesten bepalen welke instantie de status bevatte van het object dat we nodig hadden, en we deden dit op basis van de thread-metagegevens, zoals hieronder weergegeven:
Figuur 7: Met behulp van stream-metagegevens bepalen we vanaf welke instantie de status van het gewenste object moet worden opgevraagd; een soortgelijke aanpak werd gebruikt met de GET ALL API
Belangrijkste bevindingen
Staatswinkels in Kafka Streams kunnen dienen als een de facto gedistribueerde database,
- voortdurend herhaald in Kafka
- Een CRUD API kan eenvoudig bovenop een dergelijk systeem worden gebouwd
- Het omgaan met meerdere partities is iets ingewikkelder
- Het is ook mogelijk om een of meer statusopslagplaatsen aan de streamingtopologie toe te voegen om hulpgegevens op te slaan. Deze optie kan worden gebruikt voor:
- Langetermijnopslag van gegevens die nodig zijn voor berekeningen tijdens streamverwerking
- Langetermijnopslag van gegevens die nuttig kunnen zijn bij de volgende keer dat het streaming-exemplaar wordt ingericht
- veel meer...
Deze en andere voordelen maken Kafka Streams zeer geschikt voor het handhaven van de mondiale toestand in een gedistribueerd systeem als het onze. Kafka Streams heeft bewezen zeer betrouwbaar te zijn in de productie (we hebben vrijwel geen berichtverlies gehad sinds de implementatie ervan), en we zijn ervan overtuigd dat de mogelijkheden daar niet zullen stoppen!
Bron: www.habr.com