TSM - Kafka într-o arhitectură bazată pe microservicii

Edwin Hobor - Software Engineer/ Team Lead @ Micro Focus

Microserviciile sunt folosite de ceva timp, pentru a facilita dezvoltarea aplicațiilor slab cuplate, și pentru a le face ușor de întreținut și de testat. Unul din aspectele care trebuie avute în vedere atunci când se utilizează microserviciile, se referă la modul în care va avea loc partea de comunicare între acestea.

Deoarece nu există o definiție oficială a microserviciilor, se va face trimitere la o parte dintr-un articol scris de Martin Fowler, care are o scurtă descriere a acestui tip de arhitectură:

"In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms (…). These services are built around business capabilities and independently deployable by fully automated deployment machinery, (…) may be written in different programming languages and use different data storage technologies."

Kafka

Apache Kafka este o platformă open-source dezvoltată de LinkedIn și donată către Apache Software Foundation în 2011. Cele două limbaje principale de programare care sunt folosite pentru dezvoltare, sunt Scala și Java. ZooKeeper este folosit pentru a coordona mai multe instanțe de Kafka server.

Arhitectura de bază a Kafka este organizată în jurul câtorva termeni cheie: topicuri, producători, consumatori și brokeri.

Toate mesajele Kafka sunt organizate în topicuri. Dacă doriți să trimiteți un mesaj, îl trimiteți la un anumit topic și dacă doriți să citiți un mesaj, îl citiți dintr-un anumit topic. Un consumator scoate mesaje de pe un topic Kafka, în timp ce producătorii publică mesaje într-un topic Kafka. În cele din urmă, ca orice sistem distribuit, Kafka rulează într-un cluster. Fiecare nod din cluster este numit broker Kafka.

Kafka - "limbajul asincron al microserviciilor"

Într-o arhitectură bazată pe microservicii, Kafka gestionează foarte bine întregul trafic care se întâmplă în fundal. Fiind highly available, suntem mai puțin preocupați de întreruperi, iar eșecurile sunt tratate cu o întrerupere minimă a serviciilor. Deoarece Kafka păstrează datele pentru o perioadă de timp configurabilă, aveți opțiunea de a derula și relua evenimentele după cum este necesar. Această caracteristică face Kafka să fie percepută ca o soluție bună pentru aplicațiile de procesare a mesajelor pe scară largă.

Iată câteva puncte puternice în care Kafka ,,strălucește" în comparație cu alte sisteme tradiționale de mesagerie:

Randament sporit

Kafka gestionează date de mare viteză și volum mare fără a avea o configurație hardware mare. De asemenea, suportă mii de mesaje pe secundă.

Scalabilitate

Este foarte ușor să extindeți un cluster Kafka, deoarece nu necesită timp de întrerupere. Mai mult decât atât, manipularea și replicarea mesajelor din cadrul unui cluster se întâmplă transparent.

Distribuit

Un sistem distribuit este alcătuit din mai multe servere care funcționează împreună într-un cluster pentru a apărea ca un singur nod. În Kafka, aceste servere sunt numite brokeri și sunt distribuite în sensul că stochează, primesc și trimit mesaje pe noduri diferite.

Integritate a mesajelor

Să presupunem că avem un cluster Kafka cu trei noduri. În cazul în care unul dintre noduri se blochează din anumite motive, celelalte două mașini vor prelua automat echilibrarea încărcării. În cazul în care unul dintre brokeri nu reușește, ultima mașină Kafka va prelua toată prelucrarea. În cel mai rău scenariu, unde toate nodurile noastre sunt jos, vom putea totuși să ne bazăm pe jurnalul de date.

Durabilitate a mesajelor

Toate mesajele care trec prin Kafka sunt stocate pe disc. De asemenea, în cazul în care avem un cluster de noduri Kafka, putem configura replicarea astfel încât să ne asigurăm că mesajele nu se vor pierde.

Persistent (implicit)

Toate mesajele care trec prin Kafka sunt persistate implicit. Datorită acestui fapt, Kafka este durabilă și fiabilă.

Prietenos pentru consumatori

Kafka poate fi integrată cu mai mulți consumatori, utilizând diferite limbaje de programare.

Pe de altă parte, există și câteva dezavantaje atunci când alegeți să folosiți Kafka:

Complex - necesită timp pentru înțelegere

Dacă doriți să utilizați Kafka, asigurați-vă că investiți o cantitate considerabilă de timp pentru a înțelege pe deplin fiecare detaliu. Dacă nu vă alocați destul timp pentru asta, Kafka ar putea să devină mai degrabă o piedică în arhitectură decât o soluție optimă.

Lipsa unui set complet de instrumente de monitorizare integrate

Deocamdată, Kafka nu dispune de un set complet de instrumente integrate de gestionare și monitorizare. Din acest motiv, mulți oameni nu sunt convinși în a alege Kafka pe termen lung.

Persistența mesajelor este costisitoare

Datele sunt adesea duplicate și vor crește în mod semnificativ necesitând să aveți cerințe hardware serioase.

Unele dintre cazurile obișnuite de utilizare ale Kafka sunt:

Schimb de mesaje

Kafka funcționează bine ca înlocuitor pentru un broker de mesaje mai tradițional (cum ar fi RabbitMQ sau ActiveMQ).

Urmărirea activității pe site

Oferă abilitatea de a urmări toate acțiunile pe care un utilizator le ia pe o anumită pagină web și de a le putea relua.

Metrici

Oferă posibilitatea monitorizării statisticilor referitoare la aplicații și trimiterea lor la un cluster centralizat.

Agregare de loguri

Kafka poate servi ca un loc centralizat unde toate jurnalele serviciilor sunt păstrate într-un format standard.

Exemplu de utilizare Kafka

Să luăm în considerare un exemplu simplu de utilizare Kafka într-o aplicație bancară. Știm cu toții că putem alege să fim notificați ori de câte ori o operațiune este executată pe contul nostru bancar (retragere, depunere sau încasarea unei sume de bani). Notificările pot fi trimise prin e-mail, de tip push sau prin SMS. Utilizatorul va avea posibilitatea de a selecta toate sau nici una dintre aceste opțiuni.

Ce ar fi dacă am putea decupla partea legată de notificări într-un microserviciu separat și am putea să o declanșăm asincron? De fapt, acesta este partea unde Kafka ne poate ajuta, jucând rolul unui sistem de mesagerie între logica operațiunilor bancare și partea de notificare.

Privind schema de mai sus, să presupunem că utilizatorul inițiază o cerere de retragere. Să urmărim modul în care va fi procesată:

  1. Prin intermediul interfeței aplicației, utilizatorul inițiază cererea de retragere.
  2. Interfața face o cerere HTTP care ajunge la Gatewayul API al backendului.
  3. Serviciul de operații bancare (pe care îl putem considera ca o abstractizare pentru unul sau mai multe microservicii care deservesc un singur scop) va servi tot o cerere HTTP.
  4. După ce toată logica de business în jurul retragerii este executată, schimbările sunt persistate în baza de date.
  5. Într-o manieră asincronă, producătorul Kafka al acestui serviciu produce un nou mesaj pe topicul AccountOperation către clusterul Kafka. Acest mesaj va fi stocat pe brokerul Kafka care este liderul topicului AccountOperation.
  6. Serviciul de operații bancare va trimite un răspuns HTTP către gatewayul API.
  7. Gatewayul API va trimite, de asemenea, un răspuns HTTP înapoi la interfața utilizatorului.
  8. Utilizatorul primește starea acțiunii de retragere într-o manieră sincronă. În continuare, utilizatorul va aștepta ca notificările să fie trimise într-o modalitate asincronă.
  9. În mod asincron, serviciul de notificare va consuma noul mesaj care a fost postat pe topicul AccountOperation.
  10. Pe baza tipurilor de notificări pe care utilizatorul le-a selectat (email, push, SMS), acest serviciu va gestiona în continuare compoziția și livrarea mesajului către utilizatorul final.

Ce se întâmplă în cazul în care serviciul de notificare nu funcționează?

În acest caz, Kafka se asigură că toate mesajele sunt persistate și gata să fie consumate de îndată ce serviciul de notificare este readus la viață. Singurul dezavantaj ar fi că utilizatorul final nu va fi anunțat imediat în legătură cu operațiunile sale, ceea ce, în opinia mea, este un compromis acceptabil.

Să vedem cât de ușor este să scriem consumatorul și producătorul pentru partea de notificări, folosind Spring Kafka :

@Autowired
private KafkaTemplate kafkaTemplate;

public void publishNotificationMsg(NotificationMessage notificationMessage) { 
   LOGGER.debug("Pushing a new notification request message on topic AccountOperation");
   LOGGER.debug("Message: " + new Gson().toJson(notificationMessage)); 
   kafkaTemplate.send("AccountOperation", notificationMessage);
   LOGGER.info("Kafka message successfully published on AccountOperation topic.");
}
@KafkaListener(topics = "AccountOperation",
   containerFactory = "defaultKafkaListenerContainerFactory")

public void  handleNotificationMsg(
   @Header(KafkaHeaders.RECEIVED_TOPIC) final String topic, 
   @Header(KafkaHeaders.RECEIVED_PARTITION_ID) final int partition, 
   @Header(KafkaHeaders.OFFSET) final int offset, 
   @Header(KafkaHeaders.RECEIVED_MESSAGE_KEY) final String key, 
   @Payload final String message) {

LOGGER.info("Received notification Kafka message: topic={}, partition={}, offset={}, key={}",
topic, partition, offset, key);

NotificationMessage notificationMessage = 
   SerializerUtils.deserializeObject(message, NotificationMessage.class);

   notificationService.sendNotification(notificationMessage); 
   LOGGER.info("Successfully consumed notification Kafka message.");
}

În ambele cazuri, NotificationMessage este o clasă POJO care înglobează toate detaliile despre notificarea care urmează să fie trimisă.

Concluzii

Folosind Kafka ca o magistrală de mesaje, putem obține un nivel ridicat de paralelism și decuplare între producătorii și consumatorii de date, făcând arhitectura noastră mai flexibilă și mai adaptabilă la schimbare. Kafka devine rapid coloana vertebrală de date a numeroaselor organizații. Unele dintre ele sunt LinkedIn, Twitter, Netflix, Mozilla, Oracle și Micro Focus.

Chiar dacă acest articol oferă doar o privire de ansamblu asupra arhitecturii Kafka, sperăm că a reușit să vă facă mai conștienți de ceea ce este Kafka și cum se comportă într-o arhitectură orientată spre microservicii. Pentru o descriere mai cuprinzătoare, vă recomand să treceți prin documentația oficială despre Kafka. Spor la învățat Kafka!