Was ist Kafka Streams?

Kafka Streams ist eine Java (bzw. Scala) Biblio­thek, die auf Apa­che Kafka (wei­ter­hin Kafka genannt) auf­setzt und das Strea­ming von Daten in Echt­zeit ermög­licht. Um Kafka Streams zu ver­ste­hen, braucht man daher einen klei­nen Ein­blick in das Öko­sys­tem von Kafka.

Im Kafka Öko­sys­tem gibt es Bau­steine, die klein star­ten und zu immer grö­ße­ren Bau­stei­nen zusam­men­ge­setzt wer­den kön­nen. Das kleinste Ele­ment ist ein soge­nann­ter „Record“, der aus einem Pär­chen von einem Schlüs­sel und einem Wert (Key-Value Pair) besteht. Diese Records wer­den in soge­nann­ten Topics als Bytes gespei­chert und per­sis­tiert. Um die Daten­ver­ar­bei­tung par­al­le­li­sie­ren zu kön­nen, besteht ein Topic aus N ver­schie­de­nen Par­ti­tio­nen, wobei N eine kon­fi­gu­rier­bare Zahl ist. 

Zum Schrei­ben von Daten in ein Topic und zum Lesen von Daten aus einem Topic wer­den jeweils zwei Objekte benö­tigt: Zum einen wer­den beim Schrei­ben ein Seria­li­zer und ein Pro­du­cer benutzt. Dabei wan­delt der Seria­li­zer einen Record in seine Byte Reprä­sen­ta­tion um. Zu ande­ren wird zum Lesen ein Dese­ria­li­zer und ein Con­su­mer ver­wen­det. Hier liest der Dese­ria­li­zer die Byte Reprä­sen­ta­tion und kon­ver­tiert diese wie­der in den Record. Wei­ter­hin per­sis­tie­ren Pro­du­cer die Records mit Hilfe der Seria­li­zer im Topic, wäh­rend die Con­su­mer diese aus­le­sen und somit eine Wei­ter­ver­ar­bei­tung ermög­li­chen. Dabei ist es wich­tig zu wis­sen, dass Daten, die von einem von M Kon­su­men­ten aus einem Topic gele­sen wer­den, wei­ter­hin für alle ande­ren Kon­su­men­ten zur Ver­fü­gung ste­hen (im Gegen­satz zu einer Mes­sage Queue). Jede Kafka Streams Appli­ka­tion besteht min­des­tens aus der Kom­bi­na­tion von einem Con­su­mer und einem Pro­du­cer, die von der Java Biblio­thek gehand­habt wird. 

Zusätz­lich kann eine Kafka Streams Appli­ka­tion noch zusätz­li­che Bestand­teile haben, unter ande­rem kön­nen das State Stores, in denen Daten in einer Rock­sDB vor­ge­hal­ten wer­den kön­nen, Trans­for­mern, in denen Daten trans­for­miert wer­den, oder ande­ren Bestand­tei­len sein. Dabei erlaubt der Ein­satz von State Stores soge­nannte sta­teful Ope­ra­tio­nen, da der letzte Stand eines Records im State Store abge­legt wer­den kann. Dadurch kann kom­ple­xere Logik imple­men­tiert wer­den als in sta­te­l­ess Ope­ra­tio­nen, wo nur Zugriff auf den aktu­el­len Stand eines Records mög­lich ist.

Kafka Streams und ihre Topologie

Wie die Ein­zel­teile der Kafka Stream Appli­ka­tion zusam­men­spie­len, kann in Gra­phen, der „Topo­lo­gie“, dar­ge­stellt wer­den. Eine sche­ma­ti­sche Dar­stel­lung von einer Topo­lo­gie ist in Abbil­dung 1 zu sehen. In die­sen Topo­lo­gien kön­nen Daten sowohl in kon­ti­nu­ier­li­chen Streams, den KStreams, ver­ar­bei­tet wer­den, als auch in Tabel­len, den KTa­bles, ver­ar­bei­tet wer­den. Die zwei größ­ten Vor­teile von Kafka Streams gegen­über einem Con­su­mer-Pro­du­cer Paar in Apa­che Kafka sind, einer­seits die Mög­lich­keit sta­teful Ope­ra­tio­nen mit Hilfe von State Stores durch­füh­ren zu kön­nen, ande­rer­seits, dass man sich bei der Ent­wick­lung einer Kafka Streams Appli­ka­tion fast aus­schließ­lich auf die Daten­ver­ar­bei­tung kon­zen­trie­ren kann und sich nicht um low-level The­ma­ti­ken wie Par­al­le­li­sie­rung, Feh­ler­to­le­ranz und Ska­lier­bar­keit küm­mern muss.

Schematische Darstellung einer Kafka Streams Topologie
Abbil­dung 1: Sche­ma­ti­sche Dar­stel­lung einer Topologie

Was ist über­haupt Strea­ming und was ist der Unter­schied zu Batch Prozessen?

Sowohl Strea­ming als auch ein Batch Pro­zess beschreibt die auto­ma­ti­sierte Ver­ar­bei­tung von Daten als Bün­de­lung von vie­len Ein­zel­schrit­ten. Der größte Unter­schied zwi­schen bei­den Typen der Ver­ar­bei­tung ist, wann und wie oft die Daten ver­ar­bei­tet wer­den. Eines der Erken­nungs­merk­male eines Batch Pro­zes­ses ist, dass es in regel­mä­ßi­gen Abstän­den aus­ge­führt wird, wie zum Bei­spiel bei einer Tages­ab­schluss­ver­ar­bei­tung von Ver­kaufs­da­ten. Daher gibt es bei der Batch Ver­ar­bei­tung immer einen klar defi­nier­ten Start­zeit­punkt und einen klar defi­nier­ten End­zeit­punkt. Im Kon­trast dazu steht das Strea­ming, bei dem Daten kon­ti­nu­ier­lich ver­ar­bei­tet wer­den, sobald diese zur Ver­fü­gung ste­hen. Da die Daten kon­ti­nu­ier­lich ver­ar­bei­tet den, ist es mög­lich in Echt­zeit die Daten wei­ter zu ver­ar­bei­ten. Das ist beson­ders inter­es­sant, in Situa­tio­nen, in denen sich Daten oft und schnell ändern kön­nen, wie zum Bei­spiel bei ein GPS Emp­fän­ger, der jede Sekunde aus­ge­le­sen wird. Diese Daten kön­nen dann in Echt­zeit zum Bei­spiel in einem Flug­zeug benutzt wer­den, um zu über­prü­fen, dass man wei­ter­hin auf dem rich­ti­gen Kurs ist. Der zweite typi­sche Anwen­dungs­fall ent­steht dann, wenn Daten in (nahezu) Echt­zeit Bereit­ge­stellt wer­den müs­sen, bei­spiels­weise auf einer gra­fi­schen Ober­flä­che. Müsste man in die­sen bei­den Fäl­len erst war­ten, bis die Daten in einem vor­de­fi­nier­ten Zeit­fens­ter ver­ar­bei­tet wer­den, wäre dies sehr mühsam. 

Bei­spiel für Unter­schiede beim Strea­ming gegen­über Batch Prozessen

Wäh­rend Strea­ming eine her­vor­ra­gende Mög­lich­keit bie­tet, um Pro­zesse im Betrieb zu beschleu­ni­gen, ist es kein Selbst­läu­fer. Das größte Risiko beim Ver­wen­den von Strea­ming Pro­zes­sen ist es, einen sol­chen Pro­zess iden­tisch zu behan­deln wie einen Batch Pro­zess. In der Welt von Batch Pro­zes­sen muss man sich wenig Gedan­ken machen, wann und in wel­cher Rei­hen­folge Daten ver­schickt wer­den. Wenn man Daten aus zwei Quell­sys­te­men in einem Join ver­ar­bei­ten möchte, lädt man im Batch Pro­zess zunächst das erste Set von Daten in die erste Tabelle, dann das zweite Set in eine zweite Tabelle und führt dann den Join durch. Solange die Daten vor der Ver­ar­bei­tung vor­lie­gen, ist es beim Batch Pro­zess daher egal, wann die Daten geschickt wer­den. Im Gegen­satz dazu, kann die Rei­hen­folge und der Zeit­punkt in einer Kafka Streams Appli­ka­tion bei einem Join ent­schei­dend sein. Dazu sollte erwähnt wer­den, dass es bei Kafka Streams ver­schie­dene Kom­bi­na­ti­ons­mög­lich­kei­ten gibt. Je nach Daten­lage kann es sinn­voll sein, zwei KStreams, zwei KTa­bles oder einen KStream und einen KTa­ble mit­ein­an­der Joinen. Die Unter­schei­dung ist wich­tig, da sich die Joins von der ver­schie­de­nen Objekte unter­schied­lich ver­hal­ten. Wel­che Joins für die Kom­bi­na­tio­nen zur Ver­fü­gung ste­hen, ist in Abbil­dung 2 zu sehen. 

Outer, Inner und Left Joins in Kafka Streams und deren mögliche Kombinationen zwischen KStreams, KTables und Global KTables
Abbil­dung 2: Joins in Kafka Streams und mög­li­che Kom­bi­na­tio­nen zwi­schen KStreams, KTa­bles und Glo­bal KTables.

Kafka Streams am Bei­spiel des Left Joins

Will man bei­spiels­weise einen Left Join zwi­schen einem KStream und einem KTa­ble durch­füh­ren, spielt die Rei­hen­folge der Events eine wich­tige Rolle. Stel­len wir uns vor, dass wir zwei Daten­sets in Echt­zeit bezie­hen. Ers­tens durch einen KStream, genannt Views (als lin­ker Anteil) und zwei­tens durch einen KTa­ble, genannt Klicks (als rech­ter Anteil). Sobald ein Event in der View ent­steht, wird der Join durch­ge­führt. Wenn die Daten wie in Abbil­dung 3 kom­men, führt das dazu, dass die Ergeb­nisse des Left Joins zwi­schen einer Kafka Streams Appli­ka­tion und einem Batch Pro­zess abwei­chen kön­nen. Im Bei­spiel vom Event „A“ in dem Daten für Views zum Zeit­punkt 0 und für Klicks zum Zeit­punkt 1 kom­men, wird das Ergeb­nis für eine Kafka Streams Appli­ka­tion nur den lin­ken Anteil ent­hal­ten, wäh­rend ein Batch Pro­zess sowohl den lin­ken als auch den rech­ten Anteil beinhal­tet hätte. Daher sind die Ergeb­nisse vom Strea­ming und vom Batch Pro­zess nur iden­tisch, wenn der rechte Anteil zu erst oder nie kommt,

Übersicht über Ergebnisse eines Left Joins von einem KStream mit einem KTable
Abbil­dung 3: Ergeb­nis eines Left Joins von „Views“ mit „Klicks“

Zusam­men­fas­sung

Kafka Streams ist eine benut­zer­freund­li­che high-level Java Biblio­thek, mit der man Daten kon­ti­nu­ier­lich und in Echt­zeit ver­ar­bei­tet. Damit ist Kafka Streams eines von vie­len Bei­spie­len für einen Strea­ming Pro­zess. In einer Kafka Streams Appli­ka­tion wer­den Daten aus min­des­tens einem Topic gele­sen, ange­rei­chert und letzt­lich in min­des­tens einem Topic geschrie­ben. Die Anrei­che­rung kann dabei sowohl sta­te­l­ess als auch sta­teful mit einem State Store statt­fin­den. Strea­ming steht gene­rell im Gegen­satz zu Batch Ver­ar­bei­tung von Daten, in der Daten an vor­de­fi­nier­ten Zeit­punk­ten pro­zes­siert wer­den. Daher ist es ein größse Risiko eine Kafka Streams Appli­ka­tion so zu ent­wi­ckeln wie einen Batch Pro­zess. Im Gegen­satz zu Batch Pro­zes­sen, kann die näm­lich Rei­hen­folge von Events einen gro­ßen Ein­fluss auf das Ergeb­nis haben.