{"id":30778,"date":"2019-10-31T21:37:23","date_gmt":"2019-10-31T18:37:23","guid":{"rendered":"https:\/\/prohoster.info\/blog\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka\/"},"modified":"2019-10-31T21:37:23","modified_gmt":"2019-10-31T18:37:23","slug":"opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","status":"publish","type":"post","link":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","title":{"rendered":"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Was k\u00f6nnte ein so gro\u00dfes Unternehmen wie Lamoda, mit einem optimierten Prozess und Dutzenden miteinander verbundener Dienste, dazu bewegen, seinen Ansatz erheblich zu \u00e4ndern? Die Motivation kann ganz unterschiedlich sein: von gesetzlichen Vorgaben bis hin zum typischen Wunsch von Programmierern, Neues auszuprobieren.<\/p>\n<p>Das bedeutet jedoch nicht, dass man nicht mit zus\u00e4tzlichen Vorteilen rechnen kann. Was konkret gewonnen werden kann, wenn ein events-driven API auf Kafka implementiert wird, erkl\u00e4rt Sergey Zaika (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/fewald\/\" class=\"user_link\">fewald<\/a><\/noindex>). Auch \u00fcber Fehler und interessante Entdeckungen wird es unbedingt gehen \u2013 Experimente kommen nicht ohne sie aus.<\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/7ab959ab45ec5c6565b35b18b361c0ea.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<em>Haftungsausschluss: Dieser Artikel basiert auf Materialien des Meetups, das Sergey im November 2018 auf HighLoad++ gehalten hat. Die praktischen Erfahrungen von Lamoda mit Kafka zogen die Zuh\u00f6rer nicht weniger an als andere Vortr\u00e4ge im Programm. Wir glauben, dass dies ein hervorragendes Beispiel daf\u00fcr ist, wie wichtig es ist, Gleichgesinnte zu finden, und dass die Organisatoren von HighLoad++ auch weiterhin bestrebt sein werden, eine einladende Atmosph\u00e4re daf\u00fcr zu schaffen.<\/em><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>\u00dcber den Prozess<\/h2>\n<p>\nLamoda \u2014 eine gro\u00dfe E-Commerce-Plattform mit eigenem Kontaktzentrum, Lieferservice (und zahlreichen Partnern), Fotostudio und einem riesigen Lager, alles betrieben mit eigener Software. Es gibt Dutzende von Zahlungsmethoden, B2B-Partner, die einen Teil oder alle diese Dienstleistungen nutzen k\u00f6nnen und aktuelle Informationen zu ihren Produkten ben\u00f6tigen. Dar\u00fcber hinaus ist Lamoda in drei L\u00e4ndern neben Russland aktiv, und dort l\u00e4uft nicht alles ganz nach dem gleichen Muster. Insgesamt gibt es wahrscheinlich mehr als hundert M\u00f6glichkeiten, eine neue Bestellung zu konfigurieren, die auf verschiedene Weise bearbeitet werden muss. All dies funktioniert mithilfe von Dutzenden von Diensten, die sich manchmal auf nicht offensichtliche Weise austauschen. Es gibt auch ein zentrales System, dessen Hauptaufgabe die Bestellstatusverwaltung ist. Wir nennen es BOB, und ich arbeite damit.<\/p>\n<h2>Refund Tool mit einem ereignisgesteuerten API <\/h2>\n<p>\nDer Begriff ereignisgesteuert ist etwas abgedroschen; weiter unten werden wir genauer definieren, was damit gemeint ist. Ich beginne mit dem Kontext, in dem wir beschlossen haben, den Ansatz der ereignisgesteuerten API in Kafka auszuprobieren. <\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/3bfdce47dd8420fc63d84645e76de647.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIn jedem Gesch\u00e4ft gibt es neben den K\u00e4ufen, f\u00fcr die Kunden bezahlen, Momente, in denen das Gesch\u00e4ft R\u00fcckerstattungen vornehmen muss, weil das Produkt nicht den Erwartungen des Kunden entspricht. Dieser vergleichsweise kurze Prozess umfasst die Kl\u00e4rung von Informationen, falls notwendig, und die Durchf\u00fchrung der R\u00fcck\u00fcberweisung. <\/p>\n<p>Aber die R\u00fcckerstattung wurde aufgrund von Gesetzes\u00e4nderungen komplizierter, und wir mussten daf\u00fcr einen separaten Mikrodienst implementieren.<\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e358df5476e7448976e0f1147a103fb.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nUnsere Motivation:<\/p>\n<ol>\n<li><strong>Gesetz FZ-54<\/strong>\u00a0\u2013 kurz gesagt, das Gesetz verlangt, dass jede Geldtransaktion, sei es eine R\u00fcckerstattung oder ein Zahlungseingang, innerhalb eines relativ kurzen Zeitrahmens von wenigen Minuten dem Finanzamt gemeldet wird. Als E-Commerce-Unternehmen f\u00fchren wir eine Vielzahl von Transaktionen durch. Technisch bedeutet das eine neue Verantwortung (und somit einen neuen Dienst) sowie Anpassungen in allen beteiligten Systemen.<\/li>\n<li><strong>BOB Split<\/strong>\u00a0\u2013 ein internes Projekt des Unternehmens, um BOB von einer gro\u00dfen Anzahl nicht relevanter Verantwortlichkeiten zu befreien und die Komplexit\u00e4t insgesamt zu reduzieren.<\/li>\n<\/ol>\n<p>\n<img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/d3ecf9961bdb372fc5f84ee9389f73ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIn diesem Diagramm sind die Hauptsysteme von Lamoda dargestellt. Momentan besteht der Gro\u00dfteil davon eher aus einem <strong>Sternenbild von 5-10 Mikrodiensten um einen schrumpfenden Monolithen.<\/strong>. Sie wachsen langsam, aber wir bem\u00fchen uns, sie kleiner zu halten, da das Deployment eines zentralen Elements riskant ist \u2013 wir d\u00fcrfen nicht zulassen, dass es ausf\u00e4llt. Alle Exchanges (Pfeile) m\u00fcssen wir reservieren und uns darauf einstellen, dass einer von ihnen m\u00f6glicherweise nicht verf\u00fcgbar ist.<\/p>\n<p>Auch in BOB gibt es eine Menge Exchanges: Zahlungssysteme, Versand, Benachrichtigungen usw. <\/p>\n<p>Technisch gesehen ist BOB:<\/p>\n<ul>\n<li>~150k Codezeilen + ~100k Testzeilen;<\/li>\n<li>php7.2 + Zend 1 &amp; Symfony Components 3;<\/li>\n<li>&gt;100 APIs &amp; ~50 ausgehende Integrationen;<\/li>\n<li>4 L\u00e4nder mit ihrer eigenen Gesch\u00e4ftslogik. <\/li>\n<\/ul>\n<p>\nDas Deployment von BOB ist teuer und kompliziert. Die Menge an Code und die Probleme, die er l\u00f6st, sind so gro\u00df, dass niemand ihn im Ganzen im Kopf haben kann. Es gibt also viele Gr\u00fcnde, ihn zu vereinfachen.<\/p>\n<h2>R\u00fcckerstattungsprozess<\/h2>\n<p>\nUrspr\u00fcnglich sind zwei Systeme am Prozess beteiligt: BOB und Payment. Jetzt kommen noch zwei hinzu:<\/p>\n<ul>\n<li>Der Fiscalization Service, der sich um die Probleme der Fiskalisierung und die Kommunikation mit externen Diensten k\u00fcmmert.<\/li>\n<li>Das Refund Tool, in das einfach neue Exchanges eingebracht werden, um BOB nicht unn\u00f6tig aufzubl\u00e4hen.<\/li>\n<\/ul>\n<p>\nJetzt sieht der Prozess so aus:<\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/13c02975881ad35c61304053c604cda3.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ol>\n<li>BOB erh\u00e4lt eine Anfrage zur R\u00fcckerstattung von Geld.<\/li>\n<li>BOB informiert dar\u00fcber das Refund Tool.<\/li>\n<li>Das Refund Tool sagt Payment: \u201eGib das Geld zur\u00fcck.\u201c<\/li>\n<li>Payment erstattet das Geld.<\/li>\n<li>Das Refund Tool und BOB synchronisieren ihre Status, weil sie derzeit beide darauf angewiesen sind. Wir sind noch nicht bereit, vollst\u00e4ndig auf das Refund Tool umzusteigen, da BOB eine Benutzeroberfl\u00e4che, Berichte f\u00fcr die Buchhaltung und viele Daten bietet, die sich nicht einfach \u00fcbertragen lassen. So m\u00fcssen wir auf zwei St\u00fchlen sitzen.<\/li>\n<li>Es wird eine Anfrage zur Fiskalisierung gesendet.<\/li>\n<\/ol>\n<p>\nLetztendlich haben wir eine Art von Ereignisbus auf Kafka erstellt, auf dem alles basiert. Hurra, jetzt haben wir einen einzigen Ausfallpunkt (Sarkasmus).<\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/674edd7972998b4985071f5250612c7e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDie Vor- und Nachteile sind ziemlich offensichtlich. Wir haben den Bus gebaut, was bedeutet, dass jetzt alle Dienste davon abh\u00e4ngen. Das vereinfacht das Design, bringt aber einen einzigen Ausfallpunkt ins System. Wenn Kafka ausf\u00e4llt, steht der Prozess still.<\/p>\n<h2>Was ist ein ereignisgesteuertes API? <\/h2>\n<p>\nEine gute Antwort auf diese Frage finden Sie im Vortrag von Martin Fowler (GOTO 2017). <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/STKCRSUsyPO\">\u201eDie vielen Bedeutungen der ereignisgesteuerten Architektur\u201c<\/a><\/noindex>. <\/p>\n<p>Kurz gesagt, was wir gemacht haben:<\/p>\n<ol>\n<li>Wir haben alle asynchronen Austauschprozesse \u00fcber <strong>Events Storage<\/strong>. Anstatt jedem interessierten Verbraucher im Netzwerk \u00fcber eine Status\u00e4nderung zu informieren, schreiben wir ein Ereignis \u00fcber den Statuswechsel in einen zentralen Speicher, und interessierte Verbraucher lesen alles, was dort erscheint.<\/li>\n<li>Ein Ereignis (event) ist in diesem Fall eine Benachrichtigung (<strong>Benachrichtigungen<\/strong>) dar\u00fcber, dass sich etwas irgendwo ge\u00e4ndert hat. Zum Beispiel k\u00f6nnte sich der Status einer Bestellung ge\u00e4ndert haben. Der Verbraucher, der an bestimmten erg\u00e4nzenden Informationen zum Statuswechsel interessiert ist und die in der Benachrichtigung nicht enthalten sind, kann den Status selbst \u00fcberpr\u00fcfen.<\/li>\n<li>Die umfangreichste Variante ist vollst\u00e4ndiges Event Sourcing, <strong>State Transfer<\/strong>, bei dem das Ereignis alle Informationen enth\u00e4lt, die f\u00fcr die Verarbeitung erforderlich sind: von wo und in welchen Status gewechselt wurde, wie sich die Daten genau ver\u00e4ndert haben usw. Die Frage ist nur die Zweckm\u00e4\u00dfigkeit und der Umfang der Informationen, die Sie speichern k\u00f6nnen.<\/li>\n<\/ol>\n<p>\nIm Rahmen der Einf\u00fchrung des Refund Tools haben wir die dritte Variante verwendet. Dies erleichterte die Verarbeitung von Ereignissen, da keine detaillierten Informationen abgerufen werden mussten, und es wurde das Szenario ausgeschlossen, in dem jedes neue Ereignis eine Welle von anfragenden GET-Anfragen von Verbrauchern ausl\u00f6st.<\/p>\n<p>Der Refund Tool-Service <strong>ist nicht belastet<\/strong>, daher ist Kafka hier eher ein Test als eine Notwendigkeit. Ich denke nicht, dass, wenn der R\u00fcckerstattungsdienst zu einem Highload-Projekt werden w\u00fcrde, das Unternehmen gl\u00fccklich w\u00e4re.<\/p>\n<h4>Async Exchange AS IS<\/h4>\n<p>\nF\u00fcr asynchrone Austauschprozesse nutzt die PHP-Abteilung in der Regel RabbitMQ. Wir haben die Daten f\u00fcr die Anfrage gesammelt, in die Warteschlange gelegt, und der Verbraucher dieses Dienstes hat sie gelesen und gesendet (oder auch nicht gesendet). F\u00fcr die API nutzt Lamoda aktiv Swagger. Wir entwerfen die API, beschreiben sie in Swagger und generieren den Client- und Server-Code. Au\u00dferdem verwenden wir eine leicht erweiterte JSON RPC 2.0. <\/p>\n<p>An einigen Stellen werden ESB-Busse verwendet, manche arbeiten mit ActiveMQ, aber insgesamt <strong>ist RabbitMQ der Standard.<\/strong>.<\/p>\n<h4>Async exchange TO BE<\/h4>\n<p>\nBeim Entwerfen des Austauschs \u00fcber den Events-Bus zeigt sich eine Analogie. Wir beschreiben den zuk\u00fcnftigen Datenaustausch \u00e4hnlich durch die Beschreibung der Struktur des Events. Das Format ist YAML, die Codegenerierung mussten wir selbst durchf\u00fchren, der Generator erstellt DTOs gem\u00e4\u00df der Spezifikation und lehrt die Clients und Server, mit ihnen zu arbeiten. Die Generierung erfolgt in zwei Sprachen - <strong>Golang und PHP.<\/strong>Das erm\u00f6glicht es, die Bibliotheken konsistent zu halten. Der Generator ist in Golang geschrieben und tr\u00e4gt den Namen gogi.<\/p>\n<p>Event-Sourcing mit Kafka ist ein typisches Vorgehen. Es gibt eine L\u00f6sung von der Hauptunternehmensversion Kafka Confluent, es gibt <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/nakadi\">Nakadi,<\/a><\/noindex>eine L\u00f6sung von unseren \"Br\u00fcdern\" im Domain-Bereich Zalando. Unsere <strong>Motivation, mit Vanilla Kafka zu beginnen.<\/strong>\u00a0\u2014 das ist, um die L\u00f6sung kostenlos zu lassen, bis wir endg\u00fcltig entschieden haben, ob wir sie weit verbreitet nutzen werden, und um uns Spielraum f\u00fcr Anpassungen zu lassen: wir m\u00f6chten Unterst\u00fctzung f\u00fcr unser <strong>JSON RPC 2.0<\/strong>, Generatoren f\u00fcr zwei Sprachen, und wir werden sehen, was sonst noch. <\/p>\n<p>Ironischerweise k\u00f6nnen wir, selbst wenn es ein \u00e4hnliches Gesch\u00e4ft wie Zalando gibt, das eine \u00e4hnliche L\u00f6sung entwickelt hat, diese nicht effektiv nutzen. <\/p>\n<p>Architektonisch ist das Muster beim Start so: Wir lesen direkt aus Kafka, aber schreiben nur \u00fcber den Events-Bus. F\u00fcr das Lesen in Kafka gibt es viel Fertiges: Broker, Load-Balancer und es ist mehr oder weniger bereit f\u00fcr horizontale Skalierung, das wollten wir beibehalten. Das Schreiben wollten wir jedoch \u00fcber einen Gateway, auch bekannt als Events-Bus, umsetzen, und das ist der Grund.<\/p>\n<h3>Events-Bus<\/h3>\n<p>\nOder Event-Bus. Das ist einfach ein stateless HTTP-Gateway, das mehrere wichtige Rollen \u00fcbernimmt:<\/p>\n<ul>\n<li><strong>Validierung des Produzierens<\/strong>\u00a0\u2014 wir \u00fcberpr\u00fcfen, ob die Ereignisse unserer Spezifikation entsprechen.<\/li>\n<li><strong>Master-System f\u00fcr Ereignisse<\/strong>, also dies ist das Haupt- und einzige System im Unternehmen, das beantwortet, welche Events mit welchen Strukturen als g\u00fcltig gelten. Die Validierung umfasst einfach Datentypen und Enums zur strengen Spezifikation des Inhalts. <\/li>\n<li><strong>Hash-Funktion<\/strong> f\u00fcr Sharding \u2013 die Struktur der Kafka-Nachricht ist key-value und basierend auf dem Hash des Keys wird berechnet, wo dies abgelegt werden soll.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Warum<\/h3>\n<p>\nWir arbeiten in einem gro\u00dfen Unternehmen mit einem etablierten Prozess. Warum etwas \u00e4ndern? <strong>Dies ist ein Experiment<\/strong>, und wir rechnen damit, mehrere Vorteile zu erzielen.<\/p>\n<h4>1:n+1-Austausch (ein zu vielen)<\/h4>\n<p>\nMit Kafka ist es sehr einfach, neue Verbraucher an die API anzuschlie\u00dfen. <\/p>\n<p>Angenommen, Sie haben ein Verzeichnis, das in mehreren Systemen gleichzeitig aktuell gehalten werden muss (und in einigen neuen). Fr\u00fcher haben wir ein Bundle erfunden, das ein Set-API implementierte, und der Master-System sagte die Adressen der Verbraucher. Jetzt sendet das Master-System Updates in das Topic, und alle, die interessiert sind, lesen. Es gibt ein neues System \u2013 wir haben es auf das Topic angemeldet. Ja, ebenfalls ein Bundle, aber einfacher.<\/p>\n<p>Im Fall des Refund-Tools, das einen Teil von BOB darstellt, ist es f\u00fcr uns praktisch, diese \u00fcber Kafka synchronisiert zu halten. Die Zahlungsabwicklung teilt mit, dass das Geld zur\u00fcckerstattet wurde: BOB und RT erfahren dar\u00fcber, \u00e4ndern ihre Status, und der Fiscalization Service wird informiert und erzeugt den Beleg.<\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b01b22a333b58e87aeef0c52d40e6960.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nWir planen, einen einheitlichen Notifications Service zu schaffen, der die Kunden \u00fcber Neuigkeiten zu ihren Bestellungen\/R\u00fcckgaben informiert. Momentan ist diese Verantwortung auf mehrere Systeme verteilt. Es reicht aus, den Notifications Service zu schulen, damit er relevante Informationen aus Kafka abgreift und darauf reagiert (und diese Benachrichtigungen in anderen Systemen zu deaktivieren). Es sind keine neuen direkten Austauschmechanismen erforderlich.<\/p>\n<h4>Datengetrieben<\/h4>\n<p>\nDie Informationen zwischen den Systemen werden transparent \u2013 egal welches \u201eblutige Unternehmen\u201c Sie haben oder wie umfangreich Ihr Backlog ist. Bei Lamoda gibt es eine Abteilung f\u00fcr Datenanalyse, die Daten aus den Systemen sammelt und sie in wiederverwendbare Formate f\u00fcr sowohl das Gesch\u00e4ft als auch die intelligenten Systeme bringt. Kafka erm\u00f6glicht es, ihnen schnell eine gro\u00dfe Menge an Daten bereitzustellen und diesen Informationsfluss aktuell zu halten.<\/p>\n<h4>Replikationsprotokoll<\/h4>\n<p>\nNach dem Lesen verschwinden Nachrichten nicht, wie bei RabbitMQ. Wenn ein Ereignis gen\u00fcgend Informationen zur Verarbeitung enth\u00e4lt, haben wir eine Historie der letzten \u00c4nderungen am Objekt und, falls gew\u00fcnscht, die M\u00f6glichkeit, diese \u00c4nderungen anzuwenden.<\/p>\n<p>Die Aufbewahrungsfrist f\u00fcr das Replikationsprotokoll h\u00e4ngt von der Schreibintensit\u00e4t in diesem Thema ab. Kafka erm\u00f6glicht eine flexible Konfiguration der Speicherkapazit\u00e4ten sowohl zeitlich als auch hinsichtlich des Datenvolumens. F\u00fcr stark frequentierte Themen ist es wichtig, dass alle Verbraucher die Informationen rechtzeitig lesen, bevor sie verschwinden, selbst im Falle einer kurzfristigen Ausfallzeit. In der Regel k\u00f6nnen Daten \u00fcber\u00a0<strong>einige Tage<\/strong>gespeichert werden, was f\u00fcr den Support v\u00f6llig ausreichend ist. <\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e08dd384155289123ebee96430c2370.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNun folgt eine kurze Zusammenfassung der Dokumentation f\u00fcr diejenigen, die mit Kafka nicht vertraut sind (das Bild stammt ebenfalls aus der Dokumentation).<\/p>\n<p>In AMQP gibt es Warteschlangen: Wir schreiben Nachrichten in eine Warteschlange f\u00fcr den Verbraucher. In der Regel verarbeitet ein System mit derselben Gesch\u00e4ftslogik eine Warteschlange. Wenn mehrere Systeme benachrichtigt werden m\u00fcssen, kann die Anwendung so konfiguriert werden, dass sie in mehrere Warteschlangen schreibt oder einen Exchange mit einem Fanout-Mechanismus einrichtet, der sie selbst klont.<\/p>\n<p>In Kafka gibt es eine \u00e4hnliche Abstraktion <em>Thema<\/em>, in die Sie Nachrichten schreiben, die jedoch nach dem Lesen nicht verschwinden. Standardm\u00e4\u00dfig erhalten Sie beim Anschluss an Kafka alle Nachrichten und haben die M\u00f6glichkeit, den Punkt zu speichern, an dem Sie aufgeh\u00f6rt haben. Das hei\u00dft, Sie lesen sequenziell und k\u00f6nnen eine Nachricht ungesehen lassen, aber die ID speichern, von der aus Sie dann weiterlesen. Die ID, an der Sie aufgeh\u00f6rt haben, wird als Offset bezeichnet, und der Mechanismus ist das Commit-Offset. <\/p>\n<p>Dementsprechend k\u00f6nnen verschiedene Logiken implementiert werden. Beispielsweise existiert BOB in 4 Instanzen f\u00fcr verschiedene L\u00e4nder \u2013 Lamoda ist in Russland, Kasachstan, der Ukraine und Wei\u00dfrussland verf\u00fcgbar. Da sie getrennt bereitgestellt werden, haben sie leicht unterschiedliche Konfigurationen und ihre eigene Gesch\u00e4ftslogik. Wir geben in der Nachricht an, auf welches Land sie sich bezieht. Jeder BOB-Consumer in jedem Land liest mit unterschiedlichen groupIds, und wenn eine Nachricht nicht f\u00fcr ihn relevant ist, wird sie \u00fcbersprungen, d.h. der Offset wird sofort um +1 committed. Wenn dasselbe Thema von unserem Zahlungsdienst gelesen wird, geschieht dies mit einer separaten Gruppe, sodass sich die Offsets nicht \u00fcberschneiden.<\/p>\n<p><b>Anforderungen an Ereignisse:<\/b><\/p>\n<ul>\n<li><strong>Vollst\u00e4ndigkeit der Daten. <\/strong>Ich w\u00fcnschte, dass das Ereignis gen\u00fcgend Daten enth\u00e4lt, um verarbeitet werden zu k\u00f6nnen. <\/li>\n<\/ul>\n<p><\/p>\n<ul>\n<li><strong>Integrit\u00e4t. <\/strong>Wir delegieren die \u00dcberpr\u00fcfung der Konsistenz des Ereignisses an den Events-Bus, der es verarbeiten kann.<\/li>\n<li><strong>Die Reihenfolge ist wichtig. <\/strong>Im Falle von R\u00fccksendungen m\u00fcssen wir mit der Historie arbeiten. Bei Benachrichtigungen ist die Reihenfolge nicht wichtig, solange es sich um einheitliche Benachrichtigungen handelt; die E-Mail bleibt gleich, unabh\u00e4ngig davon, welcher Auftrag zuerst kommt. Bei R\u00fccksendungen gibt es einen klaren Prozess, und wenn man die Reihenfolge \u00e4ndert, k\u00f6nnen Ausnahmen auftreten, und die R\u00fcckerstattung wird m\u00f6glicherweise nicht erstellt oder verarbeitet \u2013 wir gelangen in einen anderen Status.<\/li>\n<li><strong>Konsistenz. <\/strong>Wir haben ein Repository und erstellen jetzt anstelle einer API Ereignisse. Wir ben\u00f6tigen eine M\u00f6glichkeit, schnell und kosteng\u00fcnstig Informationen \u00fcber neue Ereignisse und \u00c4nderungen an bestehenden in unsere Dienste zu \u00fcbertragen. Dies wird durch eine gemeinsame Spezifikation in einem separaten Git-Repository und Code-Generatoren erreicht. Dadurch sind unsere Kunden- und Serverdienste konsistent.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Kafka bei Lamoda<\/h2>\n<p>\nWir haben drei Kafka-Installationen: <\/p>\n<ol>\n<li>Logs;<\/li>\n<li>Forschung und Entwicklung;<\/li>\n<li>Events-Bus.<\/li>\n<\/ol>\n<p>\nHeute sprechen wir ausschlie\u00dflich \u00fcber den letzten Punkt. In unserem Events-Bus haben wir keine gro\u00dfen Installationen \u2013 3 Broker (Server) und insgesamt 27 Topics. In der Regel entspricht ein Topic einem Prozess. Aber das ist ein feiner Punkt, und darauf werden wir jetzt eingehen.<\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/f398852689b31429cc97b4cbffcabab5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOben sehen Sie das rps-Diagramm. Der Refund-Prozess ist durch eine t\u00fcrkisfarbene Linie markiert (ja, die, die auf der X-Achse liegt), und der Inhaltsaktualisierungsprozess ist durch eine rosafarbene Linie angezeigt. <\/p>\n<p>Der Katalog von Lamoda enth\u00e4lt Millionen von Artikeln, und die Daten werden st\u00e4ndig aktualisiert. Einige Kollektionen geraten aus der Mode, w\u00e4hrend neue herausgebracht werden, und im Katalog tauchen st\u00e4ndig neue Modelle auf. Wir versuchen vorherzusagen, was unseren Kunden morgen gefallen wird, weshalb wir st\u00e4ndig neue Artikel einkaufen, diese fotografieren und das Schaufenster aktualisieren. <\/p>\n<p>Die rosafarbenen Spitzen repr\u00e4sentieren Produktupdates, also \u00c4nderungen an den Artikeln. Man sieht, dass die Leute fotografiert haben, fotografiert haben, und dann pl\u00f6tzlich! \u2013 eine Flut von Ereignissen hochgeladen wurde.<\/p>\n<h2>Lamoda Events Anwendungsf\u00e4lle<\/h2>\n<p>\nDie aufgebaute Architektur verwenden wir f\u00fcr folgende Operationen:<\/p>\n<ul>\n<li><strong>Verfolgung des R\u00fcckgabestatus<\/strong>: Call-to-Action und Statusverfolgung aller beteiligten Systeme. Zahlungen, Status, Fiscalit\u00e4tspr\u00fcfung, Benachrichtigungen. Hier haben wir einen Ansatz ausprobiert, Werkzeuge entwickelt, alle Fehler gesammelt, Dokumentation geschrieben und unseren Kollegen erkl\u00e4rt, wie man sie verwendet.<\/li>\n<li><strong>Aktualisierung der Produktkarten: <\/strong>Konfiguration, Metadaten, Spezifikationen. Eine System liest (das angezeigt wird), mehrere schreiben.<\/li>\n<li><strong>E-Mail, Push und SMS.<\/strong>: Bestellung zusammengestellt, Bestellung angekommen, R\u00fccksendung akzeptiert usw., viele davon. <\/li>\n<li><strong>Lagerbestand, Aktualisierung des Bestands.<\/strong>\u00a0\u2010 quantitative Aktualisierung von Artikeln, einfache Zahlen: Eingang im Lager, R\u00fccksendung. Es ist wichtig, dass alle Systeme, die mit der Reservierung von Produkten verbunden sind, mit den aktuellsten Daten arbeiten. Momentan ist das System zur Bestandsaktualisierung recht komplex, Kafka wird helfen, es zu vereinfachen.<\/li>\n<li><strong>Datenanalyse<\/strong> (F&amp;E-Abteilung), ML-Tools, Analytik, Statistik. Wir m\u00f6chten, dass Informationen transparent sind \u2013 daf\u00fcr eignet sich Kafka gut.<\/li>\n<\/ul>\n<p>\nJetzt der spannendere Teil \u00fcber die gemachten Fehler und interessanten Entdeckungen, die in den letzten sechs Monaten gemacht wurden.<\/p>\n<h2>Entwurfsprobleme<\/h2>\n<p>\nAngenommen, wir m\u00f6chten etwas Neues machen \u2013 zum Beispiel den gesamten Lieferprozess auf Kafka umzustellen. Momentan wird ein Teil des Prozesses im Order Processing in BOB umgesetzt. Hinter der Auftrags\u00fcbergabe an den Lieferservice, dem Transport zum Zwischenlager und weiteren Aspekten steht ein Statusmodell. Es gibt ein ganzes Monolith, sogar zwei, plus eine Menge APIs, die sich mit der Lieferung besch\u00e4ftigen. Sie wissen viel mehr \u00fcber die Lieferung. <\/p>\n<p>Es scheint, dass es sich um \u00e4hnliche Bereiche handelt, aber f\u00fcr das Order Processing in BOB und f\u00fcr das Liefersystem unterscheiden sich die Status. Einige Kurierdienste kommunizieren keine Zwischenstatus, sondern nur die endg\u00fcltigen: \u201ezugestellt\u201c oder \u201everloren\u201c. Andere hingegen berichten sehr detailliert \u00fcber den Transport des Produkts. Jeder hat seine eigenen Validierungsregeln: F\u00fcr manche ist eine g\u00fcltige E-Mail ausreichend, um sie zu bearbeiten; f\u00fcr andere ist sie nicht g\u00fcltig, aber die Bestellung wird trotzdem bearbeitet, weil es eine Telefonnummer gibt, um Kontakt aufzunehmen, w\u00e4hrend jemand anderes sagt, dass solche Bestellungen gar nicht bearbeitet werden.<\/p>\n<h3>Datenstrom<\/h3>\n<p>\nIm Fall von Kafka stellt sich die Frage der Organisation des Datenflusses. Diese Aufgabe h\u00e4ngt mit der Wahl einer Strategie in mehreren Punkten zusammen; lassen Sie uns alle durchgehen.<\/p>\n<h4>In ein Thema oder in verschiedene?<\/h4>\n<p>\nWir haben eine Ereignisspezifikation. In BOB notieren wir, dass eine bestimmte Bestellung geliefert werden muss, und geben an: die Bestellnummer, ihren Inhalt, einige SKUs und Barcodes usw. Wenn die Ware im Lager ankommt, kann die Lieferung Status, Zeitstempel und alles andere, was ben\u00f6tigt wird, erhalten. Aber dann m\u00f6chten wir in BOB Updates zu diesen Daten erhalten. Dadurch entsteht ein umgekehrter Prozess zum Abrufen von Daten aus der Lieferung. Ist das dasselbe Ereignis? Oder ist es ein separater Austausch, der ein eigenes Thema verdient?<\/p>\n<p>Wahrscheinlich werden sie sehr \u00e4hnlich sein, und die Versuchung, ein einziges Thema zu erstellen, ist nicht unbegr\u00fcndet, denn ein separates Thema bedeutet separate Verbraucher, separate Konfigurationen und eine separate Generierung von all dem. Aber das ist nicht sicher.<\/p>\n<h4>Neues Feld oder neues Ereignis?<\/h4>\n<p>\nAber wenn wir die gleichen Ereignisse verwenden, entsteht ein anderes Problem. Beispielsweise k\u00f6nnen nicht alle Liefersysteme ein solches DTO erzeugen, das BOB generieren kann. Wir senden ihnen die ID, und sie speichern sie nicht, weil sie sie nicht ben\u00f6tigen, aber aus der Sicht des Startprozesses des Event-Bus ist dieses Feld zwingend erforderlich. <\/p>\n<p>Wenn wir f\u00fcr den Event-Bus die Regel festlegen, dass dieses Feld obligatorisch ist, m\u00fcssen wir entweder im BOB oder im Handler des Start-Events zus\u00e4tzliche Validierungsregeln aufstellen. Die Validierung beginnt sich im gesamten Dienst auszubreiten, was nicht sehr praktisch ist.<\/p>\n<p>Ein weiteres Problem ist die Versuchung zur inkrementellen Entwicklung. Man sagt uns, dass wir etwas zum Event hinzuf\u00fcgen m\u00fcssen und vielleicht, wenn wir gut dar\u00fcber nachdenken, h\u00e4tte es ein eigenes Event sein sollen. In unserem Schema ist ein eigenes Event jedoch ein eigenes Topic. Ein eigenes Topic umfasst den gesamten Prozess, den ich oben beschrieben habe. Der Entwickler hat die Versuchung, einfach ein weiteres Feld in das JSON-Schema einzuf\u00fcgen und neu zu generieren.<\/p>\n<p>Im Fall von Refunds haben wir innerhalb von einem halben Jahr zu einem Ereignis von Ereignissen gelangt. Wir hatten ein Meta-Ereignis, das als Refund-Update bezeichnet wird und ein Feld vom Typ enth\u00e4lt, das beschreibt, worin eigentlich dieses Update besteht. Darauf basierend hatten wir \"pr\u00e4chtig\" funktionierende Switches mit Validatoren, die angaben, wie dieses Event mit diesem Typ validiert werden sollte.<\/p>\n<h4>Versionierung von Ereignissen<\/h4>\n<p>\nZur Validierung von Nachrichten in Kafka kann man nutzen <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.confluent.io\/current\/schema-registry\/docs\/index.html\">Avro<\/a><\/noindex>, aber man h\u00e4tte sofort daf\u00fcr planen und Confluent nutzen m\u00fcssen. In unserem Fall bez\u00fcglich der Versionierung m\u00fcssen wir vorsichtig sein. Es wird nicht immer m\u00f6glich sein, die Nachrichten aus dem Replikationsprotokoll erneut zu lesen, da das Modell \u201eweggegangen\u201c ist. Es gelingt uns meist, Versionen so zu erstellen, dass das Modell abw\u00e4rtskompatibel ist: zum Beispiel, indem wir ein Feld vor\u00fcbergehend optional machen. Wenn die Unterschiede zu gro\u00df sind, beginnen wir, in ein neues Thema zu schreiben, und migrieren die Kunden, wenn sie das alte gelesen haben.<\/p>\n<h4>Garantie der Lesereihenfolge f\u00fcr Partitions<\/h4>\n<p>\nTopics innerhalb von Kafka sind in Partitions aufgeteilt. Das ist nicht sehr wichtig, solange wir die Entit\u00e4ten und den Austausch entwerfen, aber entscheidend, wenn wir bestimmen, wie wir konsumieren und skalieren.<\/p>\n<p>In der Regel schreiben Sie in Kafka ein Topic. Standardm\u00e4\u00dfig wird eine Partition verwendet, und alle Nachrichten dieses Topics landen in ihr. Der Consumer liest diese Nachrichten dann der Reihe nach. Angenommen, es ist erforderlich, das System zu erweitern, damit zwei verschiedene Consumer die Nachrichten lesen k\u00f6nnen. Wenn Sie beispielsweise eine SMS senden, k\u00f6nnen Sie Kafka anweisen, eine zus\u00e4tzliche Partition zu erstellen, sodass Kafka die Nachrichten in zwei Teile aufteilt \u2013 die eine H\u00e4lfte hierhin, die andere dorthin. <\/p>\n<p>Wie teilt Kafka sie auf? Jede Nachricht hat einen Body (in dem wir JSON speichern) und einen Key. Zu diesem Schl\u00fcssel kann eine Hash-Funktion angewendet werden, die bestimmt, in welche Partition die Nachricht gelangt.<\/p>\n<p>In unserem Fall mit Refunds ist das wichtig. Wenn wir zwei Partitionen haben, besteht die M\u00f6glichkeit, dass der parallele Consumer das zweite Ereignis fr\u00fcher verarbeitet als das erste, was problematisch sein k\u00f6nnte. Die Hash-Funktion gew\u00e4hrleistet, dass Nachrichten mit demselben Schl\u00fcssel in dieselbe Partition gelangen. <\/p>\n<h4>Events vs. Commands<\/h4>\n<p>\nDies ist ein weiteres Problem, mit dem wir konfrontiert sind. Ein Event ist ein Ereignis: wir sagen, dass etwas irgendwo passiert ist (something_happened), zum Beispiel, dass ein Artikel storniert oder eine R\u00fcckerstattung erfolgt ist. Wenn jemand diese Ereignisse anh\u00f6rt, wird bei \"Artikel storniert\" eine R\u00fcckerstattung geschaffen, und \"es hat eine R\u00fcckerstattung gegeben\" wird irgendwo in den Setups aufgezeichnet.<\/p>\n<p>Aber normalerweise, wenn Sie Ereignisse entwerfen, wollen Sie sie nicht umsonst schreiben \u2013 Sie gehen davon aus, dass sie jemand lesen wird. Der Anreiz, nicht something_happened (item_canceled, refund_refunded) zu schreiben, sondern something_should_be_done, ist gro\u00df. Zum Beispiel, der Artikel ist bereit zur R\u00fcckgabe.<\/p>\n<p>Einerseits gibt es Hinweise darauf, wie das Ereignis verwendet werden wird. Andererseits sieht es viel weniger nach einem normalen Ereignisnamen aus. Au\u00dferdem ist es von hier nicht weit zu einem Befehl do_something. Aber Sie haben keine Garantie, dass dieses Ereignis jemand gelesen hat; und wenn es jemand gelesen hat, dann erfolgreich; und wenn es erfolgreich gelesen wurde, hat er etwas getan, und dieses Etwas war erfolgreich. In dem Moment, in dem das Ereignis zu do_something wird, ist Feedback notwendig, und das ist das Problem.<\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b755d91208092bd9791a41ce4633fb48.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIm asynchronen Austausch in RabbitMQ, wenn Sie eine Nachricht gelesen haben und eine HTTP-Anfrage gestellt haben, erhalten Sie eine R\u00fcckmeldung \u2014 mindestens, dass die Nachricht akzeptiert wurde. Wenn Sie in Kafka etwas aufgezeichnet haben, gibt es eine Nachricht, dass Sie in Kafka geschrieben haben, aber wie es verarbeitet wurde, bleibt unklar. <\/p>\n<p>Deshalb mussten wir in unserem Fall ein Antwortereignis einf\u00fchren und das Monitoring so einstellen, dass wenn eine bestimmte Anzahl von Ereignissen innerhalb eines bestimmten Zeitrahmens auftritt, auch die gleiche Anzahl von Antwortereignissen eintreffen sollte. Wenn das nicht passiert, sieht es so aus, als w\u00e4re etwas schiefgegangen. Beispielsweise, wenn wir das Ereignis 'item_ready_to_refund' senden, erwarten wir, dass eine R\u00fcckerstattung erstellt wird, das Geld an den Kunden zur\u00fcckgegeben wird und wir das Ereignis 'money_refunded' erhalten. Aber das ist nicht sicher, daher ist Monitoring erforderlich.<\/p>\n<h3>Nuancen<\/h3>\n<p>\nEs gibt ein ziemlich offensichtliches Problem: Wenn Sie sequenziell von einem Thema lesen und eine meiner Nachrichten schlecht ist, f\u00e4llt der Consumer aus, und Sie kommen nicht weiter. Sie m\u00fcssen <strong>alle Consumer stoppen<\/strong>, den Offset weiter committen, um fortzufahren.<\/p>\n<p>Wir wussten dar\u00fcber Bescheid, hatten damit gerechnet, und dennoch ist es passiert. Es passierte, weil das Ereignis aus der Sicht des Event-Busses g\u00fcltig war, das Ereignis aus der Sicht des Anwendungsvalidators g\u00fcltig war, aber es war nicht aus der Sicht von PostgreSQL g\u00fcltig, da wir in einem System MySQL mit UNSIGNED INT hatten, w\u00e4hrend im neu geschriebenen System PostgreSQL nur INT verwendet wurde. Letzteres hat eine etwas kleinere Gr\u00f6\u00dfe, und die ID passte nicht. Symfony ist mit einer Ausnahme abgest\u00fcrzt. Nat\u00fcrlich haben wir die Ausnahme gefangen, weil wir darauf vorbereitet waren und vorhatten, diesen Offset zu committen, aber zuvor wollten wir den Fehlerz\u00e4hler erh\u00f6hen, da die Nachricht nicht erfolgreich verarbeitet wurde. Die Z\u00e4hler in diesem Projekt liegen ebenfalls in der Datenbank, w\u00e4hrend Symfony bereits die Kommunikation mit der Datenbank geschlossen hatte, und eine zweite Ausnahme hat den gesamten Prozess ohne Chance auf das Commit des Offsets beendet.<\/p>\n<p>Die Dienstleistung lag eine Zeit lang brach \u2013 gl\u00fccklicherweise ist das bei Kafka nicht so schlimm, da die Nachrichten erhalten bleiben. Wenn die Arbeit wiederhergestellt wird, k\u00f6nnen sie nachgelesen werden. Das ist praktisch.<\/p>\n<p>Mit Kafka besteht die M\u00f6glichkeit, \u00fcber Tools einen beliebigen Offset zu setzen. Um dies zu tun, m\u00fcssen jedoch alle Consumer gestoppt werden \u2013 in unserem Fall bedeutet das, einen speziellen Release vorzubereiten, der keine Consumer enth\u00e4lt, um dann neu zu deployen. So kann der Offset \u00fcber das Tooling in Kafka verschoben werden, und die Nachricht wird verarbeitet.<\/p>\n<p>Ein weiterer Punkt \u2013 <strong>Wiederholungsprotokoll vs rdkafka.so<\/strong>\u00a0\u2013 h\u00e4ngt mit den spezifischen Anforderungen unseres Projekts zusammen. Wir nutzen PHP, und in PHP kommunizieren in der Regel alle Bibliotheken \u00fcber das rdkafka.so-Repository mit Kafka, woraufhin eine Art Wrapper folgt. Vielleicht sind das unsere pers\u00f6nlichen Schwierigkeiten, aber es stellte sich heraus, dass es alles andere als einfach ist, einen bereits gelesenen Abschnitt erneut zu lesen. Kurz gesagt, wir hatten einige Softwareprobleme.<\/p>\n<p>Wenn wir zu den Besonderheiten der Arbeit mit Partitions zur\u00fcckkehren, steht direkt in der Dokumentation <strong>Consumer &gt;= Topic-Partitionen<\/strong>. Ich erfuhr jedoch viel sp\u00e4ter davon, als ich es mir gew\u00fcnscht h\u00e4tte. Wenn Sie skalieren m\u00f6chten und zwei Consumer haben wollen, ben\u00f6tigen Sie mindestens zwei Partitionen. Das hei\u00dft, wenn Sie eine Partition hatten, in der sich 20.000 Nachrichten angesammelt haben, und Sie eine neue erstellt haben, wird die Anzahl der Nachrichten nicht sofort gleichm\u00e4\u00dfig verteilt sein. Daher m\u00fcssen Sie sich mit Partitionen auseinandersetzen, um zwei parallele Consumer zu haben.<\/p>\n<h2>\u00dcberwachung<\/h2>\n<p>\nIch denke, dass es durch unsere \u00dcberwachung noch deutlicher wird, welche Probleme im aktuellen Ansatz bestehen.<\/p>\n<p>Zum Beispiel z\u00e4hlen wir, wie viele Produkte in der Datenbank k\u00fcrzlich den Status ge\u00e4ndert haben. Entsprechend h\u00e4tten auf Basis dieser \u00c4nderungen Ereignisse stattfinden m\u00fcssen, und diese Zahl senden wir an unser \u00dcberwachungssystem. Anschlie\u00dfend erhalten wir aus Kafka die tats\u00e4chliche Anzahl der aufgezeichneten Ereignisse. Offensichtlich sollte die Differenz zwischen diesen beiden Zahlen immer null sein.<\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/07d8ed08514f2fb97d9019466b96342c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAu\u00dferdem m\u00fcssen wir \u00fcberwachen, wie es dem Producer geht, ob der Events-Bus Nachrichten empfangen hat, und wie es dem Consumer geht. Auf den Diagrammen unten l\u00e4uft alles gut bei dem Refund Tool, aber bei BOB gibt es offensichtlich Probleme (blaue Spitzen).<\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/57112dbe70d2b388c53f19c34cca6f63.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIch habe bereits \u00fcber den Consumer-Group-Lag gesprochen. Grob gesagt, ist das die Anzahl der ungelesenen Nachrichten. Generell arbeiten unsere Consumer schnell, sodass der Lag normalerweise 0 betr\u00e4gt, aber es kann manchmal einen kurzfristigen Anstieg geben. Kafka kann das out-of-the-box, aber es muss ein gewisser Zeitraum festgelegt werden. <\/p>\n<p>Es gibt ein Projekt <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/linkedin\/Burrow\">Burrow<\/a><\/noindex>, der Ihnen mehr Informationen zu Kafka gibt. Er liefert einfach den Status \u00fcber die API f\u00fcr die Consumer-Gruppe, wie es dieser Gruppe geht. Neben OK und Failed gibt es auch Warnungen, und Sie k\u00f6nnen erfahren, dass Ihre Consumer Schwierigkeiten haben, mit dem Tempo der Produktion Schritt zu halten \u2013 sie schaffen es nicht, das Geschriebene rechtzeitig zu verarbeiten. Das System ist ziemlich intelligent und einfach zu verwenden. <\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/cece8495801e187b155487a802e1a35a.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nSo sieht die Antwort \u00fcber die API aus. Hier ist die Gruppe bob-live-fifa, Partition refund.update.v1, Status OK, Lag 0 \u2013 der letzte finale Offset ist der folgende.<\/p>\n<p><img decoding=\"async\" alt=\"Erfahrung in der Entwicklung des Refund Tool-Dienstes mit asynchroner API auf Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/1538b2ccc390e9b83075f54566f1039b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n\u00dcberwachung <strong>updated_at SLA (stuck)<\/strong> Ich habe bereits erw\u00e4hnt. Zum Beispiel hat das Produkt den Status erreicht, dass es f\u00fcr die R\u00fccksendung bereit ist. Wir setzen einen Cron ein, der sagt, dass wenn dieses Objekt innerhalb von 5 Minuten nicht in den Refund-Status \u00fcbergegangen ist (wir erstatten das Geld \u00fcber Zahlungssysteme sehr schnell), dann ist definitiv etwas schiefgelaufen, und es ist ein klarer Fall f\u00fcr den Support. Daher nutzen wir einfach einen Cron, der solche Dinge \u00fcberwacht, und wenn sie gr\u00f6\u00dfer als 0 sind, wird eine Alert-Nachricht gesendet.<\/p>\n<p><b>Zusammenfassend l\u00e4sst sich sagen, dass es praktisch ist, Ereignisse zu nutzen, wenn<\/b>:<\/p>\n<ul>\n<li>mehrere Systeme Informationen ben\u00f6tigen;<\/li>\n<li>das Ergebnis der Verarbeitung nicht wichtig ist;<\/li>\n<li>es wenige Ereignisse gibt oder die Ereignisse klein sind. <\/li>\n<\/ul>\n<blockquote><p>Auf den ersten Blick scheint der Artikel ein ganz konkretes Thema zu haben \u2013 die asynchrone API auf Kafka. Doch in diesem Zusammenhang gibt es gleich vieles, was ich empfehlen m\u00f6chte.<br \/>\nZun\u00e4chst <noindex><a rel=\"nofollow\" href=\"https:\/\/www.highload.ru\/\">HighLoad++<\/a><\/noindex> , man muss bis November warten, doch bereits im April wird es eine Version in St. Petersburg geben, und im Juni werden wir \u00fcber hohe Lasten in Nowosibirsk sprechen.<br \/>\nZweitens, der Referent Sergej Zaika ist Mitglied des Programmkomitees unserer neuen Konferenz \u00fcber Wissensmanagement <noindex><a rel=\"nofollow\" href=\"https:\/\/knowledgeconf.ru\/2019\">KnowledgeConf<\/a><\/noindex>. Die Konferenz ist eint\u00e4gig und findet am 26. April statt, jedoch hat das Programm viel zu bieten.<br \/>\nAu\u00dferdem wird es im Mai <noindex><a rel=\"nofollow\" href=\"https:\/\/phprussia.ru\/2019\">PHP Russland<\/a><\/noindex> und\u00a0<noindex><a rel=\"nofollow\" href=\"https:\/\/ritfest.ru\/2019\">RIT++<\/a><\/noindex> geben (zusammen mit DevOpsConf) \u2013 dort kann man ebenfalls Themen einreichen, von seinen Erfahrungen berichten und \u00fcber seine gesammelten R\u00fcckschl\u00e4ge klagen.<\/p><\/blockquote>\n<p>Quelle: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/oleg-bunin\/blog\/445424\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441\u00a0\u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438\u00a0\u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434? \u041c\u043e\u0442\u0438\u0432\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u0430\u044f: \u043e\u0442\u00a0\u0437\u0430\u043a\u043e\u043d\u043e\u0434\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u00a0\u043f\u0440\u0438\u0441\u0443\u0449\u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041d\u043e\u00a0\u044d\u0442\u043e \u0432\u043e\u0432\u0441\u0435 \u043d\u0435\u00a0\u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u00a0\u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0432\u044b\u0433\u043e\u0434\u0443. \u0412\u00a0\u0447\u0435\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c events-driven API \u043d\u0430\u00a0Kafka, \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u0421\u0435\u0440\u0433\u0435\u0439 \u0417\u0430\u0438\u043a\u0430 (fewald). \u041f\u0440\u043e \u043d\u0430\u0431\u0438\u0442\u044b\u0435 \u0448\u0438\u0448\u043a\u0438 \u0438\u00a0\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u043e\u0436\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":22763,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-30778","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441 \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434? \u041c\u043e\u0442\u0438\u0432\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u0430\u044f: \u043e\u0442 \u0437\u0430\u043a\u043e\u043d\u043e\u0434\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0434\u043e \u043f\u0440\u0438\u0441\u0443\u0449\u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041d\u043e \u044d\u0442\u043e \u0432\u043e\u0432\u0441\u0435 \u043d\u0435 \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0432\u044b\u0433\u043e\u0434\u0443. \u0412 \u0447\u0435\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c events-driven API \u043d\u0430 Kafka, \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u0421\u0435\u0440\u0433\u0435\u0439 \u0417\u0430\u0438\u043a\u0430 (fewald). \u041f\u0440\u043e \u043d\u0430\u0431\u0438\u0442\u044b\u0435 \u0448\u0438\u0448\u043a\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u043e\u0436\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041e\u043f\u044b\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 Refund Tool \u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c API \u043d\u0430 Kafka | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441 \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434? \u041c\u043e\u0442\u0438\u0432\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u0430\u044f: \u043e\u0442 \u0437\u0430\u043a\u043e\u043d\u043e\u0434\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0434\u043e \u043f\u0440\u0438\u0441\u0443\u0449\u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041d\u043e \u044d\u0442\u043e \u0432\u043e\u0432\u0441\u0435 \u043d\u0435 \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0432\u044b\u0433\u043e\u0434\u0443. \u0412 \u0447\u0435\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c events-driven API \u043d\u0430 Kafka, \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u0421\u0435\u0440\u0433\u0435\u0439 \u0417\u0430\u0438\u043a\u0430 (fewald). \u041f\u0440\u043e \u043d\u0430\u0431\u0438\u0442\u044b\u0435 \u0448\u0438\u0448\u043a\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u043e\u0436\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T18:37:23+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:37:23+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Erfahrungen bei der Entwicklung des Refund Tool mit asynchronem API auf Kafka | ProHoster","description":"Was kann ein so gro\u00dfes Unternehmen wie Lamoda mit einem etablierten Prozess und Dutzenden vernetzter Dienste dazu bringen, seinen Ansatz grundlegend zu \u00e4ndern? Die Motive k\u00f6nnen ganz unterschiedlich sein: von gesetzlichen Anforderungen bis hin zu dem f\u00fcr Programmierer typischen Wunsch zu experimentieren. Doch das bedeutet nicht, dass man auf zus\u00e4tzliche Vorteile verzichten sollte. Welche konkreten Gewinne durch die Implementierung einer ereignisgesteuerten API auf Kafka erzielt werden k\u00f6nnen, erl\u00e4utert Sergey Zaika (fewald). Auch von den gemachten Erfahrungen und interessanten Entdeckungen wird er berichten.","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041e\u043f\u044b\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 Refund Tool \u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c API \u043d\u0430 Kafka | ProHoster","og:description":"\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441 \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434? \u041c\u043e\u0442\u0438\u0432\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u0430\u044f: \u043e\u0442 \u0437\u0430\u043a\u043e\u043d\u043e\u0434\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0434\u043e \u043f\u0440\u0438\u0441\u0443\u0449\u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041d\u043e \u044d\u0442\u043e \u0432\u043e\u0432\u0441\u0435 \u043d\u0435 \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0432\u044b\u0433\u043e\u0434\u0443. \u0412 \u0447\u0435\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c events-driven API \u043d\u0430 Kafka, \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u0421\u0435\u0440\u0433\u0435\u0439 \u0417\u0430\u0438\u043a\u0430 (fewald). \u041f\u0440\u043e \u043d\u0430\u0431\u0438\u0442\u044b\u0435 \u0448\u0438\u0448\u043a\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u043e\u0436\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e","og:url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T18:37:23+00:00","article:modified_time":"2019-10-31T18:37:23+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"30778","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-21 02:58:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 03:29:56","updated":"2026-01-21 02:58:19"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/30778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=30778"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/30778\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/22763"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=30778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=30778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=30778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}