Niet alleen verwerking: hoe we een gedistribueerde database van Kafka Streams hebben gemaakt, en wat daaruit voortkwam

Hé Habr!

Wij herinneren u eraan dat het volgen van het boek over Kafka we hebben een even interessant werk over de bibliotheek gepubliceerd Kafka Streams-API.

Niet alleen verwerking: hoe we een gedistribueerde database van Kafka Streams hebben gemaakt, en wat daaruit voortkwam

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 Kafka-stromen wereldwijd gebruikt in ondernemingen voor gedistribueerde stroomverwerking bovenop Apache Kafka. Een van de ondergewaardeerde aspecten van dit raamwerk is dat je hiermee de lokale staat kunt opslaan die is geproduceerd op basis van threadverwerking.

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: date-intensieve situaties het behouden van consistentie en synchronisatie wordt een echte uitdaging. De database kan een knelpunt worden of daarin terechtkomen race conditie en last hebben van onvoorspelbaarheid.

Niet alleen verwerking: hoe we een gedistribueerde database van Kafka Streams hebben gemaakt, en wat daaruit voortkwam

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:

Niet alleen verwerking: hoe we een gedistribueerde database van Kafka Streams hebben gemaakt, en wat daaruit voortkwam

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).

Niet alleen verwerking: hoe we een gedistribueerde database van Kafka Streams hebben gemaakt, en wat daaruit voortkwam

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.

Niet alleen verwerking: hoe we een gedistribueerde database van Kafka Streams hebben gemaakt, en wat daaruit voortkwam

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:

Niet alleen verwerking: hoe we een gedistribueerde database van Kafka Streams hebben gemaakt, en wat daaruit voortkwam

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.

Niet alleen verwerking: hoe we een gedistribueerde database van Kafka Streams hebben gemaakt, en wat daaruit voortkwam

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:

Niet alleen verwerking: hoe we een gedistribueerde database van Kafka Streams hebben gemaakt, en wat daaruit voortkwam

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

Voeg een reactie