{"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\/sl\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","title":{"rendered":"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Kaj lahko tako veliko podjetje, kot je Lamoda, s prefinjenim procesom in desetimi medsebojno povezanimi storitvami, preoblikuje svoj pristop? Motivacija je lahko povsem razli\u010dna: od zakonodajnih sprememb do inherentne \u017eelje vseh programerjev po eksperimentiranju.<\/p>\n<p>Vendar to ne pomeni, da ni mogo\u010de ra\u010dunati na dodatne koristi. O tem, kaj konkretno lahko pridobimo z uvedbo events-driven API na Kafka, bo spregovoril Sergey Zaika (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/fewald\/\" class=\"user_link\">fewald<\/a><\/noindex>). O preizku\u0161njah in zanimivih odkritjih bo prav tako nujno govora \u2013 eksperimenti se ne morejo izogniti temu.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/7ab959ab45ec5c6565b35b18b361c0ea.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<em>Omejitev: Ta \u010dlanek temelji na gradivu s sre\u010danja, ki ga je Sergey izvedel novembra 2018 na HighLoad++. \u017divahna izku\u0161nja Lamoda pri delu s Kafka je privabila poslu\u0161alce enako kot druge predstavitve v razporedu. Mislimo, da je to odli\u010den primer, da vedno lahko in je treba najti somi\u0161ljenike, organizatorji HighLoad++ pa se bodo \u0161e naprej trudili ustvarjati vzdu\u0161je, ki to spodbuja.<\/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, ki ima svoj kontaktni center, dostavno slu\u017ebo (in \u0161tevilne partnerje), fotografski studio, ogromno skladi\u0161\u010de in vse to deluje na svoji programski opremi. Obstaja na desetine na\u010dinov pla\u010dila, B2B partnerji, ki lahko uporabljajo del teh ali vseh storitev in \u017eelijo vedeti aktualne informacije o svojih izdelkih. Poleg tega Lamoda deluje v treh dr\u017eavah poleg RF, kjer je vse rahlo druga\u010de. Na voljo je verjetno ve\u010d sto na\u010dinov konfiguracije novega naro\u010dila, ki ga je treba obravnavati na svoj na\u010din. Vse to deluje s pomo\u010djo desetine storitev, ki se med seboj pogosto sporazumevajo na na\u010din, ki ni vedno o\u010diten. Obstaja tudi osrednji sistem, katerega glavna odgovornost so statusi naro\u010dil. Imenujemo ga BOB, jaz delam zanj.<\/p>\n<h2>Orodje za vra\u010dilo z events-driven API <\/h2>\n<p>\nBeseda events-driven je precej zveli\u010davna, kasneje bomo podrobneje opredelili, kaj to pomeni. Za\u010del bom s kontekstom, v katerem smo se odlo\u010dili preizkusiti pristop events-driven API na Kafka. <\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/3bfdce47dd8420fc63d84645e76de647.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nV vsakem trgovini, poleg naro\u010dil, za katera kupci pla\u010dujejo, pridejo trenutki, ko od trgovine zahtevajo vra\u010dilo denarja, ker izdelek ni ustrezal \u017eeljam kupca. Ta relativno kratek proces vklju\u010duje pojasnitev informacij, \u010de je to potrebno, in vra\u010dilo denarja. <\/p>\n<p>Vendar se je proces vra\u010dila zapletel zaradi spremembe zakonodaje, zato smo morali razviti lo\u010den mikroservis.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim 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 na kratko, zakon zahteva, da se o vsaki denarni transakciji poro\u010da dav\u010dni upravi, naj bo to vra\u010dilo ali prihod, v zelo kratkem \u010dasu, ki obi\u010dajno ne presega nekaj minut. Mi, kot e-trgovina, izvajamo precej veliko transakcij. Tehni\u010dno to pomeni novo odgovornost (kar pomeni nov servis) in dopolnila v vseh povezanih sistemih.<\/li>\n<li><strong>BOB split<\/strong>\u00a0\u2014 interni projekt podjetja za osvoboditev BOB-a velikega \u0161tevila nepriproductivnih odgovornosti in zmanj\u0161anje njegove skupne kompleksnosti.<\/li>\n<\/ol>\n<p>\n<img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/d3ecf9961bdb372fc5f84ee9389f73ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNa tej shemi so prikazani glavni sistemi Lamoda. Zdaj ve\u010dina od njih predstavlja bolj <strong>zvezdnato nebo 5-10 mikroservisov okoli zmanj\u0161ujo\u010dega se monolita<\/strong>. Po\u010dasi rastejo, a trudimo se, da jih je manj, ker je zastra\u0161ujo\u010de, ko je treba v sredino potisniti nov segment \u2013 ne smemo dovoliti, da pade. Vsa izmenjava (pu\u0161\u010dice) mora biti rezervirana, saj se moramo zana\u0161ati, da je lahko katera od njih nedostopna.<\/p>\n<p>Tudi v BOB-u je precej izmenjav: pla\u010dne sisteme, dostava, obvestila itd. <\/p>\n<p>Tehni\u010dno je BOB:<\/p>\n<ul>\n<li>~150k vrstic kode + ~100k vrstic testov;<\/li>\n<li>php7.2 + Zend 1 &amp; Symfony Komponente 3;<\/li>\n<li>&gt;100 API &amp; ~50 odhodnih integracij;<\/li>\n<li>4 dr\u017eave s svojo poslovno logiko. <\/li>\n<\/ul>\n<p>\nZagon BOB-a je drag in bole\u010d, koli\u010dina kode in nalog, ki jih re\u0161uje, je taka, da jih nih\u010de ne more imeti v celoti v mislih. Na splo\u0161no je veliko razlogov za poenostavitev.<\/p>\n<h2>Postopek vra\u010dila<\/h2>\n<p>\nSprva sta v proces vklju\u010deni dve sistemi: BOB in Payment. Zdaj se pojavita \u0161e dve:<\/p>\n<ul>\n<li>Fiscalization Service, ki bo prevzel te\u017eave s fiskalizacijo in komunikacijo z zunanjimi storitvami.<\/li>\n<li>Refund Tool, v katerega se preprosto prenesejo nove izmenjave, da ne bi pove\u010devali BOB-a.<\/li>\n<\/ul>\n<p>\nZdaj postopek izgleda takole:<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim 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 prejme zahtevo za vra\u010dilo denarja.<\/li>\n<li>BOB o tem obvesti Refund Tool.<\/li>\n<li>Refund Tool pove Payment: 'Vrni denar'.<\/li>\n<li>Payment vrne denar.<\/li>\n<li>Refund Tool in BOB usklajujeta statusa, ker ju trenutno oba potrebujeta. \u0160e nismo pripravljeni popolnoma preklopiti na Refund Tool, ker ima BOB UI, poro\u010dila za ra\u010dunovodstvo in na splo\u0161no veliko podatkov, katerih tako enostavno ne moremo prenesti. Tako je treba sedeti na dveh stolih.<\/li>\n<li>Zahteva za fiskalizacijo gre naprej.<\/li>\n<\/ol>\n<p>\nTako smo ustvarili nekak\u0161no dogodkovno kolektivno omre\u017eje na Kafka, na katerem se vse temelji. Hura, zdaj imamo eno to\u010dko odpovedi (sarkazem).<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/674edd7972998b4985071f5250612c7e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPrednosti in slabosti so precej o\u010ditne. Ustvarili smo kolektivno omre\u017eje, kar pomeni, da zdaj vsi storitve nanj ra\u010dunajo. To poenostavi na\u010drtovanje, a prina\u0161a v sistem eno to\u010dko odpovedi. \u010ce Kafka odpove, postopek stoji.<\/p>\n<h2>Kaj je API, ki temelji na dogodkih <\/h2>\n<p>\nDober odgovor na to vpra\u0161anje najdemo v poro\u010dilu Martina Fowlersa (GOTO 2017) <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/STKCRSUsyPO\">\u00bbMnogo pomenov dogodkovne arhitekture\u00ab<\/a><\/noindex>. <\/p>\n<p>Na kratko, kar smo naredili:<\/p>\n<ol>\n<li>Obvili smo vse asinkrone izmenjave preko <strong>shramba dogodkov<\/strong>. Namesto da bi vsakemu zainteresiranemu uporabniku sporo\u010dili spremembo stanja po omre\u017eju, zapisujemo v centralizirano shrambo dogodek o spremembi stanja, zainteresirani uporabniki pa berejo vse, kar se tam pojavi.<\/li>\n<li>Dogodek (event) v tem primeru je obvestilo (<strong>obvestila<\/strong>) o tem, da se je nekaj nekje spremenilo. Na primer, spremenil se je status naro\u010dila. Uporabnik, ki mu so pomembni dolo\u010deni spremljajo\u010di podatki o spremembi statusa in katerih ni v obvestilu, lahko sam preveri njihovo stanje.<\/li>\n<li>Maksimalna razli\u010dica je popoln event sourcing, <strong>prenos stanja<\/strong>, pri katerem dogodek vsebuje vse informacije, potrebne za obdelavo: od kod in v kateri status pre\u0161li, kako so se podatki spremenili in tako naprej. Vpra\u0161anje je le smiselnost in koli\u010dina informacij, ki jih lahko shranite.<\/li>\n<\/ol>\n<p>\nV okviru zagonnega orodja za vra\u010dilo sredstev smo uporabili tretjo mo\u017enost. To je poenostavilo obdelavo dogodkov, saj ni bilo treba pridobivati podrobnih informacij, poleg tega pa je izklju\u010dilo scenarij, kjer vsako novo dogodek povzro\u010di pove\u010danje natan\u010dnih get-poizvedb s strani uporabnikov.<\/p>\n<p>Storitveno orodje za vra\u010dilo sredstev <strong>ni obremenjeno<\/strong>, zato je Kafka tam bolj neka preizku\u0161nja skrivnosti kot nuja. Ne mislim, da bi bil poslovni svet vesel, \u010de bi postala storitev vra\u010dila sredstev projekt z visokim obremenitvami.<\/p>\n<h4>Asinkroni izmenjave AS IS<\/h4>\n<p>\nZa asinkrone izmenjave PHP oddelek obi\u010dajno uporablja RabbitMQ. Zbiramo podatke za zahtevo, jih postavimo v \u010dakalno vrsto, in uporabnik iste storitve jih prebere in po\u0161lje (ali ne po\u0161lje). Za samo API Lamoda aktivno uporablja Swagger. Na\u010drtujemo API, opisujemo ga v Swaggerju, generiramo odjemalsko in stre\u017eni\u0161ko kodo. \u0160e uporabljamo nekoliko raz\u0161irjen JSON RPC 2.0. <\/p>\n<p>Nekje se uporabljajo esb-omre\u017eja, nekdo \u017eivi na activeMQ, a na splo\u0161no, <strong>RabbitMQ \u2014 standard<\/strong>.<\/p>\n<h4>Async exchange TO BE<\/h4>\n<p>\nWhen designing exchange through events-bus, there is an analogy. We describe future data exchange similarly through event structure descriptions. We had to implement the YAML format and code generation ourselves; the generator creates DTO according to the specification and teaches clients and servers to work with them. Generation is done in two languages \u2014 <strong>golang and php<\/strong>. This allows us to keep the libraries consistent. The generator is written in golang, which is why it received the name gogi.<\/p>\n<p>Event-sourcing on Kafka is typical. There is a solution from the main enterprise version Kafka Confluent, and there is <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/nakadi\">nakadi<\/a><\/noindex>, a solution from our 'brothers' in the domain area, Zalando. Our <strong>motivation to start with vanilla Kafka<\/strong>\u00a0\u2014 is to keep the solution free until we decide whether we will use it universally, as well as to allow ourselves some room for maneuver and improvements: we want support for our <strong>JSON RPC 2.0<\/strong>, generators for two languages, and we will see what else. <\/p>\n<p>Ironically, even in such a fortunate case, where there is a fairly similar business, Zalando, which made a similar solution, we cannot effectively utilize it. <\/p>\n<p>Architecturally, at launch, the pattern is as follows: we read directly from Kafka but write only through the events-bus. For reading from Kafka, there are many ready-made solutions: brokers, load balancers, and it is more or less ready for horizontal scaling, which we wanted to preserve. For writing, we wanted to wrap it through one Gateway aka Events-bus, and here\u2019s why.<\/p>\n<h3>Events-bus<\/h3>\n<p>\nOr event bus. This is just a stateless HTTP gateway that takes on several important roles:<\/p>\n<ul>\n<li><strong>Validation of producing<\/strong>\u00a0\u2014 we check that the events meet our specification.<\/li>\n<li><strong>Master system for events<\/strong>, meaning this is the main and only system in the company that answers the question of which events with which structures are considered valid. Validation includes just data types and enums for strict content specification. <\/li>\n<li><strong>Hash function<\/strong> for sharding \u2014 the structure of the Kafka message is key-value, and here the hash of the key is calculated to determine where to place it.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Why<\/h3>\n<p>\nWe work in a large company with an established process. Why change anything? <strong>This is an experiment<\/strong>, and we expect to gain several advantages.<\/p>\n<h4>1:n+1 exchanges (one to many)<\/h4>\n<p>\nWith Kafka, connecting new consumers to the API is very simple. <\/p>\n<p>Recimo da imate imenik koji treba odr\u017eavati a\u017eurnim u nekoliko sistema odjednom (i u nekim novim). Nekada smo izmi\u0161ljali bundle koji je implementirao set-API, a majstor-sistemu smo javljali adrese korisnika. Sada majstor-sistema \u0161alje a\u017euriranja na temu, a svi koji su zainteresovani \u010ditaju. Pojavila se nova sistema - prijavili smo je na temu. Da, opet bundle, ali jednostavnije.<\/p>\n<p>U slu\u010daju refund-alata, koji je u su\u0161tini deo BOB-a, zgodno nam je da ih odr\u017eavamo sinhronizovanim putem Kafke. Payment ka\u017ee da je novac vra\u0107en: BOB, RT su o tome obave\u0161teni, promenili su svoje statuse, a Fiscalization Service je saznao i izdao fiskalni \u010dek.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/b01b22a333b58e87aeef0c52d40e6960.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nImamo planove da napravimo jedinstveni Notifications Service, koji bi obave\u0161tavao klijenta o novostima vezanim za njegovu porud\u017ebinu\/vratiti. Sada je ta odgovornost raspr\u0161ena izme\u0111u sistema. Bi\u0107e dovoljno da nau\u010dimo Notifications Service da hvata relevantne informacije iz Kafke i reaguje na njih (i isklju\u010dimo ove obave\u0161tenja u ostalim sistemima). Nema potrebe za novim direktnim razmenama.<\/p>\n<h4>Podaci vo\u0111eni<\/h4>\n<p>\nInformacije izme\u0111u sistema postaju transparentne - kakav god 'krvavi enterprise' da imate i kakav god debeli backlog da imate. U Lamodi postoji odeljenje za analizu podataka koje prikuplja podatke iz sistema i pretvara ih u ponovo upotrebljiv oblik, kako za biznis, tako i za inteligentne sisteme. Kakfa omogu\u0107ava brzo pru\u017eanje velikih podataka i odr\u017eavanje ovog informativnog toka aktuelnim.<\/p>\n<h4>Replikacijski log<\/h4>\n<p>\nPoruke ne nestaju nakon \u010ditanja, kao u RabbitMQ-u. Kada doga\u0111aj sadr\u017ei dovoljno informacija za obradu, dobijamo istoriju poslednjih promena po objektu, i, ako to \u017eelimo, mogu\u0107nost da te promene primenimo.<\/p>\n<p>Rok \u010duvanja replikacijskog loga zavisi od intenziteta zapisa u ovu temu, Kafka omogu\u0107ava fleksibilno pode\u0161avanje granica po vremenskom periodu i po obimu podataka. Za intenzivne teme va\u017eno je da svi potro\u0161a\u010di stignu da pro\u010ditaju informacije pre nego \u0161to nestanu, \u010dak i u slu\u010daju kratkotrajnog nefunkcionisanja. Obi\u010dno uspevamo da \u010duvamo podatke za\u00a0<strong>jedinice dana<\/strong>, \u0161to je sasvim dovoljno za podr\u0161ku. <\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/0e08dd384155289123ebee96430c2370.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDalje slede\u0107i malo prepri\u010dane dokumentacije, za one koji nisu upoznati sa Kafka (slika je tako\u0111e iz dokumentacije)<\/p>\n<p>V AMQP so vrste, ki jih pi\u0161emo v \u010dakalno vrsto za porabnika. Obi\u010dajno eno \u010dakalno vrsto obdeluje en sistem z enako poslovno logiko. \u010ce je potrebno obvestiti ve\u010d sistemov, lahko aplikacijo nau\u010dimo pisati v ve\u010d \u010dakalnih vrst ali pa nastavimo exchange z mehanizmom fanout, ki jih sam klonira.<\/p>\n<p>V Kafki obstaja podobna abstrakcija <em>topic<\/em>, v katero pi\u0161ete sporo\u010dila, vendar ta ne izginejo po branju. Privzeto, ko se pove\u017eete s Kafko, prejmete vsa sporo\u010dila, hkrati pa imate mo\u017enost ohraniti mesto, kjer ste prenehali. Torej berete zaporedno, lahko ne ozna\u010dite sporo\u010dila kot prebranega, ampak ohranite id, s katerega boste nato nadaljevali z branjem. Id, na katerem ste prenehali, se imenuje offset, mehanizem pa commit offset. <\/p>\n<p>Ustrezno lahko izvedemo razli\u010dne logike. Na primer, na\u0161 BOB deluje v 4 instancah za razli\u010dne dr\u017eave \u2013 Lamoda je prisotna v Rusiji, Kazahstanu, Ukrajini in Belorusiji. Ker se te delijo lo\u010deno, imajo nekoliko svoje konfiguracije in svojo poslovno logiko. V sporo\u010dilu navedemo, kateri dr\u017eavi pripada. Vsak porabnik BOB v vsaki dr\u017eavi bere z razli\u010dnimi groupId, in \u010de sporo\u010dilo ni zanj, ga presko\u010di, tj. takoj zaklju\u010di offset +1. \u010ce isti topic bere na\u0161 Payment Service, to stori z lo\u010deno skupino, zato offseti ne sovpadajo.<\/p>\n<p><b>Zahteve za dogodke:<\/b><\/p>\n<ul>\n<li><strong>Popolnost podatkov. <\/strong>\u017deleli bi, da bi imel dogodek dovolj podatkov, da ga je mogo\u010de obdelati. <\/li>\n<\/ul>\n<p><\/p>\n<ul>\n<li><strong>Celovitost. <\/strong>Dele\u017eujemo Events-bus preverjanje, da je dogodek dosleden in ga lahko obdelajo.<\/li>\n<li><strong>Vrstni red je pomemben. <\/strong>V primeru vra\u010dila smo prisiljeni delati s zgodovino. Pri obvestilih vrstni red ni pomemben, \u010de so to enotna obvestila, email bo enak ne glede na to, kateri naro\u010dilo je pri\u0161lo prvo. V primeru vra\u010dila pa imamo jasen postopek, sprememba vrstnega reda lahko privede do izjem, refund ne bo ustvarjen ali obdelan \u2013 pri\u0161li bomo v drug status.<\/li>\n<li><strong>Konsistentnost. <\/strong>Imamo skladi\u0161te, in zdaj namesto API-jev kreiramo dogodke. Potrebujemo na\u010din, kako hitro in poceni prena\u0161ati informacije o novih dogodkih in spremembah obstoje\u010dih v na\u0161e storitve. To dose\u017eemo s skupno specifikacijo v posebnem git repozitoriju in generatorjih kode. Zato so na\u0161i odjemalci in stre\u017eniki v razli\u010dnih storitvah usklajeni.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Kafka v Lamodi<\/h2>\n<p>\nImamo tri namestitve Kafke: <\/p>\n<ol>\n<li>Dnevniki;<\/li>\n<li>R&amp;D;<\/li>\n<li>Dogodkovna vozila.<\/li>\n<\/ol>\n<p>\nDanes govorimo samo o zadnji to\u010dki. V dogodkovnem vozilu imamo relativno majhne namestitve \u2013 3 posrednike (stre\u017enike) in skupaj 27 tem. Na splo\u0161no je ena tema en proces. Ampak to je ob\u010dutna zadeva, in sedaj se ji bomo posvetili.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/f398852689b31429cc97b4cbffcabab5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nZgoraj je graf rps. Proces vra\u010dil je ozna\u010den s turkizno \u010drto (ja, tisto, ki le\u017ei na osi X), roza pa proces posodabljanja vsebin. <\/p>\n<p>Katalog Lamoda vsebuje milijone izdelkov, podatki pa se neprestano posodabljajo. Nekatere kolekcije izstopajo iz mode, na njihovo mesto pa prihajajo nove, v katalogu nenehno nastajajo novi modeli. Trudimo se napovedati, kaj bo zanimivo na\u0161im strankam jutri, zato nenehno kupujemo nove stvari, jih fotografiramo in posodabljamo izlo\u017ebo. <\/p>\n<p>Roza vrhovi so posodobitve izdelkov, torej spremembe glede izdelkov. Vidimo, da so fantje fotografirali, fotografirali, nato pa bum! \u2014 nalo\u017eili paket dogodkov.<\/p>\n<h2>Uporabni primeri Lamoda dogodkov<\/h2>\n<p>\nZgrajeno arhitekturo uporabljamo za tak\u0161ne operacije:<\/p>\n<ul>\n<li><strong>Spremljanje statusov vra\u010dil<\/strong>: poziv k akciji in sledenje statusom iz vseh vpletenih sistemov. Pla\u010dilo, statusi, fiskalizacija, obvestila. Tukaj smo preizkusili pristop, naredili orodja, zbrali vse napake, napisali dokumentacijo in povedali kolegom, kako to uporabljati.<\/li>\n<li><strong>Posodobitev kartic izdelkov: <\/strong>konfiguracija, meta-podatki, lastnosti. En sistem bere (ki prikazuje), ve\u010d njih pa pi\u0161e.<\/li>\n<li><strong>Email, push in SMS<\/strong>: naro\u010dilo je bilo zbrano, naro\u010dilo je prispelo, vra\u010dilo je sprejeto itd., veliko jih je. <\/li>\n<li><strong>Zaloga, posodobitev skladi\u0161\u010da<\/strong>\u00a0\u2014 kvantitativna posodobitev nazivov, samo \u0161tevilke: prejeme na skladi\u0161\u010de, vra\u010dila. Vse sisteme, povezane z rezervacijo izdelkov, je potrebno obve\u0161\u010dati z najbolj\u0161imi aktualnimi podatki. Trenutno je sistem posodabljanja zaloge precej zapleten, Kafka pa ga bo poenostavila.<\/li>\n<li><strong>Analiza podatkov<\/strong> (R&amp;D oddelek), ML orodja, analitika, statistika. \u017delimo, da je informacija pregledna - za to je Kafka dobro re\u0161itev.<\/li>\n<\/ul>\n<p>\nZdaj pa zanimivej\u0161i del o prehojenih potkah in zanimivih odkritjih, ki so se zgodila v pol leta.<\/p>\n<h2>Problemi s dizajnom<\/h2>\n<p>\nRecimo, da \u017eelimo narediti novo stvar - na primer, prenesti celoten postopek dostave na Kafka. Trenutno je del procesa realiziran v Order Processing v BOB. Za prenos naro\u010dila v dostavno slu\u017ebo, premikanje na vmesno skladi\u0161\u010de in druge stvari stoji statusna model. Imajo celoten monolit, celo dva, plus kup API-jev, posve\u010denih dostavi. Vse to ve o dostavi veliko ve\u010d. <\/p>\n<p>Zdi se, da so to podobna podro\u010dja, a za Order Processing v BOB in dostavni sistem se statusi razlikujejo. Na primer, nekatere kurirske slu\u017ebe ne posredujejo vmesnih statusov, temve\u010d le kon\u010dne: 'dostavljeno' ali 'izgubljeno'. Druge pa zelo podrobno poro\u010dajo o premikanju blaga. Vsaka ima svoja pravila validacije: za nekatere je veljaven email, kar pomeni, da ga bodo obdelali; za druge je neveljaven, a bo naro\u010dilo vseeno obdelano, ker je na voljo telefon za stik, nekdo drug pa bo dejal, da takega naro\u010dila sploh ne bodo obdelali.<\/p>\n<h3>Tok podataka<\/h3>\n<p>\nV primeru Kafka se poraja vpra\u0161anje organizacije pretoka podatkov. Ta naloga je povezana z izbiro strategije po ve\u010d to\u010dkah, poglejmo jih vse.<\/p>\n<h4>V eno temo ali v razli\u010dne?<\/h4>\n<p>\nImamo specifikacijo dogodka. V BOB pi\u0161emo, da je treba dostaviti dolo\u010deno naro\u010dilo, in navedemo: \u0161tevilko naro\u010dila, njegovo vsebino, razli\u010dne SKU in \u010drtne kode itd. Ko blago prispe v skladi\u0161\u010de, lahko dostava pridobi statuse, \u010dasovne oznake in vse, kar je potrebno. A nato \u017eelimo v BOB prejemati posodobitve o teh podatkih. Tu se pojavi obratni proces pridobivanja podatkov iz dostave. Ali je to isti dogodek? Ali gre za lo\u010deno izmenjavo, ki si zaslu\u017ei lo\u010deno temo?<\/p>\n<p>Najverjetneje bodo mo\u010dno podobni in sku\u0161njava, da ustvarimo eno temo, ni neutemeljena, saj lo\u010dena tema pomeni lo\u010dene potro\u0161nike, lo\u010dene nastavitve, lo\u010deno generiranje vsega tega. A to ni nujno.<\/p>\n<h4>Novo polje ali nov dogodek?<\/h4>\n<p>\n\u010ce pa uporabimo ista dogodka, se pojavi drug problem. Na primer, ne vse dostavne sisteme lahko generirajo tak DTO, ki bi lahko generiral BOB. Po\u0161iljamo jim id, oni pa ga ne shranijo, ker ga ne potrebujejo, kar pa je z vidika za\u010detka procesa event-bus obvezno polje. <\/p>\n<p>\u010ce za event-bus vpeljemo pravilo, da je to polje obvezno, smo prisiljeni v BOB ali v obdelovalcu za\u010detnega dogodka dodati dodatna pravila za validacijo. Validacija se za\u010dne \u0161iriti po storitvi, kar ni zelo priro\u010dno.<\/p>\n<p>\u0160e en problem je sku\u0161njava inkrementalnega razvoja. Pravijo nam, da moramo dodati nekaj v dogodek in, morda, \u010de dobro razmislimo, bi to moralo biti poseben dogodek. V na\u0161i shemi je poseben dogodek poseben topic. Poseben topic pomeni celoten proces, ki sem ga opisal zgoraj. Razvijalcu nastane sku\u0161njava, da enostavno doda \u0161e eno polje v JSON shemo in ponovno generira.<\/p>\n<p>V primeru refunds smo tako po \u0161estih mesecih pri\u0161li do dogodka dogodkov. Imeli smo eno meta-dogodke, ki se imenuje refund update, v katerem je bilo polje type, ki opisuje, v \u010dem ta update sploh je. Imeli smo \"\u010dudovite\" preklopnike z validatorji, ki so govorili, kako je treba validirati ta dogodek s tem type.<\/p>\n<h4>Versioniranje dogodkov<\/h4>\n<p>\nZa validacijo sporo\u010dil v Kafka lahko uporabimo <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.confluent.io\/current\/schema-registry\/docs\/index.html\">Avro<\/a><\/noindex>, a takoj je bilo treba to vklju\u010diti in uporabiti Confluent. V na\u0161em primeru z versioniranjem moramo biti previdni. Ni vedno mogo\u010de ponovno prebrati sporo\u010dil iz replication log, ker je model \"od\u0161el\". Na splo\u0161no se trudimo zgraditi razli\u010dice tako, da je model povratno zdru\u017eljiv: na primer, da za\u010dasno naredimo polje neobvezno. \u010ce so razlike prevelike, za\u010dnemo pisati v nov topic, stranke pa preselimo, ko preberejo starega.<\/p>\n<h4>Jamstvo o vrstnem redu branja partitions<\/h4>\n<p>\nTopics znotraj Kafka so razdeljeni na partitions. To ni zelo pomembno, dokler projektiramo entitete in izmenjave, je pa pomembno, ko odlo\u010damo, kako to porabiti in raz\u0161iriti.<\/p>\n<p>V obi\u010dajnem primeru napi\u0161ete en topic v Kafka. Privzeto se uporablja en partition, in vse sporo\u010dila tega topica gredo vanj. Kon\u010dna porabnik posledi\u010dno to sporo\u010dilo bere zaporedno. Recimo, da zdaj morate raz\u0161iriti sistem, tako da sporo\u010dila berejo dva razli\u010dna kon\u010dna porabnika. \u010ce, na primer, po\u0161iljate SMS, lahko re\u010dete Kafki, da naredi dodaten partition, in Kafka bo za\u010dela razporejati sporo\u010dila v dve enoti - polovico tja, polovico sem. <\/p>\n<p>Kako Kafka deli sporo\u010dila? Vsako sporo\u010dilo ima telo (v katerem hranimo JSON) in ima klju\u010d. Na ta klju\u010d lahko uporabite hash funkcijo, ki bo dolo\u010dila, v kateri partition bo sporo\u010dilo pristalo.<\/p>\n<p>V na\u0161em primeru z refundacijami je to pomembno, \u010de vzamemo dva partition, obstaja mo\u017enost, da bo paralelni kon\u010dni porabnik obdelal drugo dogajanje pred prvim in bo pri\u0161lo do te\u017eav. Hash funkcija zagotavlja, da sporo\u010dila z enakim klju\u010dem pridejo v enako partition. <\/p>\n<h4>Dogodki vs ukazi<\/h4>\n<p>\nTo je \u0161e ena te\u017eava, s katero se sre\u010dujemo. Dogodek je neko dogajanje: pravimo, da se je nekaj zgodilo (something_happened), na primer, item je bil preklican ali je nastala refundacija. \u010ce ta dogodka nekdo poslu\u0161a, bo ob 'item preklican' entiteta refundacija ustvarjena, medtem ko se ob 'nastala refundacija' zabele\u017ei nekje v nastavitvah.<\/p>\n<p>Toda obi\u010dajno, ko na\u010drtujete dogodke, no\u010dete pisati brez pomisleka - predvidevate, da jih bo nekdo bral. Velika sku\u0161njava je zapisati ne something_happened (item_canceled, refund_refunded), ampak something_should_be_done. Na primer, item je pripravljen za vra\u010dilo.<\/p>\n<p>Z ene strani to nakazuje, kako bo dogodek uporabljen. Z druge strani pa to veliko manj spominja na obi\u010dajno ime dogodka. Poleg tega, tukaj \u017ee ni dale\u010d do ekipe do_something. Vendar pa nimate zagotovila, da je ta dogodek nekdo prebral; in \u010de je prebral, je bil uspe\u0161no prebran; in \u010de je bil uspe\u0161no prebran, je naredil nekaj, in to nekaj je potekalo uspe\u0161no. V trenutku, ko dogodek postane do_something, postane potrebna povratna informacija, in to je te\u017eava.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/b755d91208092bd9791a41ce4633fb48.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nV asinhronem izmenjavanju v RabbitMQ, ko preberete sporo\u010dilo, greste na http, imate odgovor - vsaj, da je bilo sporo\u010dilo sprejeto. Ko zapi\u0161ete v Kafka, dobite sporo\u010dilo, da ste zapisali v Kafka, vendar o tem, kako je bilo obdelano, ne veste ni\u010d. <\/p>\n<p>Zato smo v na\u0161em primeru morali vnesti odzivni dogodek in nastaviti nadzor na to, da \u010de se zgodi toliko dogodkov, bi moralo skozi tolik\u0161no \u010dasovno obdobje priti enako \u0161tevilo odzivnih dogodkov. \u010ce se to ne zgodi, izgleda, da je \u0161lo nekaj narobe. Na primer, \u010de po\u0161ljemo dogodek \u201eitem_ready_to_refund\u201c, pri\u010dakujemo, da se bo refundacija ustvarila, stranki se bodo vrnili denar, prejet bo dogodek \u201emoney_refunded\u201c. Ampak to ni nujno, zato potrebujemo nadzor.<\/p>\n<h3>Nianse<\/h3>\n<p>\nObstaja precej o\u010diten problem: \u010de berete zaporedoma z vrstice, in imate kak\u0161no slabo sporo\u010dilo, potro\u0161nik pade in naprej ne morete. Potrebno je <strong>ustaviti vse potro\u0161nike<\/strong>, nadaljevati s komitiranjem offseta, da lahko nadaljujete z branjem.<\/p>\n<p>Vedeli smo za to, predvideli smo to in kljub temu se je zgodilo. To se je zgodilo, ker je bilo sporo\u010dilo veljavno z vidika events-bus, sporo\u010dilo je bilo veljavno z vidika validatorja aplikacije, vendar ni bilo veljavno z vidika PostgreSQL, ker imamo v enem sistemu MySQL z UNSIGNED INT, v sve\u017ee napisanem sistemu pa je bil PostgreSQL preprosto z INT. Njegova velikost je nekoliko manj\u0161a, in Id ni ustrezal. Symfony je umrl s izjemo. Seveda smo ulovili izjemo, ker smo se nanjo pripravili, in \u017eeleli smo komitirati ta offset, toda pred tem smo \u017eeleli inkrementirati \u0161tevec te\u017eav, saj se je sporo\u010dilo obdelalo neuspe\u0161no. \u0160tevec v tem projektu je prav tako shranjen v bazi, Symfony pa je \u017ee prenehal komunicirati z bazo, in druga izjema je uni\u010dila celoten postopek brez mo\u017enosti komitiranja offseta.<\/p>\n<p>Nekaj \u010dasa je storitev stala - na sre\u010do s Kafka to ni tako stra\u0161ljivo, ker ostanejo sporo\u010dila. Ko se delo obnovi, jih bo mogo\u010de prebrati. To je priro\u010dno.<\/p>\n<p>Kafka omogo\u010da preko orodij nastaviti poljuben offset. Vendar pa je za to potrebno ustaviti vse potro\u0161nike - v na\u0161em primeru pripraviti posebno razli\u010dico, v kateri ne bo potro\u0161nikov, redeployments. Takrat lahko preko orodij v Kafka premaknemo offset, in sporo\u010dilo bo pre\u0161lo.<\/p>\n<p>Druga niansa je <strong>replication log vs rdkafka.so<\/strong>\u00a0\u2014 povezana je s specifi\u010dnostima na\u0161eg projekta. Imamo PHP, a u PHP-u, kao pravilo, sve biblioteke komuniciraju s Kafka putem repozitorija rdkafka.so, a dalje ide neka oblaganje. Mo\u017eda su to na\u0161e li\u010dne pote\u0161ko\u0107e, ali se pokazalo da jednostavno pro\u010ditati deo 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 sam o tome saznao mnogo kasnije nego \u0161to bih \u017eeleo. Ako \u017eelite da se skalirate i imate dva konzumer, potrebno je najmanje dva partitions. To jest, ako ste imali jedan partition, u kojem se skupilo 20.000 poruka, i napravili ste novi, broj poruka ne\u0107e se izjedna\u010diti uskoro. Dakle, da biste imali dva paralelna konzumer, morate se pozabaviti partitions.<\/p>\n<h2>Nadzor<\/h2>\n<p>\nMislim da \u0107e po na\u010dinu na koji pratimo biti jo\u0161 jasnije koje probleme imamo s postoje\u0107im pristupom.<\/p>\n<p>Na primer, broji\u0107emo koliko je proizvoda u bazi nedavno promenilo status, i, prema tim promenama, trebali su se dogoditi doga\u0111aji, i \u0161aljemo ovaj broj u na\u0161 sistem pra\u0107enja. Zatim iz Kafke dobijamo drugi broj, koliko je zapravo zabele\u017eeno doga\u0111aja. O\u010digledno, razlika izme\u0111u ova dva broja uvek bi trebala biti nulta.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/07d8ed08514f2fb97d9019466b96342c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPored toga, treba pratiti kako je kod producenta, je li events-bus primio poruke, i kako stoji kod konzumer. Na primer, na grafikonima ispod, kod Refund Tool sve je u redu, dok kod BOB-a o\u010digledno postoje neki problemi (plavi vrhovi).<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/57112dbe70d2b388c53f19c34cca6f63.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nVe\u0107 sam pomenuo 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 kratkotrajni vrhunac. Kafka to mo\u017ee iz kutije, ali treba postaviti neki interval. <\/p>\n<p>Postoji projekat <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/linkedin\/Burrow\">Burrow<\/a><\/noindex>, koji \u0107e vam dati vi\u0161e informacija o Kafki. On jednostavno putem API-ja po consumer-group daje status, kako se toj grupi prolazi. Pored OK i Failed, tu je i warning, i mo\u017eete saznati da va\u0161i konzumeri ne uspevaju da dr\u017ee tempo produkcije \u2014 ne stignu da obradi ono \u0161to se pi\u0161e. Sistem je prili\u010dno pametan, lako ga je koristiti. <\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim 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-ja. Ovde je grupa bob-live-fifa, partition refund.update.v1, status OK, lag 0 \u2014 poslednji kona\u010dni offset je takav.<\/p>\n<p><img decoding=\"async\" alt=\"Iskustvo razvoja servisa Refund Tool sa asinhronim API-jem na Kafki.\" src=\"\/wp-content\/uploads\/2019\/04\/1538b2ccc390e9b83075f54566f1039b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNadzor <strong>updated_at SLA (stuck)<\/strong> \u017de sem \u017ee omenil. Na primer, izdelek je pre\u0161el v stanje, da je pripravljen za vra\u010dilo. Nastavimo Cron, ki pove, da \u010de ta predmet v 5 minutah ne preide v refund (denar vrnemo prek pla\u010dilnih sistemov zelo hitro), potem nekaj zagotovo ni v redu in to je zagotovo primer za podporo. Zato preprosto vzamemo Cron, ki bere take stvari, in \u010de so ve\u010dje od 0, po\u0161lje opozorilo.<\/p>\n<p><b>Za zaklju\u010dek, uporaba dogodkov je priro\u010dna, kadar<\/b>:<\/p>\n<ul>\n<li>informacije potrebujemo ve\u010d sistemom;<\/li>\n<li>rezultat obdelave ni pomemben;<\/li>\n<li>je dogodkov malo ali so dogodki majhni. <\/li>\n<\/ul>\n<blockquote><p>Zdi se, da ima \u010dlanek precej konkretno temo - asinhroni API na Kafka, vendar v zvezi s tem \u017eelim takoj priporo\u010diti veliko stvari.<br \/>\nNajprej, naslednji <noindex><a rel=\"nofollow\" href=\"https:\/\/www.highload.ru\/\">HighLoad++<\/a><\/noindex> na treba \u010dakati do novembra, \u017ee aprila bo njegova peter\u0161ka razli\u010dica, junija pa se bomo pogovarjali o visokih obremenitvah v Novosibirsku.<br \/>\nDrugi\u010d, avtor poro\u010dila Sergej Zaika je \u010dlan Programskega odbora na\u0161e nove konference o upravljanju znanja. <noindex><a rel=\"nofollow\" href=\"https:\/\/knowledgeconf.ru\/2019\">KnowledgeConf<\/a><\/noindex>Konferenca traja en dan, potekala bo 26. aprila, a program je zelo bogat.<br \/>\nIn \u0161e maja bo <noindex><a rel=\"nofollow\" href=\"https:\/\/phprussia.ru\/2019\">PHP Russia<\/a><\/noindex> in\u00a0<noindex><a rel=\"nofollow\" href=\"https:\/\/ritfest.ru\/2019\">RIT++<\/a><\/noindex> (s DevOpsConf v sestavi) - tam je \u0161e vedno mogo\u010de predlo\u017eiti svojo temo, spregovoriti o svoji izku\u0161nji in se prito\u017eiti nad svojimi izku\u0161njami.<\/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\/sl\/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=\"sl_SI\" \/>\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\/sl\/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\udd47Izku\u0161nje pri razvoju storitve Refund Tool z asinhronim API-jem na Kafka | ProHoster","description":"Kaj lahko privede do tako velike podjetja, kot je Lamoda, s preverjenim procesom in desetimi medsebojno povezanimi storitvami, da radikalno spremeni pristop?","canonical_url":"https:\/\/prohoster.info\/sl\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sl_SI","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\/sl\/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\/sl\/wp-json\/wp\/v2\/posts\/30778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/comments?post=30778"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/posts\/30778\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/media\/22763"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/media?parent=30778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/categories?post=30778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sl\/wp-json\/wp\/v2\/tags?post=30778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}