{"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\/sv\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","title":{"rendered":"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Vad kan f\u00e5 ett s\u00e5 stort f\u00f6retag som Lamoda med en v\u00e4l fungerande process och dussintals sammanl\u00e4nkade tj\u00e4nster att verkligen \u00e4ndra sitt tillv\u00e4gag\u00e5ngss\u00e4tt? Motivation kan vara helt olika: allt fr\u00e5n lagkrav till det som alla programmerare har en inneboende lust att experimentera.<\/p>\n<p>Men det betyder inte att man inte kan f\u00f6rv\u00e4nta sig extra vinster. Hur man kan vinna konkret om man implementerar ett events-driven API p\u00e5 Kafka kommer Sergey Zaika att ber\u00e4tta (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/fewald\/\" class=\"user_link\">fewald<\/a><\/noindex>). Det kommer ocks\u00e5 att finnas ber\u00e4ttelser om l\u00e4rdomar och intressanta uppt\u00e4ckter \u2013 ett experiment kan inte undvika s\u00e5dana.<\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/7ab959ab45ec5c6565b35b18b361c0ea.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<em>Disclaimer: Denna artikel \u00e4r baserad p\u00e5 material fr\u00e5n en meetup som Sergey h\u00f6ll i november 2018 p\u00e5 HighLoad++. Den verkliga erfarenheten av Lamodas arbete med Kafka lockade lyssnare lika mycket som andra presentationer p\u00e5 schemat. Vi tycker att detta \u00e4r ett utm\u00e4rkt exempel p\u00e5 att man alltid kan och b\u00f6r hitta likasinnade, och att arrang\u00f6rerna av HighLoad++ kommer att forts\u00e4tta att skapa en atmosf\u00e4r som uppmuntrar till detta.<\/em><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Om processen<\/h2>\n<p>\nLamoda \u00e4r en stor e-handelplattform som har sitt eget kontaktcenter, leveranstj\u00e4nst (och m\u00e5nga partners), fotostudio, en stor lager och allt detta fungerar p\u00e5 egen mjukvara. Det finns dussintals betalningsmetoder, b2b-partners som kan anv\u00e4nda en del eller alla dessa tj\u00e4nster och vill veta aktuell information om sina produkter. Dessutom verkar Lamoda i tre l\u00e4nder ut\u00f6ver Ryssland, och d\u00e4r fungerar allt lite annorlunda. Totalt finns det nog mer \u00e4n hundra s\u00e4tt att konfigurera en ny best\u00e4llning som m\u00e5ste bearbetas p\u00e5 sitt eget s\u00e4tt. Allt detta fungerar med hj\u00e4lp av dussintals tj\u00e4nster som ibland kommunicerar p\u00e5 ett otydligt s\u00e4tt. Det finns ocks\u00e5 ett centralt system vars huvudansvar \u00e4r orderstatusar. Vi kallar det BOB, jag arbetar med det.<\/p>\n<h2>Refund Tool with events-driven API <\/h2>\n<p>\nOrdet events-driven \u00e4r ganska slitet, l\u00e4ngre fram kommer vi att definiera mer specifikt vad som menas med detta. Jag b\u00f6rjar med kontexten d\u00e4r vi beslutade att testa tillv\u00e4gag\u00e5ngss\u00e4ttet f\u00f6r events-driven API i Kafka. <\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/3bfdce47dd8420fc63d84645e76de647.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nI varje butik, ut\u00f6ver best\u00e4llningar som kunder betalar f\u00f6r, finns det tillf\u00e4llen d\u00e5 butiken f\u00f6rv\u00e4ntas \u00e5terbetala pengar, eftersom varan inte passade kunden. Denna relativt korta process inneb\u00e4r att vi klarg\u00f6r information, om det \u00e4r n\u00f6dv\u00e4ndigt, och \u00f6verf\u00f6r pengarna. <\/p>\n<p>Men \u00e5terbetalningsprocessen blev mer komplicerad p\u00e5 grund av f\u00f6r\u00e4ndringar i lagstiftningen, och vi var tvungna att implementera en separat mikrotj\u00e4nst f\u00f6r detta.<\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e358df5476e7448976e0f1147a103fb.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nV\u00e5r motivation:<\/p>\n<ol>\n<li><strong>Lag FZ-54<\/strong>\u00a0\u2014 kort sagt kr\u00e4ver lagen att varje penningtransaktion, oavsett om det \u00e4r en \u00e5terbetalning eller en inbetalning, rapporteras till skatteverket inom en ganska kort SLA p\u00e5 n\u00e5gra minuter. Vi, som e-handel, utf\u00f6r ganska m\u00e5nga transaktioner. Tekniskt sett inneb\u00e4r det ett nytt ansvar (och d\u00e4rmed en ny tj\u00e4nst) och f\u00f6r\u00e4ndringar i alla ber\u00f6rda system.<\/li>\n<li><strong>BOB split<\/strong>\u00a0\u2014 ett internt projekt f\u00f6r att avlasta BOB fr\u00e5n ett stort antal orelaterade ansvar och minska dess \u00f6vergripande komplexitet.<\/li>\n<\/ol>\n<p>\n<img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/d3ecf9961bdb372fc5f84ee9389f73ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nI detta diagram ritas de grundl\u00e4ggande systemen i Lamoda. F\u00f6r n\u00e4rvarande best\u00e5r de flesta av dessa snarare av <strong>ett stj\u00e4rnsystem av 5-10 mikrotj\u00e4nster runt en minskande monolit<\/strong>. De v\u00e4xer sakta, men vi f\u00f6rs\u00f6ker g\u00f6ra dem mindre, eftersom det \u00e4r skr\u00e4mmande att deploya en dedikerad del i mitten \u2014 vi f\u00e5r inte l\u00e5ta den krascha. Alla utbyten (pilarna) m\u00e5ste vi reservera och f\u00f6rbereda oss f\u00f6r att n\u00e5gon av dem kan bli otillg\u00e4nglig.<\/p>\n<p>Det finns ocks\u00e5 ganska m\u00e5nga utbyten i BOB: betalningssystem, transport, notifieringar, etc. <\/p>\n<p>Tekniskt \u00e4r BOB:<\/p>\n<ul>\n<li>~150k rader kod + ~100k rader tester;<\/li>\n<li>php7.2 + Zend 1 &amp; Symfony Components 3;<\/li>\n<li>&gt;100 API &amp; ~50 utg\u00e5ende integrationer;<\/li>\n<li>4 l\u00e4nder med sin egen aff\u00e4rslogik. <\/li>\n<\/ul>\n<p>\nAtt deploya BOB \u00e4r kostsamt och sm\u00e4rtsamt; m\u00e4ngden kod och de uppgifter som hanteras \u00e4r s\u00e5dana att ingen kan h\u00e5lla allt i huvudet p\u00e5 en g\u00e5ng. Sammanfattningsvis finns det m\u00e5nga anledningar att f\u00f6renkla det.<\/p>\n<h2>\u00c5terbetalningsprocessen<\/h2>\n<p>\nI processen \u00e4r tv\u00e5 system inblandade: BOB och Payment. Nu tillkommer ytterligare tv\u00e5:<\/p>\n<ul>\n<li>Fiscalization Service, som tar hand om problemen med fiscalisering och kommunikation med externa tj\u00e4nster.<\/li>\n<li>Refund Tool, d\u00e4r nya utbyten hanteras f\u00f6r att inte \u00f6ka storleken p\u00e5 BOB.<\/li>\n<\/ul>\n<p>\nNu ser processen ut s\u00e5 h\u00e4r:<\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/13c02975881ad35c61304053c604cda3.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ol>\n<li>En beg\u00e4ran om \u00e5terbetalning kommer till BOB.<\/li>\n<li>BOB informerar Refund Tool om detta.<\/li>\n<li>Refund Tool meddelar Payment: \"\u00c5terbetala pengarna\".<\/li>\n<li>Payment \u00e5terbetalar pengarna.<\/li>\n<li>Refund Tool och BOB synkroniserar statusar med varandra, eftersom b\u00e5da beh\u00f6ver det f\u00f6r tillf\u00e4llet. Vi \u00e4r dock inte redo att helt byta till Refund Tool, eftersom BOB har UI, rapporter f\u00f6r bokf\u00f6ring och generellt mycket data som inte kan flyttas s\u00e5 enkelt. Vi sitter fortfarande p\u00e5 tv\u00e5 stolar.<\/li>\n<li>En beg\u00e4ran om fiscalisering skickas.<\/li>\n<\/ol>\n<p>\nSom vi har gjort har vi skapat en event-bus baserad p\u00e5 Kafka, d\u00e4r allt \u00e4r kopplat. Hurra, nu har vi en enda felpunkt (sarkasm).<\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/674edd7972998b4985071f5250612c7e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nF\u00f6rdelar och nackdelar \u00e4r ganska uppenbara. Vi har skapat en bus, s\u00e5 nu \u00e4r alla tj\u00e4nster beroende av den. Detta f\u00f6renklar designen, men inf\u00f6r en enda felpunkt i systemet. Om Kafka faller, stannar processen.<\/p>\n<h2>Vad \u00e4r ett event-driven API <\/h2>\n<p>\nEtt bra svar p\u00e5 denna fr\u00e5ga finns i Martin Fowlers f\u00f6redrag (GOTO 2017) <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/STKCRSUsyPO\">\u00abThe Many Meanings of Event-Driven Architecture\u00bb<\/a><\/noindex>. <\/p>\n<p>Kort sagt, vad vi har gjort:<\/p>\n<ol>\n<li>Vi har kapslat in alla asynkrona utbyten genom <strong>events storage<\/strong>. Ist\u00e4llet f\u00f6r att informera varje intresserad konsument om status\u00e4ndringar via n\u00e4tverket, skriver vi ett evenemang om status\u00e4ndringen till ett centraliserat lagringsutrymme, och intresserade konsumenter l\u00e4ser d\u00e4rifr\u00e5n allt som dyker upp.<\/li>\n<li>Ett evenemang (event) i det h\u00e4r fallet \u00e4r en notifikation (<strong>notifications<\/strong>) om att n\u00e5got har f\u00f6r\u00e4ndrats n\u00e5gonstans. Till exempel, en best\u00e4llningsstatus har \u00e4ndrats. Konsumenten, som \u00e4r intresserad av vissa till\u00e4ggsdata relaterade till status\u00e4ndringen, och som inte finns i notifikationen, kan sj\u00e4lv ta reda p\u00e5 deras tillst\u00e5nd.<\/li>\n<li>Det maximala alternativet \u00e4r fullskalig event sourcing, <strong>state transfer<\/strong>, d\u00e4r evenemanget inneh\u00e5ller all information som beh\u00f6vs f\u00f6r bearbetning: vart vi kom ifr\u00e5n, vilken status vi \u00f6vergick till, hur data f\u00f6r\u00e4ndrades, och s\u00e5 vidare. Fr\u00e5gan kvarst\u00e5r dock om det \u00e4r rimligt och hur mycket information du kan till\u00e5ta dig att lagra.<\/li>\n<\/ol>\n<p>\nInom ramen f\u00f6r lanseringen av Refund Tool anv\u00e4nde vi det tredje alternativet. Detta f\u00f6renklade h\u00e4ndelsebehandlingen eftersom det inte beh\u00f6vdes samla in detaljerad information, och det utesl\u00f6t scenariot d\u00e4r varje nytt evenemang ger upphov till en v\u00e5g av uppf\u00f6ljande get-f\u00f6rfr\u00e5gningar fr\u00e5n konsumenterna.<\/p>\n<p>Tj\u00e4nsten Refund Tool <strong>\u00e4r inte belastad<\/strong>, s\u00e5 Kafka d\u00e4r \u00e4r mer en prov\u00f6vning \u00e4n ett krav. Jag tror inte att om tj\u00e4nsten f\u00f6r \u00e5terbetalning blev ett highload-projekt, skulle f\u00f6retaget vara glatt.<\/p>\n<h4>Async exchange AS IS<\/h4>\n<p>\nF\u00f6r asynkrona utbyten anv\u00e4nder PHP-avdelningen vanligtvis RabbitMQ. Vi samlade in data f\u00f6r f\u00f6rfr\u00e5gningen, la det i k\u00f6n, och konsumenten av samma tj\u00e4nst l\u00e4ste och skickade det (eller inte). F\u00f6r API:et anv\u00e4nder Lamoda aktivt Swagger. Vi designar API:et, beskriver det i Swagger, och genererar klient- och serverkod. Vi anv\u00e4nder \u00e4ven en n\u00e5got utvidgad JSON RPC 2.0. <\/p>\n<p>ESB-bussar anv\u00e4nds h\u00e4r och d\u00e4r, vissa lever p\u00e5 ActiveMQ, men i stort sett, <strong>RabbitMQ - standard<\/strong>.<\/p>\n<h4>Asynk v\u00e4xla TO BE<\/h4>\n<p>\nN\u00e4r vi designar \u00f6verf\u00f6ring via events-bus, kan vi se en parallell. Vi beskriver p\u00e5 liknande s\u00e4tt den framtida datatransfereringen genom att beskriva strukturen av eventet. YAML-formatet, kodgenereringen fick vi g\u00f6ra sj\u00e4lva; generatorn efter specifikationen skapar DTO och l\u00e4r klienter och servrar att arbeta med dem. Genereringen sker p\u00e5 tv\u00e5 spr\u00e5k - <strong>golang och php<\/strong>. Detta s\u00e4kerst\u00e4ller att biblioteken \u00e4r konsekventa. Generatorn \u00e4r skriven i golang, vilket gav den namnet gogi.<\/p>\n<p>Event-sourcing p\u00e5 Kafka \u00e4r typiskt. Det finns en l\u00f6sning fr\u00e5n den huvudsakliga f\u00f6retagsversionen Kafka Confluent, och det finns <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/nakadi\">nakadi<\/a><\/noindex>, en l\u00f6sning fr\u00e5n v\u00e5ra \"br\u00f6der\" inom dom\u00e4nomr\u00e5det Zalando. V\u00e5r <strong>motivation att b\u00f6rja med vanilla Kafka<\/strong>\u00a0\u00e4r att h\u00e5lla l\u00f6sningen gratis tills vi definitivt beslutar om vi kommer att anv\u00e4nda den allm\u00e4nt, samt att beh\u00e5lla utrymme f\u00f6r man\u00f6vrering och f\u00f6rb\u00e4ttringar: vi vill ha st\u00f6d f\u00f6r v\u00e5r <strong>JSON RPC 2.0<\/strong>, generatorer f\u00f6r tv\u00e5 spr\u00e5k och vi f\u00e5r se vad mer. <\/p>\n<p>Ironiskt nog, \u00e4ven i det lyckliga fallet n\u00e4r det finns ett ungef\u00e4r liknande f\u00f6retag som Zalando som har gjort en ungef\u00e4r liknande l\u00f6sning, kan vi inte anv\u00e4nda den effektivt. <\/p>\n<p>Arkitektoniskt, vid starten, \u00e4r m\u00f6nstret att l\u00e4sa direkt fr\u00e5n Kafka, men skriva endast genom events-bus. F\u00f6r att l\u00e4sa fr\u00e5n Kafka finns det mycket f\u00e4rdigt: m\u00e4klare, lastbalanserare och den \u00e4r mer eller mindre klar f\u00f6r horisontell skalning, vilket vi ville beh\u00e5lla. Skriften ville vi dock linda in genom en Gateway aka Events-bus, och det \u00e4r d\u00e4rf\u00f6r.<\/p>\n<h3>Events-bus<\/h3>\n<p>\neller h\u00e4ndelsebussen. Det \u00e4r bara en stateless http-gateway som tar p\u00e5 sig flera viktiga roller:<\/p>\n<ul>\n<li><strong>Validering av producering<\/strong>\u00a0\u2013 vi kontrollerar att h\u00e4ndelserna uppfyller v\u00e5r specifikation.<\/li>\n<li><strong>Master-systemet f\u00f6r h\u00e4ndelser<\/strong>, det vill s\u00e4ga det huvudsakliga och enda systemet i f\u00f6retaget som svarar p\u00e5 fr\u00e5gan om vilka h\u00e4ndelser med vilka strukturer som anses giltiga. Valideringen inkluderar bara datatyper och enums f\u00f6r en strikt specifikation av inneh\u00e5llet. <\/li>\n<li><strong>Hash-funktion<\/strong> f\u00f6r sharding \u2013 Kafka-meddelandets struktur \u00e4r key-value och h\u00e4r ber\u00e4knas var det ska l\u00e4ggas utifr\u00e5n hash av key.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Varf\u00f6r<\/h3>\n<p>\nVi arbetar i ett stort f\u00f6retag med en v\u00e4letablerad process. Varf\u00f6r \u00e4ndra n\u00e5got? <strong>Det h\u00e4r \u00e4r ett experiment<\/strong>, och vi f\u00f6rv\u00e4ntar oss att f\u00e5 flera f\u00f6rdelar.<\/p>\n<h4>1:n+1 \u00f6verf\u00f6ringar (en till m\u00e5nga)<\/h4>\n<p>\nMed Kafka \u00e4r det mycket enkelt att koppla in nya konsumenter till API:t. <\/p>\n<p>Anta att du har en katalog som du beh\u00f6ver h\u00e5lla uppdaterad i flera system samtidigt (och kanske i nya ocks\u00e5). Tidigare utvecklade vi ett bundle som implementerade set-API, medan master-systemet informerade om konsumenternas adresser. Nu skickar master-systemet uppdateringar till en topic, och alla som \u00e4r intresserade l\u00e4ser dem. Det har kommit ett nytt system \u2013 vi har prenumererat p\u00e5 topicen. Ja, det \u00e4r ocks\u00e5 ett bundle, men enklare.<\/p>\n<p>I fallet med refund-tool, som i grunden \u00e4r en del av BOB, \u00e4r det praktiskt f\u00f6r oss att h\u00e5lla dem synkroniserade via Kafka. Payment meddelar att pengarna har \u00e5terbetalats: BOB och RT f\u00e5r veta det, \u00e4ndrar sina statusar, och Fiscalization Service f\u00e5r veta det och skriver ut kvittot.<\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b01b22a333b58e87aeef0c52d40e6960.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nVi planerar att skapa en enhetlig Notifications Service som skulle informera kunder om nyheter kring deras best\u00e4llningar\/returer. Just nu \u00e4r det ansvaret spritt mellan systemen. Det kommer att r\u00e4cka med att l\u00e4ra Notifications Service att f\u00e5nga relevant information fr\u00e5n Kafka och reagera p\u00e5 den (och st\u00e4nga av dessa meddelanden i de andra systemen). Inga nya direkta utbyten kr\u00e4vs.<\/p>\n<h4>Data-driven<\/h4>\n<p>\nInformationen mellan systemen blir transparent \u2013 oavsett vilket \"blodigt enterprise\" ni har och hur omfattande er backlog \u00e4r. Lamoda har en avdelning f\u00f6r Data Analytics som samlar in data fr\u00e5n systemen och omvandlar dem till ett \u00e5teranv\u00e4ndbart format, b\u00e5de f\u00f6r aff\u00e4rs\u00e4ndam\u00e5l och f\u00f6r intelligenta system. Kafka g\u00f6r det m\u00f6jligt att snabbt ge dem mycket data och h\u00e5lla denna informationsstr\u00f6m aktuell.<\/p>\n<h4>Replication log<\/h4>\n<p>\nMeddelanden f\u00f6rsvinner inte efter l\u00e4sning, som i RabbitMQ. N\u00e4r en h\u00e4ndelse inneh\u00e5ller tillr\u00e4ckligt med information f\u00f6r bearbetning, f\u00e5r vi en historia av de senaste \u00e4ndringarna f\u00f6r objektet och, om vi vill, m\u00f6jlighet att till\u00e4mpa dessa \u00e4ndringar.<\/p>\n<p>Lagringstiden f\u00f6r replication log beror p\u00e5 hur intensivt data skrivs till denna topic; Kafka till\u00e5ter att vi flexibelt kan st\u00e4lla in lagringsgr\u00e4nser b\u00e5de vad g\u00e4ller tidsperiod och datavolym. F\u00f6r intensiva topics \u00e4r det viktigt att alla konsumenter hinner l\u00e4sa informationen innan den f\u00f6rsvinner, \u00e4ven vid kortvariga driftstopp. Vanligtvis kan vi lagra data under\u00a0<strong>ett par dagar<\/strong>, vilket \u00e4r fullt tillr\u00e4ckligt f\u00f6r support. <\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e08dd384155289123ebee96430c2370.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nH\u00e4r f\u00f6ljer lite sammanfattning av dokumentationen f\u00f6r dem som inte \u00e4r bekanta med Kafka (bilden \u00e4r ocks\u00e5 fr\u00e5n dokumentationen)<\/p>\n<p>I AMQP finns det k\u00f6er: vi skriver meddelanden till en k\u00f6 f\u00f6r konsumenten. Vanligtvis behandlar ett system med en och samma aff\u00e4rslogik en k\u00f6. Om flera system beh\u00f6ver meddelas kan vi l\u00e4ra appliceringen att skriva till flera k\u00f6er eller konfigurera en exchange med en fanout-mekanism som klonar dem.<\/p>\n<p>I Kafka finns en liknande abstraktion <em>topic<\/em>, dit du skriver meddelanden, men de f\u00f6rsvinner inte efter l\u00e4sning. Som standard, n\u00e4r du ansluter till Kafka, f\u00e5r du alla meddelanden, och du har m\u00f6jlighet att spara platsen d\u00e4r du stannade. Det vill s\u00e4ga att du l\u00e4ser sekventiellt, kan v\u00e4lja att inte markera meddelandet som l\u00e4st men spara id d\u00e4r du sedan forts\u00e4tter l\u00e4sa. Id, d\u00e4r du stannade, kallas offset, och mekanismen \u00e4r commit offset. <\/p>\n<p>D\u00e4rf\u00f6r kan olika logik implementeras. Till exempel, v\u00e5r BOB finns i 4 instanser f\u00f6r olika l\u00e4nder \u2014 Lamoda finns i Ryssland, Kazakstan, Ukraina, Vitryssland. Eftersom de distribueras separat har de lite egna konfigurationer och sin egen aff\u00e4rslogik. Vi anger i meddelandet vilket land det h\u00f6r till. Varje BOB-konsument i varje land l\u00e4ser med olika groupId, och om meddelandet inte g\u00e4ller dem, hoppar de \u00f6ver det, det vill s\u00e4ga de kommittar offset +1 direkt. Om samma \u00e4mne l\u00e4ses av v\u00e5r Payment Service g\u00f6r den detta med en separat grupp, och d\u00e4rf\u00f6r \u00f6verlappar inte offset.<\/p>\n<p><b>Krav p\u00e5 h\u00e4ndelser:<\/b><\/p>\n<ul>\n<li><strong>Fullst\u00e4ndighet av data. <\/strong>Vi skulle vilja att h\u00e4ndelsen inneh\u00f6ll tillr\u00e4ckligt med data f\u00f6r att kunna bearbeta den. <\/li>\n<\/ul>\n<p><\/p>\n<ul>\n<li><strong>Integritet. <\/strong>Vi delegerar Events-bus att kontrollera att h\u00e4ndelsen \u00e4r konsekvent och att den kan bearbetas.<\/li>\n<li><strong>Ordning \u00e4r viktig. <\/strong>I fallet med returer m\u00e5ste vi arbeta med historik. Vid notifieringar \u00e4r ordningen inte viktig, om det handlar om homogena notifieringar, blir e-postmeddelandet detsamma oavsett vilken best\u00e4llning som kom f\u00f6rst. I fallet med retur finns det en tydlig process; om ordningen \u00e4ndras kan det uppst\u00e5 undantag, \u00e5terbetalningen kan inte skapas eller bearbetas \u2014 vi kommer att hamna i ett annat status.<\/li>\n<li><strong>Konsistens. <\/strong>Vi har ett lagringsutrymme, och nu skapar vi events ist\u00e4llet f\u00f6r API. Vi beh\u00f6ver ett s\u00e4tt att snabbt och billigt \u00f6verf\u00f6ra information om nya events och om f\u00f6r\u00e4ndringar i redan existerande till v\u00e5ra tj\u00e4nster. Detta uppn\u00e5s genom en gemensam specifikation i ett separat git-repository och kodgeneratorer. D\u00e4rf\u00f6r \u00e4r kunder och servrar i olika tj\u00e4nster synkroniserade.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Kafka i Lamoda<\/h2>\n<p>\nVi har tre installationer av Kafka: <\/p>\n<ol>\n<li>Logs;<\/li>\n<li>R&amp;D;<\/li>\n<li>Events-bus.<\/li>\n<\/ol>\n<p>\nIdag pratar vi bara om den senaste punkten. I events-busen har vi inte s\u00e5 stora installationer \u2013 3 m\u00e4klare (servrar) och totalt 27 \u00e4mnen. Som regel \u00e4r ett \u00e4mne en process. Men detta \u00e4r en delikat fr\u00e5ga, och vi kommer att ber\u00f6ra den snart.<\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/f398852689b31429cc97b4cbffcabab5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOvan \u00e4r en graf \u00f6ver rps. Processen f\u00f6r \u00e5terbetalningar \u00e4r markerad med en turkos linje (ja, ja, den som ligger p\u00e5 X-axeln), och med en rosa linje \u2013 processen f\u00f6r att uppdatera inneh\u00e5ll. <\/p>\n<p>Katalogen Lamoda inneh\u00e5ller miljontals produkter, och data uppdateras hela tiden. Vissa kollektioner g\u00e5r ur modet, nya sl\u00e4pps ist\u00e4llet, och nya modeller dyker st\u00e4ndigt upp i katalogen. Vi f\u00f6rs\u00f6ker f\u00f6ruts\u00e4ga vad som kommer att intressera v\u00e5ra kunder i morgon, s\u00e5 vi k\u00f6per st\u00e4ndigt nya saker, fotograferar dem och uppdaterar skyltf\u00f6nstret. <\/p>\n<p>De rosa topparna representerar produktuppdateringar, det vill s\u00e4ga f\u00f6r\u00e4ndringar av produkter. Det \u00e4r tydligt att teamet fotograferade, fotograferade, och sedan pl\u00f6tsligt! \u2013 laddade upp en m\u00e4ngd events.<\/p>\n<h2>Lamoda Events anv\u00e4ndningsfall<\/h2>\n<p>\nDen byggda arkitekturen anv\u00e4nder vi f\u00f6r f\u00f6ljande operationer:<\/p>\n<ul>\n<li><strong>\u00d6vervakning av returstatusar<\/strong>: call-to-action och sp\u00e5rning av statusar fr\u00e5n alla involverade system. Betalning, statusar, fackalisering, notifieringar. H\u00e4r pr\u00f6vade vi en metod, skapade verktyg, samlade alla buggar, skrev dokumentation och ber\u00e4ttade f\u00f6r kollegor hur man anv\u00e4nder det.<\/li>\n<li><strong>Uppdatering av produktkort: <\/strong>konfiguration, metadata, specifikationer. En system l\u00e4ser (som visar), medan flera skriver.<\/li>\n<li><strong>Email, push och sms<\/strong>: best\u00e4llningen har samlats, best\u00e4llningen har kommit fram, returen har accepterats etc., m\u00e5nga av dem. <\/li>\n<li><strong>Lager, uppdatering av lager<\/strong>\u00a0\u2013 kvantitativ uppdatering av artiklar, bara siffror: ankomst till lagret, retur. Det \u00e4r viktigt att alla system som \u00e4r kopplade till varureserviingen hanterar s\u00e5 aktuella data som m\u00f6jligt. F\u00f6r n\u00e4rvarande \u00e4r lagersystemet ganska komplext, Kafka kommer att f\u00f6renkla det.<\/li>\n<li><strong>Dataanalys<\/strong> (F&amp;U-avdelning), ML-verktyg, analys, statistik. Vi vill att informationen ska vara transparent - f\u00f6r detta passar Kafka bra.<\/li>\n<\/ul>\n<p>\nNu kommer den mer intressanta delen om de misstag som gjorts och de intressanta uppt\u00e4ckter som har skett under det senaste halv\u00e5ret.<\/p>\n<h2>Designproblem<\/h2>\n<p>\nAnta att vi vill skapa n\u00e5got nytt - till exempel, \u00f6verf\u00f6ra hela leveransprocessen till Kafka. Just nu implementeras en del av processen i Order Processing i BOB. Bakom \u00f6verf\u00f6ringen av best\u00e4llningen till fraktj\u00e4nsten, flyttning till ett mellanlager och annat ligger en statusmodell. Det finns en hel monolit, till och med tv\u00e5, plus massor av API:er relaterade till leverans. De vet mycket mer om leveransen. <\/p>\n<p>Det verkar som om dessa omr\u00e5den \u00e4r lika, men f\u00f6r Order Processing i BOB och f\u00f6r leveranssystemet \u00e4r statusarna olika. Vissa budfirmor skickar till exempel inte mellanliggande statusar, utan bara de slutgiltiga: \"levererad\" eller \"f\u00f6rlorad\". Andra, tv\u00e4rtom, rapporterar v\u00e4ldigt detaljerat om varans r\u00f6relse. Var och en har sina egna valideringsregler: f\u00f6r vissa \u00e4r en e-postadress giltig, s\u00e5 den behandlas; f\u00f6r andra \u00e4r den ogiltig, men best\u00e4llningen kommer fortfarande att behandlas eftersom det finns ett telefonnummer f\u00f6r kontakt, medan n\u00e5gon annan s\u00e4ger att en s\u00e5dan best\u00e4llning \u00f6verhuvudtaget inte kommer att behandlas.<\/p>\n<h3>Datastream<\/h3>\n<p>\nI fallet med Kafka uppst\u00e5r fr\u00e5gan om organisationen av datastreamen. Denna uppgift \u00e4r kopplad till valet av strategi p\u00e5 flera punkter, l\u00e5t oss g\u00e5 igenom dem alla.<\/p>\n<h4>I en topic eller i flera?<\/h4>\n<p>\nVi har en specifikation f\u00f6r h\u00e4ndelsen. I BOB skriver vi att en viss best\u00e4llning ska levereras, och vi anger: best\u00e4llningsnummer, dess inneh\u00e5ll, n\u00e5gra SKU och streckkoder etc. N\u00e4r varan anl\u00e4nder till lagret kommer leveransen att kunna f\u00e5 statusar, tidsst\u00e4mplar och allt som beh\u00f6vs. Men vi vill sedan i BOB f\u00e5 uppdateringar baserat p\u00e5 dessa data. En omv\u00e4nd process f\u00f6r att h\u00e4mta data fr\u00e5n leveransen uppst\u00e5r. \u00c4r det samma h\u00e4ndelse? Eller \u00e4r det en separat utbyte som f\u00f6rtj\u00e4nar en egen topic?<\/p>\n<p>F\u00f6rmodligen kommer de att likna varandra starkt, och frestelsen att skapa en topic \u00e4r inte ogrundad, eftersom en separat topic inneb\u00e4r separata konsumenter, separata konfigurationer, separat generering av allt detta. Men det \u00e4r inte s\u00e4kert.<\/p>\n<h4>Nytt f\u00e4lt eller ny h\u00e4ndelse?<\/h4>\n<p>\nMen om man anv\u00e4nder samma h\u00e4ndelser, uppst\u00e5r ett annat problem. Till exempel kan inte alla distributionssystem generera ett s\u00e5dant DTO som BOB kan generera. Vi skickar dem id, men de sparar inte dessa eftersom de inte beh\u00f6vs, och sett ur h\u00e4ndelse-hubbens startpunkt \u00e4r det h\u00e4r f\u00e4ltet obligatoriskt. <\/p>\n<p>Om vi s\u00e4tter en regel f\u00f6r h\u00e4ndelse-hubben att detta f\u00e4lt \u00e4r obligatoriskt, tvingas vi inf\u00f6ra ytterligare valideringsregler i BOB eller i hanteraren f\u00f6r start-h\u00e4ndelsen. Valideringen b\u00f6rjar sprida sig \u00f6ver tj\u00e4nsten \u2013 det \u00e4r inte s\u00e4rskilt praktiskt.<\/p>\n<p>Ett annat problem \u00e4r frestelsen till inkrementell utveckling. Man s\u00e4ger till oss att vi beh\u00f6ver l\u00e4gga till n\u00e5got i h\u00e4ndelsen, och kanske, om man verkligen t\u00e4nker efter, borde det ha varit en separat h\u00e4ndelse. Men i v\u00e5r struktur \u00e4r en separat h\u00e4ndelse ett separat \u00e4mne. Ett separat \u00e4mne inneb\u00e4r hela den process jag beskrev ovan. Utvecklaren frestas att helt enkelt l\u00e4gga till ett annat f\u00e4lt i JSON-schemat och regenerera.<\/p>\n<p>F\u00f6r refunds kom vi p\u00e5 ett halv\u00e5r till h\u00e4ndelse av h\u00e4ndelser. Vi hade en meta-h\u00e4ndelse som kallas refund update, som hade ett f\u00e4lt av typen som beskrev vad den h\u00e4r uppdateringen egentligen handlar om. Fr\u00e5n detta hade vi \"fantastiska\" switchar med validerare som sa hur man skulle valida denna h\u00e4ndelse med denna typ.<\/p>\n<h4>Versionering av h\u00e4ndelser<\/h4>\n<p>\nF\u00f6r validering av meddelanden i Kafka kan man anv\u00e4nda <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.confluent.io\/current\/schema-registry\/docs\/index.html\">Avro<\/a><\/noindex>, men man beh\u00f6vde planera f\u00f6r detta direkt och anv\u00e4nda Confluent. I v\u00e5rt fall med versioneringen m\u00e5ste vi vara f\u00f6rsiktiga. Det kommer inte alltid att vara m\u00f6jligt att l\u00e4sa om meddelandena fr\u00e5n repliceringsloggen, eftersom modellen har \"\u00e5kt iv\u00e4g\". I huvudsak f\u00e5r vi bygga versioner s\u00e5 att modellen \u00e4r bak\u00e5tkompatibel: till exempel g\u00f6ra f\u00e4ltet tillf\u00e4lligt icke-obligatoriskt. Om skillnaderna \u00e4r f\u00f6r stora b\u00f6rjar vi skriva i ett nytt \u00e4mne, och klienterna byter \u00f6ver n\u00e4r de har l\u00e4st klart det gamla.<\/p>\n<h4>Garanti f\u00f6r l\u00e4sningsordning av partitioner<\/h4>\n<p>\n\u00c4mnen i Kafka \u00e4r uppdelade i partitioner. Detta \u00e4r inte s\u00e4rskilt viktigt medan vi designar entiteter och utbyten, men viktigt n\u00e4r vi beslutar hur vi ska konsumera och skala.<\/p>\n<p>I normala fall skriver du till Kafka en topic. Som standard anv\u00e4nds en partition, och alla meddelanden i denna topic hamnar d\u00e4r. Consumer l\u00e4ser d\u00e4refter dessa meddelanden sekventiellt. Anta nu att du beh\u00f6ver ut\u00f6ka systemet s\u00e5 att meddelandena kan l\u00e4sas av tv\u00e5 olika consumrar. Om du till exempel skickar ett SMS kan du be Kafka att skapa en extra partition, och Kafka b\u00f6rjar f\u00f6rdela meddelandena mellan tv\u00e5 delar \u2014 h\u00e4lften dit, h\u00e4lften hit. <\/p>\n<p>Hur delar Kafka dem? Varje meddelande har en kropp (d\u00e4r vi lagrar JSON) och en nyckel. Till denna nyckel kan en hash-funktion kopplas, som kommer att best\u00e4mma vilken partition meddelandet hamnar i.<\/p>\n<p>I v\u00e5rt fall med refunds \u00e4r detta viktigt. Om vi tar tv\u00e5 partitioner finns risken att en parallell consumer hanterar den andra h\u00e4ndelsen tidigare \u00e4n den f\u00f6rsta, vilket kan leda till problem. Hash-funktionen garanterar att meddelanden med samma nyckel hamnar i samma partition. <\/p>\n<h4>Events vs kommandon<\/h4>\n<p>\nDetta \u00e4r ett annat problem som vi st\u00f6tte p\u00e5. Event \u00e4r en h\u00e4ndelse: vi s\u00e4ger att n\u00e5got har h\u00e4nt (something_happened), till exempel att ett objekt har avbrutits eller en \u00e5terbetalning har skett. Om n\u00e5gon lyssnar p\u00e5 dessa h\u00e4ndelser, kommer en entitet refund att skapas vid \"objektet avbr\u00f6ts\", och \"\u00e5terbetalning har skett\" kommer att registreras n\u00e5gonstans i inst\u00e4llningarna.<\/p>\n<p>Men vanligtvis, n\u00e4r du designar h\u00e4ndelser, vill du inte bara skriva dem f\u00f6r sakens skull \u2014 du f\u00f6rv\u00e4ntar dig att de ska l\u00e4sas av n\u00e5gon. Det finns en stark frestelse att skriva n\u00e5got som inte \u00e4r something_happened (item_canceled, refund_refunded), utan n\u00e5got som b\u00f6r g\u00f6ras, till exempel item klar f\u00f6r retur.<\/p>\n<p>\u00c5 ena sidan indikerar detta hur h\u00e4ndelsen kommer att anv\u00e4ndas. \u00c5 andra sidan liknar det mycket mindre ett normalt h\u00e4ndelse namn. Dessutom ligger det n\u00e4ra till hands att ge en kommando do_something. Men du har ingen garanti f\u00f6r att detta event har blivit l\u00e4st; och om det har l\u00e4sts, s\u00e5 har det l\u00e4sts framg\u00e5ngsrikt; och om det har l\u00e4sts framg\u00e5ngsrikt, s\u00e5 gjordes n\u00e5got, och det n\u00e5got gick igenom framg\u00e5ngsrikt. I det \u00f6gonblick h\u00e4ndelsen blir do_something, blir \u00e5terkoppling n\u00f6dv\u00e4ndig, och det \u00e4r ett problem.<\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b755d91208092bd9791a41ce4633fb48.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nVid asynkron kommunikation i RabbitMQ, n\u00e4r du har l\u00e4st ett meddelande, g\u00e5tt vidare till http, har du en respons \u2014 \u00e5tminstone att meddelandet har accepterats. N\u00e4r du skriver i Kafka har du ett meddelande om att du har skrivit i Kafka, men du vet ingenting om hur det har behandlats. <\/p>\n<p>D\u00e4rf\u00f6r var vi tvungna att inf\u00f6ra en svarsh\u00e4ndelse i v\u00e5rt fall och konfigurera \u00f6vervakning f\u00f6r att om det intr\u00e4ffade ett visst antal h\u00e4ndelser p\u00e5 en viss tid, s\u00e5 skulle lika m\u00e5nga svarsh\u00e4ndelser komma in. Om detta inte h\u00e4nde, verkar det som om n\u00e5got gick fel. Till exempel, om vi skickade h\u00e4ndelsen \"item_ready_to_refund\", f\u00f6rv\u00e4ntar vi oss att en \u00e5terbetalning ska skapas, att kunden ska f\u00e5 tillbaka sina pengar och att vi ska f\u00e5 h\u00e4ndelsen \"money_refunded\". Men det \u00e4r inte s\u00e4kert, d\u00e4rf\u00f6r beh\u00f6vs \u00f6vervakning.<\/p>\n<h3>Detaljer<\/h3>\n<p>\nDet finns ett ganska uppenbart problem: om du l\u00e4ser fr\u00e5n \u00e4mnet sekventiellt och du har n\u00e5got meddelande som \u00e4r d\u00e5ligt, kraschar konsumenten och du kommer inte vidare. Du beh\u00f6ver <strong>stoppa alla konsumenter<\/strong>, beg\u00e5 offset l\u00e4ngre fram f\u00f6r att kunna forts\u00e4tta l\u00e4sa.<\/p>\n<p>Vi var medvetna om detta, vi hade f\u00f6rberett oss f\u00f6r det, men det h\u00e4nde \u00e4nd\u00e5. Detta intr\u00e4ffade eftersom h\u00e4ndelsen var giltig ur events-bus perspektiv, h\u00e4ndelsen var giltig ur applikationsvaliderarens perspektiv, men den var inte giltig ur PostgreSQL:s perspektiv, eftersom vi i ett system har MySQL med UNSIGNED INT, medan det nyss skrivna systemet hade PostgreSQL med bara INT. Dess storlek \u00e4r n\u00e5got mindre och Id fick inte plats. Symfony dog med ett undantag. Sj\u00e4lvklart f\u00e5ngade vi undantaget eftersom vi hade f\u00f6rberett oss f\u00f6r det och planerade att beg\u00e5 detta offset, men innan dess ville vi inkrementera problemr\u00e4knaren, eftersom meddelandet hanterades misslyckat. R\u00e4knarna i detta projekt ligger ocks\u00e5 i databasen, och Symfony hade redan avslutat kommunikationen med databasen, och det andra undantaget d\u00f6dade hela processen utan chans att beg\u00e5 offset.<\/p>\n<p>Servicen l\u00e5g nere ett tag - lyckligtvis \u00e4r det inte s\u00e5 farligt med Kafka, f\u00f6r meddelandena ligger kvar. N\u00e4r arbetet \u00e5terupptas kan de l\u00e4sas igen. Det \u00e4r praktiskt.<\/p>\n<p>Kafka har m\u00f6jlighet att via verktyg s\u00e4tta en godtycklig offset. Men f\u00f6r att g\u00f6ra detta beh\u00f6ver du stoppa alla konsumenter - i v\u00e5rt fall f\u00f6rbereda en separat release d\u00e4r det inte finns n\u00e5gra konsumenter, inga omlokaliseringar. D\u00e5 kan du via verktygen i Kafka justera offset, och meddelandet kommer att g\u00e5 igenom.<\/p>\n<p>En annan detalj - <strong>replikationslogg vs rdkafka.so<\/strong>\u00a0\u2014 \u00e4r kopplad till specifikationen av v\u00e5rt projekt. Vi anv\u00e4nder PHP, och i PHP kommunicerar ofta alla bibliotek med Kafka via rdkafka.so-repositoriet, och sedan finns det n\u00e5got slags omslag. Kanske \u00e4r det v\u00e5ra personliga utmaningar, men det visade sig att det helt enkelt inte \u00e4r s\u00e5 l\u00e4tt att l\u00e4sa om n\u00e5got som man redan har l\u00e4st. Sammanfattningsvis hade vi programvaruproblem.<\/p>\n<p>Tillbaka till specifikationerna f\u00f6r hur man arbetar med partitions, st\u00e5r det skrivet direkt i dokumentationen <strong>consumers &gt;= topic partitions<\/strong>. Men jag fick veta detta mycket senare \u00e4n jag hade velat. Om du vill skala upp och ha tv\u00e5 konsumenter beh\u00f6ver du minst tv\u00e5 partitions. Det inneb\u00e4r att om du har haft en partition d\u00e4r 20 000 meddelanden har samlats, och du har skapat en ny, kommer antalet meddelanden att j\u00e4mnas ut f\u00f6rst efter en tid. S\u00e5 f\u00f6r att ha tv\u00e5 parallella konsumenter m\u00e5ste man f\u00f6rst\u00e5 sig p\u00e5 partitions.<\/p>\n<h2>\u00d6vervakning<\/h2>\n<p>\nJag tror att det kommer att bli \u00e4nnu tydligare, baserat p\u00e5 hur vi \u00f6vervakar, vilka problem som finns i den nuvarande metoden.<\/p>\n<p>Till exempel r\u00e4knar vi hur m\u00e5nga produkter som nyligen har \u00e4ndrat status i databasen, och i enlighet med dessa f\u00f6r\u00e4ndringar borde det ha intr\u00e4ffat h\u00e4ndelser, och vi skickar detta nummer till v\u00e5rt \u00f6vervakningssystem. Sedan f\u00e5r vi fr\u00e5n Kafka det andra numret, det som faktiskt har registrerats av h\u00e4ndelser. Uppenbarligen ska skillnaden mellan dessa tv\u00e5 siffror alltid vara noll.<\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/07d8ed08514f2fb97d9019466b96342c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDessutom m\u00e5ste vi \u00f6vervaka hur det g\u00e5r f\u00f6r producenterna, om events-bussen har tagit emot meddelanden, och hur det g\u00e5r f\u00f6r konsumenten. Till exempel \u00e4r allt bra med Refund Tool p\u00e5 graferna nedan, men BOB har uppenbarligen n\u00e5gra problem (bl\u00e5 toppar).<\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/57112dbe70d2b388c53f19c34cca6f63.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nJag har redan n\u00e4mnt consumer-group lag. Grovt sagt \u00e4r det antalet ol\u00e4sta meddelanden. Generellt arbetar konsumenterna snabbt, s\u00e5 lag \u00e4r vanligtvis 0, men ibland kan det uppst\u00e5 kortvariga toppar. Kafka hanterar detta ur l\u00e5dan, men man m\u00e5ste ange en viss tidsram. <\/p>\n<p>Det finns ett projekt <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/linkedin\/Burrow\">Burrow<\/a><\/noindex>, som ger dig mer information om Kafka. Det ger helt enkelt via API:n statusen f\u00f6r consumer-group och hur det g\u00e5r f\u00f6r denna grupp. F\u00f6rutom OK och Failed finns det ocks\u00e5 warning, och du kan f\u00e5 veta att dina konsumenter inte klarar av tempot i producerandet \u2013 att de inte hinner l\u00e4sa det som skrivs. Systemet \u00e4r ganska smart, det \u00e4r praktiskt att anv\u00e4nda. <\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/cece8495801e187b155487a802e1a35a.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nS\u00e5 h\u00e4r ser svaret ut via API:n. H\u00e4r \u00e4r gruppen bob-live-fifa, partition refund.update.v1, status OK, lag 0 \u2013 den senaste slutliga offset \u00e4r s\u00e5 h\u00e4r.<\/p>\n<p><img decoding=\"async\" alt=\"Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/1538b2ccc390e9b83075f54566f1039b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n\u00d6vervakning <strong>updated_at SLA (stuck)<\/strong> Jag har redan n\u00e4mnt det. Till exempel har produkten \u00f6verg\u00e5tt till statusen \"redo f\u00f6r \u00e5terbetalning\". Vi s\u00e4tter upp en Cron som s\u00e4ger att om den h\u00e4r objektet inte har \u00f6verg\u00e5tt till \u00e5terbetalning inom 5 minuter (vi \u00e5terbetalar pengar snabbt via betalningssystem), s\u00e5 har n\u00e5got definitivt g\u00e5tt fel, och det \u00e4r en klar fall f\u00f6r supporten. S\u00e5 vi anv\u00e4nder helt enkelt en Cron som l\u00e4ser s\u00e5dana saker, och om de \u00e4r fler \u00e4n 0, s\u00e5 skickar den en alert.<\/p>\n<p><b>Sammanfattningsvis \u00e4r det bekv\u00e4mt att anv\u00e4nda h\u00e4ndelser n\u00e4r<\/b>:<\/p>\n<ul>\n<li>information beh\u00f6vs av flera system;<\/li>\n<li>resultatet av bearbetningen inte betyder n\u00e5got;<\/li>\n<li>det finns f\u00e5 h\u00e4ndelser eller h\u00e4ndelserna \u00e4r sm\u00e5. <\/li>\n<\/ul>\n<blockquote><p>Det kan tyckas som att artikeln har ett helt konkret \u00e4mne \u2013 asynkront API p\u00e5 Kafka, men jag har mycket att rekommendera i samband med det.<br \/>\nF\u00f6r det f\u00f6rsta, n\u00e4sta <noindex><a rel=\"nofollow\" href=\"https:\/\/www.highload.ru\/\">HighLoad++<\/a><\/noindex> beh\u00f6ver vi v\u00e4nta till november, men redan i april kommer den lokala versionen att h\u00e5llas i Sankt Petersburg, och i juni pratar vi om h\u00f6g belastning i Novosibirsk.<br \/>\nF\u00f6r det andra, rapportf\u00f6rfattaren Sergey Zaika \u00e4r medlem av det programkommitt\u00e9 f\u00f6r v\u00e5r nya konferens om kunskapsstyrning. <noindex><a rel=\"nofollow\" href=\"https:\/\/knowledgeconf.ru\/2019\">KnowledgeConf<\/a><\/noindex>Konferensen \u00e4r en endags och \u00e4ger rum den 26 april, men programmet \u00e4r mycket intensivt.<br \/>\nOch dessutom kommer det i maj <noindex><a rel=\"nofollow\" href=\"https:\/\/phprussia.ru\/2019\">PHP Russia<\/a><\/noindex> och\u00a0<noindex><a rel=\"nofollow\" href=\"https:\/\/ritfest.ru\/2019\">RIT++<\/a><\/noindex> (med DevOpsConf som del) \u2013 dit kan du \u00e4ven foresl\u00e5 ditt \u00e4mne, ber\u00e4tta om din erfarenhet och klaga p\u00e5 dina inl\u00e4rda l\u00e4xor.<\/p><\/blockquote>\n<p>K\u00e4lla: <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 5.0.4 - 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?\" \/>\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\/sv\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"sv_SE\" \/>\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?\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/sv\/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\udd47Erfarenhet av att utveckla tj\u00e4nsten Refund Tool med asynkront API p\u00e5 Kafka | ProHoster","description":"Vad kan f\u00e5 ett s\u00e5 stort f\u00f6retag som Lamoda med en v\u00e4letablerad process och tiotals sammankopplade tj\u00e4nster att \u00e4ndra sin strategi avsev\u00e4rt?","canonical_url":"https:\/\/prohoster.info\/sv\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sv_SE","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?","og:url":"https:\/\/prohoster.info\/sv\/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","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts\/30778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/comments?post=30778"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/posts\/30778\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media\/22763"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/media?parent=30778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/categories?post=30778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sv\/wp-json\/wp\/v2\/tags?post=30778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}