{"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\/hr\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","title":{"rendered":"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>\u0160to mo\u017ee natjerati tako veliku kompaniju kao \u0161to je Lamoda, koja ima uspostavljen proces i desetke me\u0111usobno povezanih servisa, da bitno promijeni pristup? Motivacija mo\u017ee biti potpuno razli\u010dita: od zakonodavne do uro\u0111ene sklonosti programera prema eksperimentiranju.<\/p>\n<p>No, to ne zna\u010di da se ne mo\u017ee ra\u010dunati na dodatnu korist. O \u010demu to\u010dno mo\u017eete profitirati ako uvedete events-driven API na Kafki, objasnit \u0107e Sergej Zaika (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/fewald\/\" class=\"user_link\">fewald<\/a><\/noindex>). O proma\u0161enim poku\u0161ajima i zanimljivim otkri\u0107ima tako\u0111er \u0107e biti rije\u010di \u2013 eksperimenti se ne mogu obaviti bez njih.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/7ab959ab45ec5c6565b35b18b361c0ea.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<em>Izjava o odricanju: Ovaj \u010dlanak temelji se na materijalima meetupa koji je Sergej odr\u017eao u studenom 2018. godine na HighLoad++. Iskustvo Lamode u radu s Kafkom privuklo je slu\u0161atelje ne manje od drugih predavanja iz rasporeda. Smatramo da je to odli\u010dan primjer toga da uvijek mo\u017eete i trebate prona\u0107i istomi\u0161ljenike, a organizatori HighLoad++ i dalje \u0107e se truditi stvoriti povoljnu atmosferu za to.<\/em><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>O procesu<\/h2>\n<p>\nLamoda je velika e-commerce platforma koja ima svoj kontakt-centar, slu\u017ebu dostave (i mno\u0161tvo partnera), foto-studio, ogromno skladi\u0161te, a sve to radi na vlastitom softveru. Postoji desetke na\u010dina pla\u0107anja, b2b partneri koji mogu koristiti dio ili sve te usluge i \u017eele znati aktuelne informacije o svojim proizvodima. Osim toga, Lamoda radi u tri zemlje osim Rusije, a poslovanje tamo je malo druga\u010dije. Ukratko, postoji vjerovatno vi\u0161e od stotinu na\u010dina za konfiguriranje novog narud\u017ebe koja se mora obraditi na specifi\u010dan na\u010din. Sve to radi uz pomo\u0107 desetaka servisa koji me\u0111usobno komuniciraju ponekad na nejasan na\u010din. Tu je i sredi\u0161nji sustav \u010dija je glavna odgovornost status narud\u017ebi. Mi ga nazivamo BOB, ja radim s njim.<\/p>\n<h2>Refund Tool with events-driven API <\/h2>\n<p>\nIzraz events-driven je prili\u010dno kori\u0161ten, malo \u0107emo kasnije detaljnije definirati \u0161to to podrazumijeva. Zapo\u010det \u0107emo s kontekstom u kojem smo odlu\u010dili isprobati pristup events-driven API-ju u Kafki. <\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/3bfdce47dd8420fc63d84645e76de647.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nU svakoj trgovini, osim narud\u017ebi za koje kupci pla\u0107aju, postoje trenuci kada se od trgovine tra\u017ei povrat novca, jer proizvod nije odgovarao kupcu. Ovaj relativno kratak proces uklju\u010duje potvrdu informacija, ako je to potrebno, i vra\u0107anje novca. <\/p>\n<p>No, povrat se zakomplicirao zbog promjene zakonodavstva, pa smo morali implementirati zasebnu mikroservis za to.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/0e358df5476e7448976e0f1147a103fb.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNa\u0161a motivacija:<\/p>\n<ol>\n<li><strong>Zakon FZ-54<\/strong>\u00a0\u2014 ukratko, zakon zahtijeva prijavu svake nov\u010dane transakcije poreznoj upravi, bilo da se radi o povratu ili uplati, unutar prili\u010dno kratkog SLA-a od nekoliko minuta. Kao e-commerce, provodimo prili\u010dno mnogo transakcija. Tehni\u010dki, to zna\u010di novu odgovornost (a time i nov servis) i prilagodbe u svim relevantnim sustavima.<\/li>\n<li><strong>BOB split<\/strong>\u00a0\u2014 unutarnji projekt tvrtke za osloba\u0111anje BOB-a od velikog broja vanjskih odgovornosti i smanjenje njegove slo\u017eenosti.<\/li>\n<\/ol>\n<p>\n<img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/d3ecf9961bdb372fc5f84ee9389f73ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNa ovoj shemi prikazane su glavne sustave Lamoda. Trenutno ve\u0107ina njih vi\u0161e nalikuje <strong>konstelaciji od 5-10 mikroservisa oko smanjuju\u0107e monolitne strukture<\/strong>. Polako se pove\u0107avaju, ali nastojimo ih smanjiti jer je zastra\u0161uju\u0107e rasporediti odvojen dio u sredini \u2014 ne smijemo dopustiti da padne. Sve razmjene (strelice) moramo rezervirati i pripremiti se za to da bilo koja od njih mo\u017ee biti nedostupna.<\/p>\n<p>U BOB-u tako\u0111er postoji prili\u010dno mnogo razmjena: sustavi pla\u0107anja, dostave, obavijesti itd. <\/p>\n<p>Tehni\u010dki, BOB je:<\/p>\n<ul>\n<li>~150k linija koda + ~100k linija testova;<\/li>\n<li>php7.2 + Zend 1 &amp; Symfony Components 3;<\/li>\n<li>&gt;100 API &amp; ~50 odlaznih integracija;<\/li>\n<li>4 zemlje s vlastitom poslovnom logikom. <\/li>\n<\/ul>\n<p>\nRaspore\u0111ivanje BOB-a skupo je i bolno, koli\u010dina koda i zadataka koje on rje\u0161ava takva je da nitko ne mo\u017ee shvatiti sve u cjelini. Uglavnom, ima mnogo razloga za njegovo pojednostavljenje.<\/p>\n<h2>Proces povrata<\/h2>\n<p>\nU procesu su prvotno uklju\u010dena dva sustava: BOB i Payment. Sada se dodaju jo\u0161 dva:<\/p>\n<ul>\n<li>Fiscalization Service, koji \u0107e preuzeti probleme s fiskalizacijom i komunikaciju s vanjskim servisima.<\/li>\n<li>Refund Tool, u koji se jednostavno iznose nove razmjene, kako se ne bi preopteretio BOB.<\/li>\n<\/ul>\n<p>\nSada proces izgledaju ovako:<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/13c02975881ad35c61304053c604cda3.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ol>\n<li>BOB prima zahtjev za povrat novca.<\/li>\n<li>BOB o tome obavje\u0161tava Refund Tool.<\/li>\n<li>Refund Tool govori Payment-u: 'Vrati novac.'<\/li>\n<li>Payment vra\u0107a novac.<\/li>\n<li>Refund Tool i BOB sinkroniziraju me\u0111usobne statuse, jer im je to trenutno potrebno. Jo\u0161 uvijek nismo spremni potpuno se prebaciti na Refund Tool, jer u BOB-u postoji UI, izvje\u0161taji za ra\u010dunovodstvo i op\u0107enito mnogo podataka koje nije lako prenijeti. Moramo sjediti na dva stolca.<\/li>\n<li>Odlazi zahtjev za fiskalizaciju.<\/li>\n<\/ol>\n<p>\nNa kraju smo stvorili neku vrstu event-busa na temelju Kafke, na kojoj se sve temelji. Hura, sada imamo jedinstvenu to\u010dku kvara (sarcasti\u010dno).<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/674edd7972998b4985071f5250612c7e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPrednosti i nedostaci su prili\u010dno o\u010diti. Napravili smo bus, \u0161to zna\u010di da svi servisi ovise o njemu. To pojednostavljuje projektiranje, ali unosi jedinstvenu to\u010dku kvara u sustav. Ako Kafka padne, proces \u0107e stati.<\/p>\n<h2>\u0160to je events-driven API <\/h2>\n<p>\nDobar odgovor na ovo pitanje mo\u017eete prona\u0107i u izvje\u0161taju Martina Fowlersa (GOTO 2017) <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/STKCRSUsyPO\">\u00abMnoge Stranice Event-Driven Arhitekture\u00bb<\/a><\/noindex>. <\/p>\n<p>Ukratko, \u0161to smo napravili:<\/p>\n<ol>\n<li>Obavili smo sve asinkrone razmjene putem <strong>events storage<\/strong>. Umjesto da obavje\u0161tavamo svakog zainteresiranog korisnika putem mre\u017ee o promjeni statusa, zapisujemo u centralizirano skladi\u0161te doga\u0111aj o promjeni stanja, a zainteresirani korisnici \u010ditaju sve \u0161to se pojavljuje odatle.<\/li>\n<li>Doga\u0111aj (event) u ovom slu\u010daju je obavijest (<strong>notifications<\/strong>) o tome da se ne\u0161to negdje promijenilo. Na primjer, promijenio se status narud\u017ebe. Korisnik kojem su va\u017eni neki pridru\u017eeni podaci k promjeni statusa koji nisu uklju\u010deni u obavijest, mo\u017ee sam provjeriti njihovo stanje.<\/li>\n<li>Maksimalna opcija je potpuni event sourcing, <strong>state transfer<\/strong>, pri \u010demu doga\u0111aj sadr\u017ei sve informacije potrebne za obradu: odakle i u koji status su pre\u0161li, kako su se podaci promijenili i sl. Pitanje je samo opravdanost i koli\u010dina informacija koje si mo\u017eete priu\u0161titi pohraniti.<\/li>\n<\/ol>\n<p>\nU okviru pokretanja Refund Tool koristili smo tre\u0107u opciju. To je pojednostavilo obradu doga\u0111aja, budu\u0107i da nije bilo potrebno prikupljati detaljne informacije, plus je isklju\u010dilo scenarij kada svaka nova doga\u0111aj uzrokuje val dodatnih get-zahtjeva od potro\u0161a\u010da.<\/p>\n<p>Servis Refund Tool <strong>nije optere\u0107en<\/strong>, stoga je Kafka tu vi\u0161e probna nego potreba. Ne mislim da bi, da je servis za povrat sredstava postao highload projekt, poslovanje bilo zadovoljno.<\/p>\n<h4>Async exchange AS IS<\/h4>\n<p>\nZa asinkrone razmjene, PHP odjel obi\u010dno koristi RabbitMQ. Prikupili smo podatke za zahtjev, stavili ih u red, a konzumator iste usluge ih je pro\u010ditao i poslao (ili nije poslao). Za API Lamoda aktivno koristi Swagger. Projektiramo API, opisujemo ga u Swaggeru, generiramo klijentski i serverski kod. Tako\u0111er koristimo malo pro\u0161ireni JSON RPC 2.0. <\/p>\n<p>Negdje se koriste esb-busi, netko koristi activeMQ, ali, u cjelini, <strong>RabbitMQ \u2014 standard<\/strong>.<\/p>\n<h4>Async exchange TO BE<\/h4>\n<p>\nKada projektiramo razmjenu putem events-bus, uo\u010dava se analogija. Na sli\u010dan na\u010din opisujemo budu\u0107u razmjenu podataka kroz opise strukture event-a. Format yaml, programsku generaciju morali smo napraviti sami, generator prema specifikaciji stvara DTO i podu\u010dava klijente i servere kako s njima raditi. Generacija ide na dva jezika \u2014 <strong>golang i php<\/strong>. To omogu\u0107ava da biblioteke budu uskla\u0111ene. Generator je napisan u golangu, zbog \u010dega nosi ime gogi.<\/p>\n<p>Event-sourcing na Kafka je tipi\u010dna stvar. Postoji rje\u0161enje iz glavne enterprise verzije Kafka Confluent, postoji <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/nakadi\">nakadi<\/a><\/noindex>, rje\u0161enje na\u0161ih 'bra\u0107e' iz domene Zalando. Na\u0161a <strong>motivacija da krenemo s vanilla Kafka<\/strong>\u00a0je da ostavimo rje\u0161enje besplatnim, dok kona\u010dno ne odlu\u010dimo ho\u0107emo li ga koristiti posvuda, a tako\u0111er da zadr\u017eimo prostora za manevre i dorade: \u017eelimo podr\u0161ku za na\u0161 <strong>JSON RPC 2.0<\/strong>, generatore za dva jezika i vidjet \u0107emo \u0161to jo\u0161. <\/p>\n<p>Ironi\u010dno je da \u010dak i u tako sretnom slu\u010daju, kada postoji otprilike sli\u010dan posao kao Zalando, koji je napravio otprilike sli\u010dno rje\u0161enje, ne mo\u017eemo ga u\u010dinkovito koristiti. <\/p>\n<p>Arhitektonski je na pokretanju obrazac ovakav: \u010ditamo direktno iz Kafka, ali zapisujemo samo putem events-bus. Za \u010ditanje u Kafka postoji mnogo gotovog: brokeri, balansirnici i vi\u0161e-manje je spreman za horizontalno skaliranje, to smo htjeli zadr\u017eati. Zapis, me\u0111utim, \u017eeljeli smo obaviti preko jednog Gateway-a, odnosno Events-busa, i to je razlog za\u0161to.<\/p>\n<h3>Events-bus<\/h3>\n<p>\nIli autobus doga\u0111aja. To je jednostavno stateless http gateway koji preuzima nekoliko va\u017enih uloga:<\/p>\n<ul>\n<li><strong>Validacija produkcije<\/strong>\u00a0provjeravamo da li doga\u0111aji odgovaraju na\u0161oj specifikaciji.<\/li>\n<li><strong>Master-sustav za doga\u0111aje<\/strong>, dakle, to je glavni i jedini sustav u kompaniji koji odgovara na pitanje koji doga\u0111aji s kojim strukturama smatraju valjanima. U validaciju ulaze samo tipovi podataka i enums za strogu specifikaciju sadr\u017eaja. <\/li>\n<li><strong>Hash-funkcija<\/strong> za sharding \u2014 struktura poruka Kafka je key-value i na temelju hasha od key-a izra\u010dunava se gdje to staviti.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Za\u0161to<\/h3>\n<p>\nRadimo u velikoj kompaniji s uhodanim procesom. Za\u0161to mijenjati ne\u0161to? <strong>Ovo je eksperiment<\/strong>, i ra\u010dunate na nekoliko koristi.<\/p>\n<h4>1:n+1 razmjene (jedan prema mnogima)<\/h4>\n<p>\nS Kafka je vrlo jednostavno povezati nove potro\u0161a\u010de na API. <\/p>\n<p>Pretpostavimo da imate direktorij koji treba odr\u017eavati a\u017eurnim u nekoliko sustava odjednom (uklju\u010duju\u0107i bilo koje nove). Prije smo izumili bundle koji je implementirao set-API, a glavnom sustavu smo javili adrese potro\u0161a\u010da. Sada glavni sustav \u0161alje a\u017euriranja u topic, a svi koji su zainteresirani ih \u010ditaju. Pojavio se novi sustav \u2013 prijavili smo ga na topic. Da, tako\u0111er bundle, ali jednostavniji.<\/p>\n<p>U slu\u010daju refund-tool, koji je zapravo komadi\u0107 BOB-a, prakti\u010dno nam je da ih odr\u017eavamo sinkroniziranima putem Kafke. Payment govori da su novci vra\u0107eni: BOB, RT o tome saznaju, mijenjaju svoje statuse, a Fiscalization Service je obavije\u0161ten i izdao je ra\u010dun.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/b01b22a333b58e87aeef0c52d40e6960.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPlaniramo uspostaviti jedinstvenu Notifications Service koja bi obavje\u0161tavala klijente o novostima vezanima uz njihove narud\u017ebe\/ povratke. Trenutno je ta odgovornost raspodijeljena me\u0111u sustavima. Bit \u0107e nam dovoljno nau\u010diti Notifications Service da iz Kafke izdvaja relevantne informacije i reagira na njih (i isklju\u010diti ta obavje\u0161tenja u ostalim sustavima). Nema potrebe za novim izravnim razmjenama.<\/p>\n<h4>Data-driven<\/h4>\n<p>\nInformacije izme\u0111u sustava postaju transparentne \u2013 bez obzira na to kakav 'krvavi enterprise' imate ili koliko debeo bio va\u0161 backlog. U Lamodi postoji odjel Data Analytics koji prikuplja podatke iz sustava i dovodi ih u ponovno upotrebljiv oblik, kako za biznis, tako i za inteligentne sustave. Kafka omogu\u0107ava brzo pru\u017eanje velikih koli\u010dina podataka i odr\u017eavanje tog informacijskog toka aktualnim.<\/p>\n<h4>Replication log<\/h4>\n<p>\nPoruke ne nestaju nakon \u010ditanja, kao u RabbitMQ. Kada event sadr\u017ei dovoljno informacija za obradu, imamo povijest posljednjih promjena objekta i, po \u017eelji, mogu\u0107nost da te promjene primijenimo.<\/p>\n<p>Rok \u010duvanja replication loga ovisi o intenzitetu unosa u ovaj topic, Kafka omogu\u0107ava fleksibilno postavljanje granica za vrijeme \u010duvanja i po volumenu podataka. Za intenzivne topice va\u017eno je da svi potro\u0161a\u010di sti\u017eu \u010ditati informacije prije nego \u0161to nestanu, \u010dak i u slu\u010daju kratkotrajne neispravnosti. Obi\u010dno je mogu\u0107e \u010duvati podatke za\u00a0<strong>jedinice dana<\/strong>, \u0161to je sasvim dovoljna za podr\u0161ku. <\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/0e08dd384155289123ebee96430c2370.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDalje slijedi malo prepri\u010davanje dokumentacije, za one koji nisu upoznati s Kafkom (slika je tako\u0111er iz dokumentacije)<\/p>\n<p>U AMQP postoje redovi: pi\u0161emo poruke u red za konzumer. Obi\u010dno jedan red obra\u0111uje jedan sustav s istom poslovnom logikom. Ako treba obavijestiti nekoliko sustava, aplikaciju se mo\u017ee nau\u010diti da pi\u0161e u vi\u0161e redova ili se mo\u017ee postaviti exchange s fanout mehanizmom koji ih sam klonira.<\/p>\n<p>U Kafka postoji sli\u010dna apstrakcija <em>topic<\/em>, u koju pi\u0161ete poruke, ali one ne nestaju nakon \u010ditanja. Po defaultu, pri povezivanju na Kafka, dobivamo sve poruke, a postoji mogu\u0107nost da zadr\u017eite mjesto na kojem ste stali. Dakle, \u010ditate sekvencijalno, ne morate ozna\u010diti poruku kao pro\u010ditanu, ali mo\u017eete zadr\u017eati id s kojeg \u0107ete potom nastaviti \u010ditanje. Id na kojem ste stali nazivamo offset (pomak), a mehanizam je commit offset. <\/p>\n<p>Prema tome, mo\u017ee se realizirati razli\u010dita logika. Na primjer, na\u0161 BOB postoji u 4 instance za razli\u010dite zemlje - Lamoda postoji u Rusiji, Kazahstanu, Ukrajini, Bjelorusiji. Budu\u0107i da se isporu\u010duju odvojeno, oni imaju malo svoje konfiguracije i svoju poslovnu logiku. U poruci ozna\u010davamo kojoj zemlji pripada. Svaki konzumer BOB u svakoj zemlji \u010dita s razli\u010ditim groupId, i, ako poruka nije vezana za njega, propu\u0161ta je, tj. odmah komitira offset +1. Ako isti topic \u010dita na\u0161 Payment Service, onda to radi s odvojenom grupom, pa se offseti ne preklapaju.<\/p>\n<p><b>Zahtjevi za doga\u0111ajima:<\/b><\/p>\n<ul>\n<li><strong>Potpunost podataka. <\/strong>\u017deljeli bismo da u doga\u0111aju bude dovoljno podataka za njegovu obradu. <\/li>\n<\/ul>\n<p><\/p>\n<ul>\n<li><strong>Cjelovitost. <\/strong>Delegiramo Events-bus provjeru da je doga\u0111aj konzistentan i da ga mo\u017ee obraditi.<\/li>\n<li><strong>Redoslijed je va\u017ean. <\/strong>U slu\u010daju povrata, prisiljeni smo raditi s povije\u0161\u0107u. S notifikacijama redoslijed nije va\u017ean, ako su to homogene notifikacije, email \u0107e biti isti neovisno o tome koji je narud\u017eba stigla prva. U slu\u010daju povrata postoji jasan proces, ako promijenimo redoslijed, nastat \u0107e iznimke, refund se ne\u0107e stvoriti ili obraditi - dospjet \u0107emo u drugi status.<\/li>\n<li><strong>Usagla\u0161enost. <\/strong>Imamo skladi\u0161te, i sada umjesto API-a kreiramo doga\u0111aje. Trebamo na\u010din da brzo i jeftino proslijedimo informacije o novim doga\u0111ajima i promjenama u postoje\u0107ima na\u0161im servisima. To se posti\u017ee zajedni\u010dkom specifikacijom u zasebnom git repozitoriju i generatorima koda. Zato su klijenti i serveri u razli\u010ditim servisima uskla\u0111eni.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Kafka u Lamodi<\/h2>\n<p>\nImamo tri instalacije Kafke: <\/p>\n<ol>\n<li>Zapisi;<\/li>\n<li>R&amp;D;<\/li>\n<li>Events-bus.<\/li>\n<\/ol>\n<p>\nDanas govorimo samo o posljednjem stavku. U events-bus imamo ne tako velike instalacije \u2014 3 brokera (servera) i ukupno 27 tema. Obi\u010dno, jedna tema je jedan proces. No, to je suptilna to\u010dka, i sada \u0107emo je dotaknuti.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/f398852689b31429cc97b4cbffcabab5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nGore je grafikon rps. Proces povrata ozna\u010den je tirkiznom linijom (da, da, onom koja le\u017ei na osi X), a ru\u017ei\u010dastom \u2014 proces a\u017euriranja sadr\u017eaja. <\/p>\n<p>Katalog Lamode sadr\u017ei milijune proizvoda, a podaci se neprekidno a\u017euriraju. Neke kolekcije izlaze iz mode, dok se nove lansiraju, u katalogu neprekidno se pojavljuju novi modeli. Poku\u0161avamo predvidjeti \u0161to \u0107e biti zanimljivo na\u0161im klijentima sutra, stoga neprekidno nabavljamo nove stvari, fotografiramo ih i a\u017euriramo izloge. <\/p>\n<p>Ru\u017ei\u010dasti vrhovi su a\u017euriranja proizvoda, \u0161to zna\u010di promjene u proizvodima. Vidljivo je da su de\u010dki fotografirali, fotografirali, a onda bam! \u2014 u\u010ditali su hrpu doga\u0111aja.<\/p>\n<h2>Upotreba Lamoda doga\u0111aja<\/h2>\n<p>\nIzgra\u0111enu arhitekturu koristimo za takve operacije:<\/p>\n<ul>\n<li><strong>Pra\u0107enje statusa povrata<\/strong>: poziv na akciju i pra\u0107enje statusa svih uklju\u010denih sustava. Pla\u0107anja, statusi, fiskalizacija, obavijesti. Ovdje smo isprobali pristup, napravili alate, prikupili sve bugove, napisali dokumentaciju i ispri\u010dali kolegama kako to koristiti.<\/li>\n<li><strong>A\u017euriranje kartica proizvoda: <\/strong>konfiguracija, meta-podaci, karakteristike. \u010cita jedan sustav (koji prikazuje), a pi\u0161u nekoliko.<\/li>\n<li><strong>Email, push i sms<\/strong>: narud\u017eba je prikupljena, narud\u017eba je stigla, povratak prihva\u0107en itd., mnogo ih je. <\/li>\n<li><strong>Zalihe, a\u017euriranje skladi\u0161ta<\/strong>\u00a0\u2014 kvantitativno a\u017euriranje naziva, samo brojevi: prijem na skladi\u0161te, povrat. Potrebno je da svi sustavi povezani s rezervacijom proizvoda operiraju maksimalno a\u017euriranim podacima. Trenutno je sustav a\u017euriranja zaliha prili\u010dno slo\u017een, Kafka \u0107e ga pojednostaviti.<\/li>\n<li><strong>Analiza podataka<\/strong> (R&amp;D odjel), ML alati, analiza, statistika. \u017delimo da informacije budu transparentne - za to Kafka dobro odgovara.<\/li>\n<\/ul>\n<p>\nSada dolazi zanimljiviji dio o pogre\u0161kama i zanimljivim otkri\u0107ima koja su se dogodila u posljednjih pola godine.<\/p>\n<h2>Problemi dizajna<\/h2>\n<p>\nRecimo da \u017eelimo napraviti novu stvar - na primjer, prevesti cijeli proces dostave na Kafka. Trenutno se dio procesa provodi u Order Processing u BOB-u. Iza prijenosa narud\u017ebe u dostavnu slu\u017ebu, premje\u0161tanja u me\u0111uskladi\u0161te i ostalog stoji statusni model. Postoji cijeli monolit, \u010dak i dva, plus hrpa API-ja posve\u0107enih dostavi. Oni znaju puno vi\u0161e o dostavi. <\/p>\n<p>\u010cini se da su to sli\u010dna podru\u010dja, ali statuse za Order Processing u BOB-u i za sustav dostave se razlikuju. Na primjer, neke dostavne slu\u017ebe ne \u0161alju me\u0111ustatuse, nego samo kona\u010dne: 'isporu\u010deno' ili 'izgubljeno'. Druge, naprotiv, vrlo detaljno izvje\u0161tavaju o kretanju proizvoda. Svi imaju svoje vlastite pravila validacije: za nekoga je validna email adresa, pa \u0107e je obraditi; za druge nije validna, ali narud\u017eba \u0107e se svejedno obraditi, jer postoji broj telefona za kontakt, a neki \u0107e re\u0107i da takvu narud\u017ebu ne\u0107emo uop\u0107e obra\u0111ivati.<\/p>\n<h3>Tok podataka<\/h3>\n<p>\nU slu\u010daju s Kafka postavlja se pitanje organizacije protoka podataka. Ovaj zadatak povezan je s izborom strategije po nekoliko to\u010daka, pro\u0107i \u0107emo kroz sve njih.<\/p>\n<h4>U jedan topic ili u razli\u010dite?<\/h4>\n<p>\nImamo specifikaciju doga\u0111aja. U BOB-u pi\u0161emo da treba isporu\u010diti odre\u0111enu narud\u017ebu, i navodimo: broj narud\u017ebe, njen sadr\u017eaj, neke SKU i barkodove itd. Kada roba stigne u skladi\u0161te, dostava mo\u017ee primiti statuse, timestamps i sve \u0161to je potrebno. No, dalje \u017eelimo u BOB-u primati a\u017euriranja o tim podacima. Pojavljuje se povratni proces dobivanja podataka iz dostave. Je li to isti doga\u0111aj? Ili je to zasebna razmjena koja zaslu\u017euje zasebni topic?<\/p>\n<p>Vjerojatno \u0107e biti vrlo sli\u010dni, i isku\u0161enje da se napravi jedan topic nije bez osnove, jer zaseban topic donosi zasebne potro\u0161a\u010de, zasebne konfiguracije, zasebnu generaciju svega toga. No, to nije isklju\u010deno.<\/p>\n<h4>Novo polje ili novi doga\u0111aj?<\/h4>\n<p>\nNo, ako koristimo iste doga\u0111aje, javlja se drugi problem. Na primjer, ne mogu sve dostavne sustave generirati takav DTO koji mo\u017ee generirati BOB. \u0160aljemo im id, a oni ga ne \u010duvaju, jer im nije potreban, a s gledi\u0161ta pokretanja procesa event-busa ovo polje je obavezno. <\/p>\n<p>Ako postavimo pravilo za event-bus da je ovo polje obavezno, moramo u BOB ili u obradi po\u010detnog doga\u0111aja postaviti dodatna pravila validacije. Validacija po\u010dinje da se \u0161iri po servisu - to nije ba\u0161 prakti\u010dno.<\/p>\n<p>Jo\u0161 jedan problem je primamljivost inkrementalnog razvoja. Govore nam da je potrebno ne\u0161to dodati u doga\u0111aj i, mo\u017eda, ako dobro razmislimo, to je trebalo biti poseban doga\u0111aj. No, u na\u0161oj shemi, poseban doga\u0111aj je poseban topic. Poseban topic je cijeli taj proces koji sam opisao ranije. Razvijatelj biva primamljiv da jednostavno doda jo\u0161 jedno polje u JSON shemu i regenerira.<\/p>\n<p>U slu\u010daju refunds, tako smo za pola godine do\u0161li do doga\u0111anja doga\u0111aja. Imali smo jedno meta-doga\u0111anje koje se zove refund update, u kojem je bilo polje type, koje opisuje o \u010demu se zapravo radi u ovom updateu. Iz toga smo imali \"prekrasne\" switcheve s validatorima koji su govorili kako treba validirati ovaj doga\u0111aj s tim type.<\/p>\n<h4>Verzioniranje doga\u0111aja<\/h4>\n<p>\nZa validaciju poruka u Kafka mo\u017ee se koristiti <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.confluent.io\/current\/schema-registry\/docs\/index.html\">Avro<\/a><\/noindex>, ali to je trebalo odmah ugraditi i koristiti Confluent. U na\u0161em slu\u010daju s verzioniranjem trebamo biti oprezni. Ne\u0107e uvijek biti mogu\u0107e pro\u010ditati poruke iz replication loga, jer je model \"oti\u0161ao\". Uglavnom, poku\u0161avamo graditi verzije tako da model bude unazad kompatibilan: na primjer, privremeno u\u010diniti polje neobaveznim. Ako su razlike prevelike, po\u010dinjemo pisati u novi topic, a klijente prebacujemo kada pro\u010ditaju stari.<\/p>\n<h4>Jamstvo redoslijeda \u010ditanja particija<\/h4>\n<p>\nTopici unutar Kafke su podijeljeni na particije. To nije jako va\u017eno dok projektiramo entitete i izmjene, ali je va\u017eno kada odlu\u010dujemo kako konzumirati i skalirati.<\/p>\n<p>Uobi\u010dajeno pi\u0161ete u Kafka jedan topic. Po zadanim postavkama koristi se jedan partition, i sve poruke tog topica dolaze u njega. A konzumatori odgovaraju\u0107e redom \u010ditaju te poruke. Pretpostavimo da sada treba pro\u0161iriti sustav tako da poruke \u010ditaju dva razli\u010dita konzumera. Ako, na primjer, \u0161aljete SMS, mo\u017eete re\u0107i Kafki da napravi dodatni partition, i Kafka \u0107e po\u010deti raspore\u0111ivati poruke na dva dijela \u2013 polovicu ovamo, polovicu onamo. <\/p>\n<p>Kako Kafka dijeli? Svaka poruka ima tijelo (u kojem pohranjujemo JSON) i klju\u010d. Na taj klju\u010d mo\u017ee se primijeniti hash funkcija koja \u0107e odrediti u koji partition \u0107e poruka u\u0107i.<\/p>\n<p>U na\u0161em slu\u010daju s refundacijama to je va\u017eno; ako uzmemo dva partitiona, postoji mogu\u0107nost da paralelni konzumator obradi drugo doga\u0111anje prije prvog i to bi moglo stvoriti probleme. Hash funkcija jam\u010di da \u0107e poruke s istim klju\u010dem zavr\u0161iti u istom partitionu. <\/p>\n<h4>Doga\u0111aji vs. naredbe<\/h4>\n<p>\nTo je jo\u0161 jedan problem s kojim smo se susreli. Doga\u0111aj je neka vrsta de\u0161avanja: ka\u017eemo da se ne\u0161to negdje dogodilo (something_happened), na primjer, stavka je otkazana ili je do\u0161lo do refundacije. Ako netko slu\u0161a te doga\u0111aje, tada \u0107e na osnovu \"stavka je otkazana\" biti stvoren entitet refundacije, dok \u0107e \"do\u0161lo do refundacije\" biti zabilje\u017eeno negdje u postavkama.<\/p>\n<p>Ali obi\u010dno, kada projektirate doga\u0111aje, ne \u017eelite ih pisati uzalud - oslanjate se na to da \u0107e ih netko \u010ditati. Velika je ku\u0161nja napisati ne something_happened (item_canceled, refund_refunded), ve\u0107 something_should_be_done. Na primjer, stavka je spremna za povrat.<\/p>\n<p>S jedne strane, to sugerira kako \u0107e se doga\u0111aj koristiti. S druge strane, to mnogo manje nalikuje na normalno ime doga\u0111aja. Osim toga, ovdje je ve\u0107 blizu do naredbe do_something. No, nemate jamstvo da je taj doga\u0111aj netko pro\u010ditao; a ako je pro\u010ditao, je li to u\u010dinio uspje\u0161no; a ako je to u\u010dinio uspje\u0161no, je li ne\u0161to u\u010dinio, i je li to ne\u0161to pro\u0161lo uspje\u0161no. U trenutku kada doga\u0111aj postane do_something, potrebna je povratna informacija, i to je problem.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/b755d91208092bd9791a41ce4633fb48.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nU asinkronoj razmjeni u RabbitMQ, kada pro\u010ditate poruku, oti\u0161li ste na http, imate odgovor - barem da je poruka prihva\u0107ena. Kada zapisujete u Kafku, postoji poruka da ste zapisali u Kafku, ali o tome kako je obra\u0111ena, ne znate ni\u0161ta. <\/p>\n<p>Stoga smo u na\u0161em slu\u010daju morali uvesti povratni doga\u0111aj i postaviti nadgledanje da bi se osiguralo da, ako se dogodi odre\u0111eni broj doga\u0111aja, unutar odre\u0111enog vremenskog perioda bi se trebalo dogoditi toliko povratnih doga\u0111aja. Ako to ne pro\u0111e, izgleda da ne\u0161to nije u redu. Na primjer, ako smo poslali doga\u0111aj \u201eitem_ready_to_refund\u201c, o\u010dekujemo da se refundacija aktivira, kupcu se vrate novci, a nama se dogoditi doga\u0111aj \u201emoney_refunded\u201c. No, to nije sigurno, stoga je potrebno nadgledanje.<\/p>\n<h3>Detalji<\/h3>\n<p>\nPostoji prili\u010dno o\u010dit problem: ako \u010ditate iz topic-a sljede\u0107im redom, a imate neku lo\u0161u poruku, consumer se sru\u0161i i dalje ne mo\u017eete nastaviti. Trebate <strong>zaustaviti sve consumere<\/strong>, potvrditi offset dalje kako biste mogli nastaviti s \u010ditanjem.<\/p>\n<p>O tome smo znali, uzeli smo to u obzir, a svejedno se to dogodilo. Dogodilo se to jer je doga\u0111aj bio valjan s gledi\u0161ta events-busa, doga\u0111aj je bio valjan s gledi\u0161ta validatora aplikacije, ali nije bio valjan s gledi\u0161ta PostgreSQL-a, jer smo imali u jednom sustavu MySQL s UNSIGNED INT, a u novom sustavu bio je PostgreSQL samo s INT. Njegova veli\u010dina je malo manja, i Id se nije mogao smjestiti. Symfony je umro s iznimkom. Naravno, uhvatili smo iznimku jer smo to predvidjeli i planirali smo potvrditi taj offset, ali prije toga smo htjeli inkrementirati broja\u010d problema, budu\u0107i da je poruka neuspje\u0161no obra\u0111ena. Broja\u010di u ovom projektu tako\u0111er se nalaze u bazi, a Symfony je ve\u0107 zatvorio komunikaciju s bazom, i druga iznimka je ubila cijeli proces bez \u0161anse za potvrdu offseta.<\/p>\n<p>Neko vrijeme je servis le\u017eao \u2013 sre\u0107om, s Kafka to nije tako stra\u0161no, jer poruke ostaju. Kada se rad ponovno uspostavi, mo\u017ee ih se pro\u010ditati. To je prakti\u010dno.<\/p>\n<p>Kafka ima mogu\u0107nost putem alata postaviti proizvoljni offset. No, da biste to u\u010dinili, morate zaustaviti sve consumere \u2013 u na\u0161em slu\u010daju pripremiti poseban release u kojem ne\u0107e biti consumera, redeployments. Tada se u Kafki putem alata mo\u017ee pomaknuti offset, i poruka \u0107e pro\u0107i.<\/p>\n<p>Drugi detalj \u2013 <strong>replication log vs rdkafka.so<\/strong>\u00a0\u2014 vezano uz specifi\u010dnosti na\u0161eg projekta. Mi koristimo PHP, a u PHP-u, kao pravilo, sve biblioteke komuniciraju s Kafka putem repozitorija rdkafka.so, a dalje ide neka vrsta omotnika. Mo\u017eda su to na\u0161i osobni problemi, ali ispostavilo se da jednostavno pro\u010ditati deo onoga \u0161to je ve\u0107 pro\u010ditano nije tako lako. Uglavnom, bilo je programskih problema.<\/p>\n<p>Vra\u0107aju\u0107i se na osobitosti rada s partitions, direktno u dokumentaciji pi\u0161e <strong>consumers &gt;= topic partitions<\/strong>. Ali saznao sam o tome mnogo kasnije nego \u0161to bih \u017eeleo. Ako \u017eelite skalirati i imati dva konzumer, trebate barem dva partitions. To jest, ako imate jedan partition u kojem je skupljeno 20 tisu\u0107a poruka, i napravili ste novi, broj poruka ne\u0107e se izjedna\u010diti brzo. Stoga, da biste imali dva paralelna konzumer, morate se pozabaviti partitions.<\/p>\n<h2>Monitoring<\/h2>\n<p>\nMislim, prema tome kako nadgledamo, bit \u0107e jo\u0161 jasnije koje probleme imamo u postoje\u0107em pristupu.<\/p>\n<p>Na primjer, brojimo koliko je proizvoda u bazi nedavno promenilo status, i u skladu s tim bi se po tim promenama trebali dogoditi doga\u0111aji, i \u0161aljemo taj broj u na\u0161 sustav za nadgledanje. Zatim iz Kafke primamo drugi broj, koliko je zapravo doga\u0111aja zabilje\u017eeno. O\u010dito, razlika izme\u0111u ta dva broja uvijek treba biti nula.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/07d8ed08514f2fb97d9019466b96342c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOsim toga, potrebno je nadgledati kako stoji producent, da li je events-bus primio poruke, i kako stoji konzumer. Na primjer, na grafikonu ispod, Refund Tool je u dobrom stanju, dok kod BOB-a o\u010digledno postoje neki problemi (plavi vrhovi).<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/57112dbe70d2b388c53f19c34cca6f63.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nVe\u0107 sam spomenuo consumer-group lag. Grubo re\u010deno, to je broj nepro\u010ditanih poruka. Uglavnom, na\u0161i konzumeri rade brzo, tako da je lag obi\u010dno 0, ali ponekad mo\u017ee biti kratkotrajan vrh. Kafka to podr\u017eava iz kutije, ali treba postaviti neki interval. <\/p>\n<p>Postoji projekt <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/linkedin\/Burrow\">Burrow<\/a><\/noindex>, koji \u0107e vam pru\u017eiti vi\u0161e informacija o Kafki. On jednostavno putem API-a za consumer-group pru\u017ea status, kako ide toj grupi. Osim OK i Failed, tu su i upozorenja, i mo\u017eete saznati da va\u0161i konzumeri ne uspevaju pratiti tempo produkcije \u2014 nemaju vremena da pro\u010ditaju ono \u0161to se pi\u0161e. Sustav je prili\u010dno pametan, lako je koristiti. <\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/cece8495801e187b155487a802e1a35a.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOvako izgleda odgovor putem API-a. Ovdje je grupa bob-live-fifa, partition refund.update.v1, status OK, lag 0 \u2014 posljednji krajni offset je takav.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki\" src=\"\/wp-content\/uploads\/2019\/04\/1538b2ccc390e9b83075f54566f1039b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMonitoring <strong>updated_at SLA (zaklju\u010dan)<\/strong> Ve\u0107 sam spomenuo. Na primjer, proizvod je pre\u0161ao u status da je spreman za povrat. Postavljamo Cron koji govori da ako u 5 minuta ta stavka ne pre\u0111e u refund (novac vra\u0107amo putem platnih sustava vrlo brzo), onda ne\u0161to zasigurno nije u redu, i to je definitivno slu\u010daj za podr\u0161ku. Stoga jednostavno uzimamo Cron koji \u010dita takve stvari, i ako ih ima vi\u0161e od 0, \u0161alje upozorenje.<\/p>\n<p><b>Ukratko, kori\u0161tenje doga\u0111aja je prakti\u010dno kada<\/b>:<\/p>\n<ul>\n<li>informacija treba vi\u0161e sustavima;<\/li>\n<li>rezultat obrade nije va\u017ean;<\/li>\n<li>ima malo doga\u0111aja ili su doga\u0111aji mali. <\/li>\n<\/ul>\n<blockquote><p>Naizgled, \u010dlanak ima vrlo specifi\u010dnu temu - asinkroni API na Kafki, ali u vezi s tim odmah bih preporu\u010dio mnogo toga.<br \/>\nPrvo, sljede\u0107e <noindex><a rel=\"nofollow\" href=\"https:\/\/www.highload.ru\/\">HighLoad++<\/a><\/noindex> ne treba \u010dekati do studenog, ve\u0107 u travnju \u0107e biti njegova verzija u St. Petersburgu, a u lipnju \u0107emo razgovarati o visokim optere\u0107enjima u Novosibirsku.<br \/>\nDrugo, autor izlaganja Sergej Zaika je \u010dlan Programskog odbora na\u0161e nove konferencije o upravljanju znanjem. <noindex><a rel=\"nofollow\" href=\"https:\/\/knowledgeconf.ru\/2019\">KnowledgeConf<\/a><\/noindex>Konferencija je jednodnevna, odr\u017eat \u0107e se 26. travnja, ali program \u0107e biti vrlo bogat.<br \/>\nA tako\u0111er \u0107e u svibnju biti <noindex><a rel=\"nofollow\" href=\"https:\/\/phprussia.ru\/2019\">PHP Russia<\/a><\/noindex> i\u00a0<noindex><a rel=\"nofollow\" href=\"https:\/\/ritfest.ru\/2019\">RIT++<\/a><\/noindex> (s DevOpsConf u sastavu) - tamo se jo\u0161 mo\u017ee predlo\u017eiti vlastita tema, ispri\u010dati svoje iskustvo i po\u017ealiti se na svoje nakupljene neugodnosti.<\/p><\/blockquote>\n<p>Izvor: <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\/hr\/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=\"hr_HR\" \/>\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\/hr\/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\udd47Iskustvo razvoja usluge Refund Tool s asinkronim API-jem na Kafki | ProHoster","description":"\u0160to mo\u017ee natjerati tako veliku tvrtku kao \u0161to je Lamoda s uspostavljenim procesom i desecima me\u0111usobno povezanih servisa da zna\u010dajno promijeni pristup?","canonical_url":"https:\/\/prohoster.info\/hr\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"hr_HR","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\/hr\/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\/hr\/wp-json\/wp\/v2\/posts\/30778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/comments?post=30778"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/posts\/30778\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/media\/22763"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/media?parent=30778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/categories?post=30778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/hr\/wp-json\/wp\/v2\/tags?post=30778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}