Hej, Habr-nÀtverksmedlemmar! Den hÀr boken passar alla utvecklare som vill förstÄ strömmande bearbetning. Att förstÄ distribuerad programmering kommer att hjÀlpa till att lÀra sig mer om Kafka och Kafka Streams. Det skulle vara bra att kÀnna till sjÀlva Kafka-ramverket, men det Àr inte nödvÀndigt: jag kommer att berÀtta allt du behöver veta. BÄde erfarna Kafka-utvecklare och nybörjare kommer med hjÀlp av den hÀr boken att kunna skapa intressanta applikationer för strömmande bearbetning med Kafka Streams-biblioteket. Java-utvecklare pÄ medelnivÄ och högre, som redan Àr bekanta med begrepp som serialisering, kommer att lÀra sig att anvÀnda sina fÀrdigheter för att skapa Kafka Streams-applikationer. KÀllkoden till boken Àr skriven i Java 8 och anvÀnder i stor utstrÀckning syntaxen för lambda-uttryck i Java 8, sÄ att kunna arbeta med lambda-funktioner (Àven om det Àr pÄ ett annat programmeringssprÄk) kommer att vara till nytta.
Utdrag. 5.3. Agregering och fönsteroperationer
I den hÀr sektionen gÄr vi vidare med att utforska de mest lovande delarna av Kafka Streams. Hittills har vi undersökt följande aspekter av Kafka Streams:
- skapa en behandlings-topologi;
- anvÀnda tillstÄnd i strömmande applikationer;
- utföra datastream-anslutningar;
- skillnaderna mellan hÀndelseströmmar (KStream) och uppdateringsströmmar (KTable).
I de följande exemplen kommer vi att sĂ€tta ihop alla dessa element. Dessutom kommer du att bekanta dig med fönsteroperationer â en annan fantastisk möjlighet för strömmande applikationer. VĂ„rt första exempel kommer att handla om enkel aggregering.
5.3.1. Aggerering av aktieförsÀljning efter branscher
Aggreggering och gruppering Àr livsnödvÀndiga verktyg nÀr man arbetar med strömmade data. Att undersöka individuella poster i takt med att de kommer in visar sig ofta vara otillrÀckligt. För att extrahera mer information frÄn data behöver de grupperas och kombineras.
I det hÀr exemplet kommer du att fÄ prova en inrikesdagshandlares kostym, som behöver följa aktieförsÀljning för företag i flera branscher. Specifikt Àr du intresserad av fem företag med de högsta aktieförsÀljningarna inom varje bransch.
För att göra denna typ av aggregering kommer flera steg att behöva tas för att översÀtta data till önskat format (om man talar i allmÀnna termer).
- Skapa en kÀlla baserad pÄ Àmnet som publicerar rÄ information om aktiehandel. Vi mÄste avbilda ett objekt av typen StockTransaction till ett objekt av typen ShareVolume. Det handlar om att objektet StockTransaction innehÄller metadata om försÀljning, och vi behöver endast data om antalet sÄlda aktier.
- Gruppera data i ShareVolume efter aktiesymboler. Efter gruppering efter symboler kan dessa data reduceras till mellanliggande summor av aktieförsÀljningsvolymer. Det Àr vÀrt att notera att metoden KStream.groupBy returnerar en instans av typen KGroupedStream. En instans av KTable kan erhÄllas genom att anropa metoden KGroupedStream.reduce.
Vad Àr grÀnssnittet KGroupedStream?
Metoderna KStream.groupBy och KStream.groupByKey returnerar en instans av KGroupedStream. KGroupedStream Àr en mellanliggande representation av en ström av hÀndelser efter gruppering av nycklar. Den Àr inte avsedd för direkt arbete. IstÀllet anvÀnds KGroupedStream för aggregationsoperationer vars resultat alltid Àr en KTable. Eftersom resultatet av aggregationsoperationer Àr en KTable och dessa anvÀnder ett tillstÄndslager, kan det hÀnda att inte alla uppdateringar i resultatet skickas vidare i kedjan.
Metoden KTable.groupBy returnerar en motsvarande KGroupedTable â en mellanliggande representation av en ström av uppdateringar omgrupperade efter nyckel.
LÄt oss ta en kort paus och titta pÄ fig. 5.9, som visar vad vi har Ästadkommit. Denna topologi bör redan vara bekant för dig.

LÄt oss nu titta pÄ koden för denna topologi (den finns i filen src/main/java/bbejeck/chapter_5/AggregationsAndReducingExample.java) (listning 5.2).

Koden som presenteras Àr kompakt och utför en stor mÀngd ÄtgÀrder pÄ nÄgra fÄ rader. I den första parametern av metoden builder.stream kan du se nÄgot nytt: vÀrdet av den upprÀknade typen AutoOffsetReset.EARLIEST (det finns ocksÄ LATEST), som stÀlls in med hjÀlp av metoden Consumed.withOffsetResetPolicy. Med denna upprÀknade typ kan du ange en strategi för offsetÄterstÀllning för varje KStream eller KTable, vilket har högre prioritet Àn parametern för offsetÄterstÀllning i konfigurationen.
GroupByKey och GroupBy
I grÀnssnittet KStream finns det tvÄ metoder för att gruppera poster: GroupByKey och GroupBy. BÄda returnerar en KGroupedTable, sÄ du kanske undrar: vad Àr skillnaden mellan dem och nÀr ska man anvÀnda vilken?
Metoden GroupByKey anvÀnds nÀr nycklarna i KStream redan Àr icke-tomma. Viktigt Àr att flaggan "krÀver ompartitionering" aldrig har satt.
Metoden GroupBy föreslÄr att du har Àndrat nycklarna för grupperingen, sÄ flaggan för ompartitionering Àr satt till true. Genomförande efter metoden GroupBy avkopplingar, aggregering och liknande kommer att leda till automatisk ompartitionering.
Sammanfattning: anvÀnd alltid GroupByKey vid minsta möjlighet, snarare Àn GroupBy.
Vad metoderna mapValues och groupBy gör Àr sjÀlvklart, sÄ lÄt oss titta pÄ metoden sum() (den kan hittas i filen src/main/java/bbejeck/model/ShareVolume.java) (listing 5.3).

Metoden ShareVolume.sum returnerar det mellanliggande summan av aktieförsÀljningsvolymen, och resultatet av hela berÀkningskedjan representerar ett objekt KTable. Nu förstÄr du vilken roll KTable spelar. NÀr objekt ShareVolume kommer in, sparas den senaste aktuella uppdateringen i den motsvarande KTable-objektet. Det Àr viktigt att komma ihÄg att alla uppdateringar Äterspeglas i föregÄende shareVolumeKTable, men inte alla skickas vidare.
Vidare anvÀnder vi denna KTable för att utföra aggregering (baserat pÄ antalet sÄlda aktier) för att fÄ fram fem företag med störst försÀljningsvolymer av aktier inom varje bransch. VÄra ÄtgÀrder kommer att vara liknande dem som vid den första aggregeringen.
- Utför ytterligare en groupBy-operation för att gruppera individuella ShareVolume-objekt efter branscher.
- Börja summera ShareVolume-objekten. Denna gÄng representerar aggregationsobjektet en prioritetskö med fast storlek. I en sÄdan kö med fast storlek lagras endast fem företag med de största mÀngderna sÄlda aktier.
- Konvertera köerna frÄn föregÄende punkt till ett strÀngvÀrde och returnera de fem mest sÄlda aktierna per bransch.
- Skriv resultaten i strÀngformat till Àmnet.
Figur 5.10 visar topologin för datarörelsen. Som du kan se, Àr den andra bearbetningscykeln ganska enkel.

Nu, efter att tydligt ha förstÄtt strukturen av denna andra bearbetningscykel, kan du gÄ till dess kÀllkod (du hittar den i filen src/main/java/bbejeck/chapter_5/AggregationsAndReducingExample.java) (listing 5.4).
I den hÀr initialiseringen finns en variabel fixedQueue. Det Àr ett anpassat objekt - en adapter för java.util.TreeSet, som anvÀnds för att spÄra de N största resultaten i fallande ordning av sÄlda aktier.

Du har redan stött pÄ anropen groupBy och mapValues, sÄ vi kommer inte att stanna vid dem (vi anropar metoden KTable.toStream, eftersom metoden KTable.print anses vara förÄldrad). Men du har Ànnu inte sett KTable-versionen av metoden aggregate(), sÄ vi kommer att ta lite tid för att diskutera den.
Som du kommer ihÄg, kÀnnetecknas KTable av att poster med samma nycklar betraktas som uppdateringar. KTable ersÀtter den gamla posten med den nya. Aggering sker pÄ liknande sÀtt: senaste poster med samma nyckel aggeras. NÀr en post kommer in lÀggs den till en instans av klassen FixedSizePriorityQueue med hjÀlp av en summerare (den andra parametern i anropet till metoden aggregate), men om det redan finns en annan post med samma nyckel, tas den gamla posten bort med hjÀlp av en subtraherar (den tredje parametern i anropet till metoden aggregate).
Detta betyder att vÄr aggregator, FixedSizePriorityQueue, inte aggerar alla vÀrden med samma nyckel, utan lagrar det glidande medelvÀrdet av de N mest sÄlda aktietyperna. I varje inkommande post finns det totala antalet aktier som sÄlts hittills. KTable ger dig information om vilka företags aktier som sÀljer mest just nu; glidande aggering av varje uppdatering behövs inte.
Vi har lÀrt oss att göra tvÄ viktiga saker:
- gruppera vÀrden i KTable efter en gemensam nyckel;
- utföra nyttiga operationer pÄ dessa grupperade vÀrden, sÄsom reduktion och aggering.
FörmÄgan att utföra dessa operationer Àr viktig för att förstÄ betydelsen av de data som rör sig genom Kafka Streams-applikationen och att ta reda pÄ vilken information de bÀr.
Vi har ocksĂ„ sammanfört nĂ„gra av de centrala begreppen som diskuterats tidigare i denna bok. I kapitel 4 berĂ€ttade vi hur viktigt det Ă€r för en streamingapplikation att ha ett motstĂ„ndskraftigt, lokalt tillstĂ„nd. Det första exemplet frĂ„n detta kapitel visade varför lokalt tillstĂ„nd Ă€r sĂ„ viktigt â det möjliggör spĂ„rning av vilken information du redan har sett. Lokalt tilltrĂ€de eliminerar nĂ€tverksfördröjningar, vilket gör att applikationen blir mer presterande och felresistent.
Vid utförande av nÄgon form av aggregerings- eller sammanfogning mÄste namnet pÄ tillstÄndslagringen anges. Aggregerings- och sammanfogningsoperationer returnerar en instans av KTable, och KTable anvÀnder tillstÄndslagringen för att ersÀtta gamla resultat med nya. Som du har sett skickas lÄngt ifrÄn alla uppdateringar vidare i kedjan, och detta Àr viktigt eftersom aggregeringsoperationer Àr avsedda för att skaffa slutgiltig information. Om du inte anvÀnder lokalt tillstÄnd kommer KTable att skicka vidare alla aggregerings- och sammanfogningsresultat.
NĂ€st ska vi titta pĂ„ utförandet av operationer som aggregering inom angiven tidsram â de sĂ„ kallade fönsteroperationerna (windowing operations).
5.3.2. Fönsteroperationer
I föregÄende avsnitt bekantade vi oss med "glidande" sammanfogning och aggregering. Applikationen genomförde en kontinuerlig sammanfogning av aktieförsÀljningsvolymen följt av aggregering av de fem mest sÄlda aktierna pÄ börsen.
Ibland Àr sÄdana kontinuerliga aggregeringar och sammanfogningar av resultat nödvÀndiga. Och ibland mÄste operationer utföras endast över en angiven tidsperiod. Till exempel berÀkna hur mÄnga aktieaffÀrer som gjordes med en specifik företagets aktie under de senaste 10 minuterna. Eller hur mÄnga anvÀndare som klickade pÄ den nya reklam bannern under de senaste 15 minuterna. Applikationen kan genomföra sÄdana operationer flera gÄnger, men med resultat som endast gÀller för angivna tidsperioder (tidsfönster).
RÀkna aktietransaktioner per köpare
I nĂ€sta exempel kommer vi att spĂ„ra aktietransaktioner för flera handlare â antingen stora organisationer eller smarta finansierare som agerar ensamma.
Det finns tvÄ möjliga skÀl för sÄdan spÄrning. Det ena Àr behovet av att veta vad marknadsledare köper/sÀljer. Om dessa stora aktörer och erfarna investerare ser möjligheter för sig sjÀlva, Àr det vÀrt att följa deras strategi. Den andra anledningen Àr att upptÀcka tecken pÄ olagliga affÀrer med anvÀndning av insiderinformation. För detta behöver du analysera korrelationen mellan stora försÀljningsökningar och viktiga pressmeddelanden.
Denna spÄrning bestÄr av följande steg:
- skapa en ström för att lÀsa frÄn Àmnet stock-transactions;
- gruppera inkommande poster efter köparens identifierare och aktiens börssymbol. Anrop av metoden groupBy returnerar en instans av klassen KGroupedStream;
- ÄtervÀnda med metoden KGroupedStream.windowedBy en datastream begrÀnsad av ett tidsfönster, vilket gör det möjligt att utföra fönsteraggregat. Beroende pÄ fönstertypen returneras antingen TimeWindowedKStream eller SessionWindowedKStream;
- rÀkna transaktioner för aggregationsoperationen. Det fönsterdataflöde som definieras avgör om en specifik post rÀknas med;
- skriva resultaten till Àmnet eller skriva ut dem till konsolen under utvecklingen.
Topologin för denna applikation Àr enkel, men en visuell representation kan vara till hjÀlp. LÄt oss titta pÄ fig. 5.11.
DÀrefter kommer vi att titta pÄ funktionerna hos fönsteroperationer och motsvarande kod.

Typer av fönster
I Kafka Streams finns det tre typer av fönster:
- sessionsfönster;
- tumbling fönster;
- glidande/pÄhoppande fönster.
Vilket du vĂ€ljer beror pĂ„ affĂ€rskrav. Tumbling och hoppande fönster begrĂ€nsas av tid, medan sessionsfönstrens begrĂ€nsningar Ă€r kopplade till anvĂ€ndarnas handlingar â lĂ€ngden pĂ„ sessionen(-erna) bestĂ€ms enbart av hur aktivt anvĂ€ndaren beter sig. Huvudsaken Ă€r att komma ihĂ„g att alla typer av fönster baseras pĂ„ tidsstĂ€mplar för posterna, och inte pĂ„ systemtiden.
DÀrefter implementerar vi vÄr topologi med varje typ av fönster. FullstÀndig kod kommer att ges endast i det första exemplet, för de andra typerna av fönster kommer inget att förÀndras förutom typen av fönsteroperation.
Sessionsfönster
Sessionsfönster skiljer sig starkt frÄn alla andra typer av fönster. De begrÀnsas inte sÄ mycket av tid som av anvÀndarens aktivitet (eller aktiviteten hos den enhet som du vill spÄra). Sessionsfönster avgrÀnsas av perioder av inaktivitet.
Figur 5.12 illustrerar begreppet sessionsfönster. En mindre session kommer att glida samman med sessionen till vÀnster om den. Och sessionen till höger kommer att vara separat, eftersom den följer efter en lÄng period av inaktivitet. Sessionsfönster baseras pÄ anvÀndaraktiviteter, men anvÀnder tidsstÀmpel-taggar frÄn poster för att avgöra vilken session posten tillhör.

AnvÀndning av sessionsfönster för att spÄra börstransaktioner.
Vi anvÀnder sessionsfönster för att fÄnga information om börstransaktioner. Implementeringen av sessionsfönster visas i listing 5.5 (som kan hittas i filen src/main/java/bbejeck/chapter_5/CountingWindowingAndKTableJoinExample.java).

De flesta operationerna i denna topologi har du redan stött pÄ, sÄ det finns ingen anledning att gÄ igenom dem igen hÀr. Men hÀr finns ocksÄ nÄgra nya element som vi nu kommer att diskutera.
Vid varje operation groupBy utförs vanligtvis nÄgon form av aggregering (aggregering, sammanstÀllning eller rÀkning). Antingen kan du utföra en ackumulerande aggregering med löpande totalsummor, eller ett fönsteraggregat dÀr poster beaktas inom ett givet tidsfönster.
Koden frÄn listing 5.5 rÀknar antalet transaktioner inom sessionsfönster. I figur 5.13 analyseras dessa ÄtgÀrder steg för steg.
Med anropet windowedBy(SessionWindows.with(twentySeconds).until(fifteenMinutes)) skapade vi ett sessionsfönster med en inaktivitetsperiod pÄ 20 sekunder och en bevarandeperiod pÄ 15 minuter. Inaktivitetsperioden pÄ 20 sekunder innebÀr att applikationen kommer att inkludera varje post som kommer in inom 20 sekunder efter slutet eller början av den aktuella sessionen i den nuvarande (aktiva) sessionen.

HĂ€r anger vi vilken aggregationsoperation som ska utföras i sessionsfönstret â i detta fall count. Om den inkommande posten gĂ„r utanför inaktivitetens intervall (pĂ„ valfri sida av tidsstĂ€mpeln), skapar applikationen en ny session. Bevarandeintervallet innebĂ€r att sessionen hĂ„lls aktiv under en viss tid och tillĂ„ter fördröjda data som gĂ„r utanför inaktivitetsperioden för sessionen men som fortfarande kan kopplas. Dessutom motsvarar början och slutet pĂ„ den nya sessionen, som skapats genom sammanslagning, den tidigaste och den senaste tidsstĂ€mpeln.
LÄt oss titta pÄ nÄgra poster frÄn count-metoden för att se hur sessioner fungerar (tabell 5.1).

NĂ€r poster kommer in, letar vi efter redan existerande sessioner med samma nyckel, dĂ€r slutetiden Ă€r tidigare Ă€n den aktuella tidsstĂ€mpeln â inaktivitetsintervallet, och starttiden Ă€r senare Ă€n den aktuella tidsstĂ€mpeln + inaktivitetsintervallet. Med detta i Ă„tanke slĂ„s fyra poster frĂ„n tabell 5.1 samman till en enda session pĂ„ följande sĂ€tt.
1. Först kommer post 1, sÄ starttiden Àr lika med sluttiden och Àr 00:00:00.
2. DÀrefter kommer post 2, och vi letar efter sessioner som slutar senast 23:59:55 och börjar senast 00:00:35. Vi hittar post 1 och slÄr samman sessionerna 1 och 2. Vi tar starttiden för session 1 (den tidigare) och sluttiden för session 2 (den senare), sÄ vÄr nya session börjar kl. 00:00:00 och slutar kl. 00:00:15.
3. Post 3 kommer in, vi letar efter sessioner mellan 00:00:30 och 00:01:10 och hittar inga. Vi lÀgger till den andra sessionen för nyckeln 123-345-654,FFBE, som börjar och slutar kl. 00:00:50.
4. Post 4 kommer in, och vi letar efter sessioner mellan 23:59:45 och 00:00:25. Denna gĂ„ng hittar vi bĂ„da sessionerna â 1 och 2. Alla tre sessionerna slĂ„s samman i en, med starttid 00:00:00 och sluttid 00:00:15.
FrÄn det som har beskrivits i detta avsnitt Àr det viktigt att komma ihÄg följande kriterier:
- sessioner Àr inte fönster av fast storlek. Sessionens lÀngd bestÀms av aktiviteten inom det angivna tidsintervallet;
- tidsstÀmplarna i data avgör om en hÀndelse faller inom en befintlig session eller i inaktivitetsintervallet.
DĂ€refter kommer vi att diskutera den nĂ€sta typen av fönster â "rullande" fönster.
"Rullande" fönster
«Kullande» (tumbling) fönster fÄngar hÀndelser som intrÀffar under en viss tidsperiod. TÀnk dig att du behöver fÄnga alla aktietransaktioner för ett företag var 20:e sekund, sÄ att du samlar alla hÀndelser under denna tidsperiod. I slutet av den 20-sekundersintervallet "kullar" fönstret och gÄr vidare till det nya 20-sekunders observationsintervallet. Figur 5.14 illustrerar denna situation.

Som du kan se inkluderar alla hÀndelser som kommit in under de senaste 20 sekunderna i fönstret. I slutet av denna tidsperiod skapas ett nytt fönster.
I listing 5.6 visas koden som demonstrerar anvÀndningen av «kullande» fönster för att fÄnga aktietransaktioner var 20:e sekund (den kan hittas i filen src/main/java/bbejeck/chapter_5/CountingWindowingAndKtableJoinExample.java).

Tack vare denna lilla förÀndring i metoden TimeWindows.of kan ett «kullande» fönster anvÀndas. I det hÀr exemplet finns det ingen metodanrop till until(), vilket innebÀr att standardbevarandet av intervall, som Àr 24 timmar, kommer att anvÀndas.
Slutligen Àr det dags att gÄ till den sista av fönsteralternativen - «hoppar» (hopping) fönster.
Glidande («hoppar») fönster
Glidande/«hoppar» (sliding/hopping) fönster liknar «kullande», men med en liten skillnad. Glidande fönster vÀntar inte tills tidsintervallet Àr slut innan ett nytt fönster skapas för att bearbeta nyligen intrÀffade hÀndelser. De initierar nya berÀkningar efter ett vÀntetidsintervall som Àr kortare Àn fönstrets varaktighet.
För att illustrera skillnaderna mellan «kullande» och «hoppar» fönster, ÄtervÀnder vi till exemplet med aktietransaktionsrÀkning. VÄrt mÄl Àr fortfarande att rÀkna antalet transaktioner, men vi vill inte vÀnta hela tidsintervallet innan vi uppdaterar rÀknaren. IstÀllet kommer vi att uppdatera rÀknaren genom kortare tidsperioder. Till exempel, vi kommer fortfarande att rÀkna antalet transaktioner var 20:e sekund, men uppdatera rÀknaren var 5:e sekund, som visas i fig. 5.15. PÄ sÄ sÀtt fÄr vi tre resultatfönster med överlappande data.

I listing 5.7 visas koden för att stÀlla in glidande fönster (den kan hittas i filen src/main/java/bbejeck/chapter_5/CountingWindowingAndKtableJoinExample.java).

Det "rullande" fönstret kan omvandlas till ett "hoppa" genom att lÀgga till anropet till metoden advanceBy(). I det angivna exemplet Àr intervallet för lagring 15 minuter.
I detta avsnitt har du sett hur man begrÀnsar aggregatresultat med tidsfönster. I synnerhet vill jag att du ska komma ihÄg följande tre saker frÄn detta avsnitt:
- storleken pÄ sessionfönster begrÀnsas inte av tidsspannet, utan av anvÀndarnas aktivitet;
- de "rullande" fönstren ger en översikt över hÀndelser under en given tidsperiod;
- lÀngden pÄ "hoppa" fönster Àr fast, men de uppdateras ofta och kan innehÄlla överlappande poster i alla fönster.
Vi kommer nu att lÀra oss hur man omvandlar KTable tillbaka till KStream för anslutning.
5.3.3. Anslutning av KStream- och KTable-objekt
I kapitel 4 diskuterade vi anslutningen av tvÄ KStream-objekt. Nu ska vi lÀra oss att koppla KTable och KStream. Detta kan vara nödvÀndigt av följande enkla anledning. KStream Àr en ström av poster, medan KTable Àr en ström av uppdateringar av poster, men ibland kan det vara nödvÀndigt att lÀgga till ytterligare kontext till strömmen av poster med hjÀlp av uppdateringar frÄn KTable.
LÄt oss ta data om antalet börshandler och koppla dem till börsnyheter inom de relevanta branscherna. HÀr Àr vad vi behöver göra för att uppnÄ detta med den redan existerande koden.
- Omvandla KTable-objektet med data om antal börshandler till KStream med efterföljande byte av nyckeln till den som representerar branschen som motsvarar denna aktiesymbol.
- Skapa ett KTable-objekt som lÀser data frÄn Àmnet med börsnyheter. Denna nya KTable kommer att kategoriseras efter branscher.
- Koppla nyhetsuppdateringar med information om antalet börshandler efter branscher.
Nu ska vi se hur vi genomför den hÀr handlingsplanen.
Omvandling av KTable till KStream
För att omvandla KTable till KStream behöver man göra följande.
- Anropa metoden KTable.toStream().
- Genom att anropa metoden KStream.map, ersÀtt nyckeln med namnet pÄ branschen, och extrahera sedan objektet TransactionSummary frÄn instansen av Windowed.
Vi kommer att kedja dessa operationer pÄ följande sÀtt (koden finns i filen src/main/java/bbejeck/chapter_5/CountingWindowingAndKtableJoinExample.java) (listning 5.8).

Eftersom vi utför operationen KStream.map kommer om-sektioneringen av den returnerade KStream-instansen att göras automatiskt nÀr den anvÀnds i en sammanfogning.
Vi har slutfört konverteringsprocessen, nu behöver vi skapa ett KTable-objekt för att lÀsa börsnyheter.
Skapande av KTable för börsnyheter
Lyckligtvis rÀcker det med en rad kod för att skapa KTable-objektet (denna kod finns i filen src/main/java/bbejeck/chapter_5/CountingWindowingAndKtableJoinExample.java) (listing 5.9).

Det bör noteras att inga Serde-objekt behöver anges, eftersom strÀng-Surde anvÀnds i instÀllningarna. Dessutom fylls tabellen med poster i början tack vare anvÀndningen av upprÀkningen EARLIEST.
Nu kan vi gĂ„ vidare till det sista steget â sammanfogning.
Sammanfogning av nyhetsuppdateringar med data om antalet transaktionen
Att skapa sammanfogningen Àr inte svÄrt. Vi kommer att anvÀnda en vÀnster-sammanfogning för att hantera fallet dÀr det inte finns nÄgra börsnyheter för den aktuella branschen (den nödvÀndiga koden kan hittas i filen src/main/java/bbejeck/chapter_5/CountingWindowingAndKtableJoinExample.java) (listing 5.10).

Denna leftJoin-operator Àr ganska enkel. Till skillnad frÄn sammanfogningarna i kapitel 4 anvÀnds metoden JoinWindow inte, eftersom det för varje nyckel i KTable finns endast en post vid KStream-KTable-sammanfogningen. Denna sammanfogning Àr inte tidsbegrÀnsad: posten antingen finns i KTable eller inte. Den viktigaste insikten Àr att KTable-objekt kan berika KStream med mindre frekvent uppdaterad referensdata.
Nu ska vi titta pÄ ett mer effektivt sÀtt att berika hÀndelser frÄn KStream.
5.3.4. GlobalKTable-objekt
Som ni förstÄtt finns det ett behov av att berika hÀndelseflöden eller lÀgga till kontext till dem. I kapitel 4 sÄg ni sammanfogningarna av tvÄ KStream-objekt, och i det föregÄende avsnittet sammanfogningen av KStream och KTable. I alla dessa fall Àr det nödvÀndigt att om-sektera datan vid kartlÀggning av nycklar till en ny typ eller vÀrde. Ibland görs om-sektering explicit, och ibland gör Kafka Streams det automatiskt. Om-sektering Àr nödvÀndig eftersom nycklarna har Àndrats och posterna mÄste hamna i nya sektioner, annars kommer sammanfogningen att vara omöjlig (detta diskuterades i kapitel 4, avsnittet 'Om-sektering av data' del 4.2.4).
Ominaraktivering har sitt pris
Ominaraktivering krÀver resurser - ytterligare resurskostnader för att skapa mellanliggande Àmnen, lagra duplicerade data i ytterligare ett Àmne; det innebÀr ocksÄ en ökad latens pÄ grund av skrivning och lÀsning frÄn det Àmnet. Dessutom, om man behöver utföra en förening utöver en aspekt eller dimension, mÄste man organisera föreningarna i en kedja, avbilda poster med nya nycklar och Äterigen genomföra processen för omaraktivering.
Förening med mindre datamÀngder
I vissa fall Àr volymen av referensdata som planeras för förening relativt liten, sÄ att hela deras kopior kan rymmas lokalt pÄ varje nod. För sÄdana situationer finns det en klass som heter GlobalKTable i Kafka Streams.
Exemplaren av GlobalKTable Àr unika eftersom applikationen replikeras alla data pÄ varje nod. Och eftersom alla noder innehÄller all data, Àr det inte nödvÀndigt att omarakterisera hÀndelseflödet efter referensdatanyckeln för att den ska vara tillgÀnglig för alla sektioner. Med hjÀlp av objekten GlobalKTable kan man Àven utföra nyckellösa föreningar. LÄt oss Äterkomma till ett av de tidigare exemplen för att demonstrera den hÀr funktionen.
Förening av KStream-objekt med GlobalKTable-objekt
I avsnitt 5.3.2 genomförde vi fönsteraggregering av börstransaktioner utifrÄn köpare. Resultaten av denna aggregering sÄg ungefÀr ut sÄ hÀr:
{customerId='074-09-3705', stockTicker='GUTM'}, 17
{customerId='037-34-5184', stockTicker='CORK'}, 16Ăven om dessa resultat motsvarade det uppsatta mĂ„let skulle det vara mer praktiskt om ocksĂ„ kundens namn och företagets fullstĂ€ndiga namn visades. För att lĂ€gga till kundens namn och företagets namn kan man utföra vanliga föreningar, men dĂ„ behöver man göra tvĂ„ nyckelavbildningar och omaraktivering. Med GlobalKTable kan man undvika kostnader för sĂ„dana operationer.
För detta kommer vi att anvÀnda objektet countStream frÄn listings 5.11 (den motsvarande koden kan hittas i filen src/main/java/bbejeck/chapter_5/GlobalKTableExample.java), och förena det med tvÄ GlobalKTable-objekt.

Vi har redan diskuterat detta tidigare, sÄ jag ska inte upprepa mig. Jag vill dock notera att koden i funktionen toStream().map Àr abstraherad i ett funktionellt objekt istÀllet för en inbÀddad lambda-uttryck för lÀsbarhetens skull.
NÀsta steg Àr att deklarera tvÄ instanser av GlobalKTable (den medföljande koden finns i filen src/main/java/bbejeck/chapter_5/GlobalKTableExample.java) (lista 5.12).

Observera att Àmnesnamnen anges med hjÀlp av upprÀkningsbara typer.
Nu nÀr vi har förberett alla komponenter ÄterstÄr det att skriva koden för föreningen (som kan hittas i filen src/main/java/bbejeck/chapter_5/GlobalKTableExample.java) (lista 5.13).

Ăven om det finns tvĂ„ föreningar i denna kod, Ă€r de organiserade i en kedja eftersom inget av resultaten anvĂ€nds separat. Resultaten visas i slutet av hela operationen.
NÀr du kör den ovanstÄende föreningsoperationen fÄr du resultat av följande slag:
{customer='Barney, Smith' company="Exxon", transactions= 17}KÀrnan Àr oförÀndrad, men dessa resultat ser mer förstÄeliga ut.
Om vi rÀknar med kapitel 4 har du redan sett flera typer av föreningar i aktion. De listas i tabell 5.2. Denna tabell Äterspeglar föreningsmöjligheterna som Àr aktuella för version 1.0.0 av Kafka Streams; i framtida utgÄvor kan nÄgot förÀndras.

Som en sammanfattning pÄminner jag om det viktigaste: du kan förena hÀndelseströmmar (KStream) och uppdateringsflöden (KTable) med hjÀlp av lokal stat. Dessutom, om storleken pÄ referensdata inte Àr för stor, kan du anvÀnda objektet GlobalKTable. GlobalKTable replikerar alla partitioner pÄ varje nod i Kafka Streams-applikationen, vilket garanterar tillgÄngen till all data oavsett vilken partition nyckeln tillhör.
DÀrefter kommer vi att se en funktion i Kafka Streams som gör det möjligt att övervaka förÀndringar i tillstÄndet utan att konsumera data frÄn Kafka-Àmnet.
5.3.5. TillgÀngligt för frÄgetillstÄnd
Vi har redan utfört flera operationer som involverar tillstÄnd och alltid skrivit ut resultaten i konsolen (för utvecklingsÀndamÄl) eller skrivit dem till ett Àmne (för industriellt bruk). NÀr resultaten skrivs till ett Àmne mÄste du anvÀnda en Kafka-konsument för att se dem.
Att lĂ€sa data frĂ„n dessa Ă€mnen kan betraktas som en form av materialiserade vyer. För vĂ„ra Ă€ndamĂ„l kan vi anvĂ€nda definitionen av en materialiserad vy frĂ„n "Wikipedia": â... ett fysiskt objekt i databasen som innehĂ„ller resultaten av en frĂ„ga. Det kan till exempel vara en lokal kopia av fjĂ€rrdata, eller en delmĂ€ngd av rader och/eller kolumner frĂ„n en tabell eller resultaten av en sammanfogning, eller en pivottabell som erhĂ„llits genom aggregering" (https://en.wikipedia.org/wiki/Materialized_view).
Kafka Streams möjliggör ocksÄ interaktiva frÄgor mot tillstÄndslager, vilket ger betydelsen av direkt lÀsning av dessa materialiserade vyer. Det Àr viktigt att notera att en förfrÄgan till tillstÄndslagret Àr en "endast lÀsning"-operation. Tack vare detta behöver du inte vara rÀdd för att oavsiktligt göra tillstÄndet inkonsekvent under databehandlingen i applikationen.
Möjligheten att direkt frĂ„ga tillstĂ„ndslager Ă€r mycket betydelsefull. Det innebĂ€r att man kan skapa applikationer â instrumentpaneler utan att först behöva fĂ„ data frĂ„n Kafka-konsumenten. Den ökar ocksĂ„ effektiviteten i applikationen, eftersom det inte behövs att skriva om data:
- tack vare datalokaliteten kan de snabbt nÄs;
- dataduplicering utesluts, eftersom de inte skrivs till ett externt lagringsutrymme.
Det viktigaste jag vill att du ska komma ihÄg: du kan direkt utföra frÄgor mot tillstÄndet frÄn applikationen. Möjligheterna som detta ger dig kan inte överskattas. IstÀllet för att konsumera data frÄn Kafka och lagra poster i en databas för applikationen, kan du frÄga tillstÄndslager med samma resultat. Direkt frÄgor mot tillstÄndslager innebÀr mindre kod (ingen konsument) och mindre mjukvara (ingen behov av en databastabell för att lagra resultaten).
Vi har tĂ€ckt en stor mĂ€ngd information i detta kapitel, sĂ„ vi kommer att pausa vĂ„r diskussion om interaktiva förfrĂ„gningar till tillstĂ„ndslager. Men oroa dig inte: i kapitel 9 kommer vi att skapa en enkel applikation â en informationspanel med interaktiva förfrĂ„gningar. För att demonstrera interaktiva förfrĂ„gningar och möjligheterna att lĂ€gga till dem i Kafka Streams-applikationer kommer vi att anvĂ€nda nĂ„gra av exemplen frĂ„n detta och föregĂ„ende kapitel.
Sammanfattning
- KStream-objekt representerar flöden av hÀndelser, jÀmförbara med insÀttningar i en databas. KTable-objekt representerar flöden av uppdateringar och liknar mer uppdateringar i en databas. Storleken pÄ KTable-objekt vÀxer inte, gamla poster ersÀtts med nya.
- KTable-objekt behövs för aggregeringsoperationer.
- Med hjÀlp av fönsteroperationer kan man dela upp aggregerade data i tidsfack.
- Tack vare GlobalKTable-objekt kan man fÄ tillgÄng till referensdata var som helst i applikationen, oavsett partitionering.
- Det Àr möjligt att göra anslutningar mellan KStream-, KTable- och GlobalKTable-objekt.
Hitintills har vi fokuserat pĂ„ att skapa Kafka Streams-applikationer med hjĂ€lp av det högre nivĂ„ns DSL KStream. Ăven om den högre nivĂ„ns metod möjliggör skapandet av rena och koncisa program, Ă€r det en viss kompromiss. Att arbeta med DSL KStream innebĂ€r en ökning av koden koncishet pĂ„ bekostnad av kontrollnivĂ„. I nĂ€sta kapitel kommer vi att titta pĂ„ det lĂ€gre nivĂ„ns API för bearbetningsnoder och övervĂ€ga andra kompromisser. Programmen kommer att bli lĂ€ngre Ă€n de har varit tidigare, men vi kommer att ha möjlighet att skapa nĂ€stan vilken bearbetningsnod vi Ă€n kan behöva.
â LĂ€s mer om boken pĂ„
â För Habr-anhĂ€ngare finns det 25% rabatt med kupong â Kafka Streams
â Vid betalning av den tryckta versionen av boken skickas e-boken via e-post.
KĂ€lla: habr.com
