Inte bara bearbetning: Hur vi skapade en distribuerad databas från Kafka Streams, och vad som kom ut av det

Hej Habr!

Vi påminner om att efter boken om kafka vi har publicerat ett lika intressant verk om biblioteket Kafka Streams API.

Inte bara bearbetning: Hur vi skapade en distribuerad databas från Kafka Streams, och vad som kom ut av det

För närvarande lär samhället bara gränserna för detta kraftfulla verktyg. Så, en artikel publicerades nyligen, vars översättning vi skulle vilja presentera dig för. Av egen erfarenhet berättar författaren hur man förvandlar Kafka Streams till en distribuerad datalagring. Njut av att läsa!

Apache bibliotek Kafka strömmar används över hela världen i företag för distribuerad strömbehandling ovanpå Apache Kafka. En av de underskattade aspekterna av detta ramverk är att det låter dig lagra lokalt producerat tillstånd baserat på trådbearbetning.

I den här artikeln kommer jag att berätta hur vårt företag lyckades utnyttja denna möjlighet med lönsamhet när vi utvecklade en produkt för molnapplikationssäkerhet. Med Kafka Streams skapade vi delade tillståndsmikrotjänster, som var och en fungerar som en feltolerant och högt tillgänglig källa för tillförlitlig information om tillståndet för objekt i systemet. För oss är detta ett steg framåt både vad gäller tillförlitlighet och enkel support.

Om du är intresserad av ett alternativt tillvägagångssätt som låter dig använda en enda central databas för att stödja det formella tillståndet för dina objekt, läs den, det kommer att bli intressant...

Varför vi tyckte att det var dags att ändra vårt sätt att arbeta med delad tillstånd

Vi behövde upprätthålla tillståndet för olika objekt baserat på agentrapporter (till exempel: var webbplatsen under attack)? Innan vi migrerade till Kafka Streams förlitade vi oss ofta på en enda central databas (+ tjänst API) för tillståndshantering. Detta tillvägagångssätt har sina nackdelar: datumintensiva situationer att upprätthålla konsekvens och synkronisering blir en verklig utmaning. Databasen kan bli en flaskhals eller hamna i loppets skick och lider av oförutsägbarhet.

Inte bara bearbetning: Hur vi skapade en distribuerad databas från Kafka Streams, och vad som kom ut av det

Figur 1: Ett typiskt delat tillståndsscenario sett före övergången till
Kafka och Kafka Streams: agenter kommunicerar sina åsikter via API, uppdaterat tillstånd beräknas genom en central databas

Möt Kafka Streams, vilket gör det enkelt att skapa delade statliga mikrotjänster

För ungefär ett år sedan bestämde vi oss för att ta en ordentlig titt på våra delade tillståndsscenarier för att ta itu med dessa problem. Vi bestämde oss omedelbart för att prova Kafka Streams – vi vet hur skalbar, högtillgänglig och feltolerant den är, vilken rik streamingfunktionalitet den har (transformationer, inklusive stateful sådana). Precis vad vi behövde, för att inte tala om hur moget och pålitligt meddelandesystemet har blivit i Kafka.

Var och en av de statliga mikrotjänsterna vi skapade byggdes ovanpå en Kafka Streams-instans med en ganska enkel topologi. Den bestod av 1) en källa 2) en processor med ett beständigt nyckel-värdelager 3) en disk:

Inte bara bearbetning: Hur vi skapade en distribuerad databas från Kafka Streams, och vad som kom ut av det

Figur 2: Standardtopologin för våra streaminginstanser för stateful mikrotjänster. Observera att det också finns ett arkiv här som innehåller planeringsmetadata.

I detta nya tillvägagångssätt komponerar agenter meddelanden som matas in i källämnet, och konsumenter – t.ex. en e-postaviseringstjänst – får det beräknade delade tillståndet genom diskbänken (utdataämnet).

Inte bara bearbetning: Hur vi skapade en distribuerad databas från Kafka Streams, och vad som kom ut av det

Figur 3: Nytt exempel på uppgiftsflöde för ett scenario med delade mikrotjänster: 1) agenten genererar ett meddelande som kommer till Kafkas källämne; 2) en mikrotjänst med delat tillstånd (med Kafka Streams) bearbetar det och skriver det beräknade tillståndet till det slutliga Kafka-ämnet; varefter 3) konsumenter accepterar den nya staten

Hej, denna inbyggda nyckel-värde butik är faktiskt väldigt användbar!

Som nämnts ovan innehåller vår delade tillståndstopologi ett nyckel-värdelager. Vi hittade flera alternativ för att använda det, och två av dem beskrivs nedan.

Alternativ #1: Använd ett nyckel-värdelager för beräkningar

Vårt första nyckel-värdelager innehöll de extra data vi behövde för beräkningar. Till exempel bestämdes i vissa fall den delade staten av principen om "majoritetsröster". Lagret kan innehålla alla de senaste agentrapporterna om status för något objekt. Sedan, när vi fick en ny rapport från en eller annan agent, kunde vi spara den, hämta rapporter från alla andra agenter om tillståndet för samma objekt från lagringen och upprepa beräkningen.
Figur 4 nedan visar hur vi exponerade nyckel/värdelagret för processorns bearbetningsmetod så att det nya meddelandet sedan kunde bearbetas.

Inte bara bearbetning: Hur vi skapade en distribuerad databas från Kafka Streams, och vad som kom ut av det

Illustration 4: Vi öppnar åtkomst till nyckel-värdelagret för processorns bearbetningsmetod (efter detta måste varje skript som fungerar med delat tillstånd implementera metoden doProcess)

Alternativ #2: Skapa ett CRUD API ovanpå Kafka Streams

Efter att ha etablerat vårt grundläggande uppgiftsflöde började vi försöka skriva ett RESTful CRUD API för våra delade tillståndsmikrotjänster. Vi ville kunna hämta tillståndet för vissa eller alla objekt, samt ställa in eller ta bort ett objekts tillstånd (användbart för backend-stöd).

För att stödja alla Get State APIs, närhelst vi behövde räkna om tillståndet under bearbetning, lagrade vi det i ett inbyggt nyckel-värdelager under lång tid. I det här fallet blir det ganska enkelt att implementera ett sådant API med en enda instans av Kafka Streams, som visas i listan nedan:

Inte bara bearbetning: Hur vi skapade en distribuerad databas från Kafka Streams, och vad som kom ut av det

Figur 5: Använda det inbyggda nyckel-värdelagret för att erhålla det förberäknade tillståndet för ett objekt

Att uppdatera ett objekts tillstånd via API:et är också enkelt att implementera. I grund och botten är allt du behöver göra att skapa en Kafka-producent och använda den för att göra en skiva som innehåller det nya tillståndet. Detta säkerställer att alla meddelanden som genereras via API:et kommer att behandlas på samma sätt som de som tas emot från andra producenter (t.ex. agenter).

Inte bara bearbetning: Hur vi skapade en distribuerad databas från Kafka Streams, och vad som kom ut av det

Figur 6: Du kan ställa in tillståndet för ett objekt med Kafka-producenten

Liten komplikation: Kafka har många partitioner

Därefter ville vi fördela bearbetningsbelastningen och förbättra tillgängligheten genom att tillhandahålla ett kluster av mikrotjänster med delad tillstånd per scenario. Installationen var en bris: när vi väl konfigurerade alla instanser att köras under samma applikations-ID (och samma bootstrap-servrar) gjordes nästan allt annat automatiskt. Vi specificerade också att varje källämne skulle bestå av flera partitioner, så att varje instans kunde tilldelas en delmängd av sådana partitioner.

Jag ska också nämna att det är vanligt att göra en säkerhetskopia av den statliga butiken så att, till exempel, i händelse av återställning efter ett fel, överför denna kopia till en annan instans. För varje statlig butik i Kafka Streams skapas ett replikerat ämne med en ändringslogg (som spårar lokala uppdateringar). Därmed backar Kafka hela tiden upp den statliga butiken. Därför, i händelse av ett fel på en eller annan Kafka Streams-instans, kan tillståndsarkivet snabbt återställas på en annan instans, dit motsvarande partitioner kommer att gå. Våra tester har visat att detta görs på några sekunder, även om det finns miljontals skivor i butiken.

När man går från en enskild mikrotjänst med delat tillstånd till ett kluster av mikrotjänster blir det mindre trivialt att implementera Get State API. I den nya situationen innehåller tillståndsarkivet för varje mikrotjänst endast en del av den övergripande bilden (de objekt vars nycklar mappades till en specifik partition). Vi var tvungna att bestämma vilken instans som innehöll tillståndet för objektet vi behövde, och vi gjorde detta baserat på trådens metadata, som visas nedan:

Inte bara bearbetning: Hur vi skapade en distribuerad databas från Kafka Streams, och vad som kom ut av det

Figur 7: Med hjälp av strömmetadata bestämmer vi från vilken instans vi ska fråga tillståndet för det önskade objektet; ett liknande tillvägagångssätt användes med GET ALL API

Huvudresultat

Statliga butiker i Kafka Streams kan fungera som en de facto distribuerad databas,

  • ständigt replikeras i Kafka
  • Ett CRUD API kan enkelt byggas ovanpå ett sådant system
  • Att hantera flera partitioner är lite mer komplicerat
  • Det är också möjligt att lägga till en eller flera tillståndslagringar till streamingtopologin för att lagra hjälpdata. Det här alternativet kan användas för:
  • Långtidslagring av data som behövs för beräkningar under strömbehandling
  • Långtidslagring av data som kan vara användbar nästa gång streaminginstansen tillhandahålls
  • mycket mer...

Dessa och andra fördelar gör Kafka Streams väl lämpade för att upprätthålla globala tillstånd i ett distribuerat system som vårt. Kafka Streams har visat sig vara mycket tillförlitlig i produktionen (vi har praktiskt taget inga meddelandeförluster sedan vi distribuerade det), och vi är övertygade om att dess kapacitet inte kommer att sluta där!

Källa: will.com

Lägg en kommentar