{"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\/sk\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","title":{"rendered":"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>\u010co m\u00f4\u017ee n\u00fati\u0165 tak\u00fa ve\u013ek\u00fa spolo\u010dnos\u0165 ako Lamoda s prec\u00edzne nastaven\u00fdm procesom a desiatkami vz\u00e1jomne prepojen\u00fdch slu\u017eieb z\u00e1sadne zmeni\u0165 pr\u00edstup? Motiv\u00e1cia m\u00f4\u017ee by\u0165 \u00faplne r\u00f4zna: od legislat\u00edvnych d\u00f4vodov po t\u00fa\u017ebu v\u0161etk\u00fdch program\u00e1torov experimentova\u0165.<\/p>\n<p>Ale to rozhodne neznamen\u00e1, \u017ee sa ned\u00e1 po\u010d\u00edta\u0165 s dodato\u010dn\u00fdm ziskom. O \u010dom konkr\u00e9tne m\u00f4\u017eete z\u00edska\u0165, ak implementujete events-driven API na Kafka, v\u00e1m povie Sergej Zaika (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/fewald\/\" class=\"user_link\">fewald<\/a><\/noindex>). O zraneniach a zauj\u00edmav\u00fdch objavoch sa takisto ur\u010dite dozviete \u2013 experimenty sa bez nich nezaob\u00eddu.<\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/7ab959ab45ec5c6565b35b18b361c0ea.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<em>Disclaimer: Tento \u010dl\u00e1nok je zalo\u017een\u00fd na materi\u00e1loch meetupu, ktor\u00fd Sergej uskuto\u010dnil v novembri 2018 na HighLoad++. Sk\u00fasenosti Lamoda s Kafka pritiahli posluch\u00e1\u010dov rovnako ako ostatn\u00e9 predn\u00e1\u0161ky v programe. Mysl\u00edme si, \u017ee je to v\u00fdborn\u00fd pr\u00edklad toho, \u017ee v\u017edy je mo\u017en\u00e9 a potrebn\u00e9 n\u00e1js\u0165 priate\u013eov v obore, a organiz\u00e1tori HighLoad++ bud\u00fa na\u010falej usilova\u0165 o vytvorenie takej atmosf\u00e9ry.<\/em><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>O procese<\/h2>\n<p>\nLamoda je ve\u013ek\u00e1 e-commerce platforma, ktor\u00e1 m\u00e1 svoj kontaktn\u00fd centr\u00e1, doru\u010dovaciu slu\u017ebu (a mno\u017estvo partnerov), fotost\u00fadio, obrovsk\u00fd sklad a v\u0161etko to pracuje na svojom softv\u00e9ri. Existuje desiatky sp\u00f4sobov platby, b2b-partneri, ktor\u00ed m\u00f4\u017eu vyu\u017e\u00edva\u0165 \u010das\u0165 alebo v\u0161etky tieto slu\u017eby a chc\u00fa pozna\u0165 aktu\u00e1lne inform\u00e1cie o svojich produktoch. Okrem toho Lamoda p\u00f4sob\u00ed v troch krajin\u00e1ch mimo RF a tam to v\u0161etko funguje tro\u0161ku inak. Celkovo teda existuje asi viac ako sto sp\u00f4sobov, ako konfigurova\u0165 nov\u00fa objedn\u00e1vku, ktor\u00e1 mus\u00ed by\u0165 spracovan\u00e1 po svojom. V\u0161etko to pracuje s pomocou desiatok slu\u017eieb, ktor\u00e9 niekedy komunikuj\u00fa sp\u00f4sobom, ktor\u00fd nie je zjavn\u00fd. Existuje aj centr\u00e1lna syst\u00e9m, ktor\u00e9ho hlavnou zodpovednos\u0165ou s\u00fa stavy objedn\u00e1vok. Naz\u00fdvame to BOB, pracujem s n\u00edm.<\/p>\n<h2>Refund Tool with events-driven API <\/h2>\n<p>\nSlovo events-driven je u\u017e dos\u0165 pou\u017e\u00edvan\u00e9, nesk\u00f4r presnej\u0161ie definujeme, \u010do to znamen\u00e1. Za\u010dnem kontextom, v ktorom sme sa rozhodli vysk\u00fa\u0161a\u0165 pr\u00edstup events-driven API v Kafka. <\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/3bfdce47dd8420fc63d84645e76de647.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nV akomko\u013evek obchode, okrem objedn\u00e1vok, za ktor\u00e9 z\u00e1kazn\u00edci platia, s\u00fa aj momenty, ke\u010f obchod mus\u00ed vr\u00e1ti\u0165 peniaze, preto\u017ee z\u00e1kazn\u00edkovi produkt nevyhovoval. Tento relat\u00edvne kr\u00e1tky proces spo\u010d\u00edva v overen\u00ed inform\u00e1ci\u00ed, ak je to potrebn\u00e9, a v prevode pe\u0148az\u00ed. <\/p>\n<p>Av\u0161ak vr\u00e1tenie sa skomplikovalo z d\u00f4vodu zmien v legislat\u00edve, a preto sme museli implementova\u0165 samostatn\u00fa mikroslu\u017ebu na tento \u00fa\u010del.<\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e358df5476e7448976e0f1147a103fb.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNa\u0161a motiv\u00e1cia:<\/p>\n<ol>\n<li><strong>Z\u00e1kon FZ-54<\/strong>\u00a0\u2014 v skratke, z\u00e1kon vy\u017eaduje nahlasova\u0165 ka\u017edu finan\u010dn\u00fa transakciu, \u010di u\u017e ide o vr\u00e1tenie alebo prich\u00e1dzaj\u00face platby, v relat\u00edvne kr\u00e1tkom SLA, po\u010das nieko\u013ek\u00fdch min\u00fat. My, ako e-commerce, vykon\u00e1vame mno\u017estvo oper\u00e1ci\u00ed. Technicky to znamen\u00e1 nov\u00fa zodpovednos\u0165 (a teda nov\u00fa slu\u017ebu) a \u00fapravy vo v\u0161etk\u00fdch dotknut\u00fdch syst\u00e9moch.<\/li>\n<li><strong>BOB split<\/strong>\u00a0\u2014 intern\u00fd projekt spolo\u010dnosti na zbavenie BOB mno\u017estva nepotrebn\u00fdch zodpovednost\u00ed a zn\u00ed\u017eenie jeho celkovej zlo\u017eitosti.<\/li>\n<\/ol>\n<p>\n<img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/d3ecf9961bdb372fc5f84ee9389f73ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNa tejto sch\u00e9me s\u00fa zn\u00e1zornen\u00e9 hlavn\u00e9 syst\u00e9my Lamoda. V s\u00fa\u010dasnosti v\u00e4\u010d\u0161ina z nich predstavuje sk\u00f4r <strong>kon\u0161tel\u00e1ciu z 5-10 mikroslu\u017eieb okolo sa zmen\u0161uj\u00faceho monolitu<\/strong>. Pomaly rast\u00fa, ale sna\u017e\u00edme sa ich zmen\u0161ova\u0165, preto\u017ee je desiv\u00e9 nasadzova\u0165 vy\u010dlenen\u00fd fragment uprostred \u2014 nesmieme dopusti\u0165, aby zlyhal. V\u0161etky v\u00fdmeny (\u0161\u00edpky) mus\u00edme rezervova\u0165 a po\u010d\u00edta\u0165 s t\u00fdm, \u017ee ktor\u00fdko\u013evek z nich m\u00f4\u017ee by\u0165 nedostupn\u00fd.<\/p>\n<p>V BOB tie\u017e existuje pomerne ve\u013ea v\u00fdmen: syst\u00e9my platieb, dod\u00e1vok, notifik\u00e1ci\u00ed at\u010f. <\/p>\n<p>Technicky je BOB:<\/p>\n<ul>\n<li>~150k riadkov k\u00f3du + ~100k riadkov testov;<\/li>\n<li>php7.2 + Zend 1 &amp; Symfony Components 3;<\/li>\n<li>&gt;100 API &amp; ~50 odch\u00e1dzaj\u00facich integr\u00e1ci\u00ed;<\/li>\n<li>4 krajiny s vlastnou biznis logikou. <\/li>\n<\/ul>\n<p>\nNasadzovanie BOB je n\u00e1kladn\u00e9 a bolestiv\u00e9, po\u010det k\u00f3du a \u00faloh, ktor\u00e9 rie\u0161i, je tak\u00fd, \u017ee nikto ho nem\u00f4\u017ee ma\u0165 v hlave cel\u00fd. V skratke, existuje ve\u013ea d\u00f4vodov na jeho zjednodu\u0161enie.<\/p>\n<h2>Proces vr\u00e1tenia<\/h2>\n<p>\nP\u00f4vodne s\u00fa do procesu zapojen\u00e9 dva syst\u00e9my: BOB a Payment. Teraz prib\u00fadaj\u00fa \u010fal\u0161ie dva:<\/p>\n<ul>\n<li>Fiskaliz\u00e1cie Slu\u017eba, ktor\u00e1 preberie probl\u00e9my s fiskaliz\u00e1ciou a komunik\u00e1ciu s extern\u00fdmi slu\u017ebami.<\/li>\n<li>Refund Tool, do ktor\u00e9ho sa jednoducho prevez\u00fa nov\u00e9 v\u00fdmeny, aby sa nepreh\u00e1\u0148al BOB.<\/li>\n<\/ul>\n<p>\nTeraz proces vyzer\u00e1 takto:<\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/13c02975881ad35c61304053c604cda3.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ol>\n<li>K BOB prich\u00e1dza \u017eiados\u0165 o vr\u00e1tenie pe\u0148az\u00ed.<\/li>\n<li>BOB o tom informuje Refund Tool.<\/li>\n<li>Refund Tool hovor\u00ed Payment: \u201eVr\u00e1\u0165 peniaze\u201c.<\/li>\n<li>Payment vr\u00e1ti peniaze.<\/li>\n<li>Refund Tool a BOB synchronizuj\u00fa medzi sebou stavy, preto\u017ee s\u00fa obom moment\u00e1lne potrebn\u00e9. E\u0161te nie sme pripraven\u00ed \u00faplne prejs\u0165 na Refund Tool, preto\u017ee v BOB je UI, reporty pre \u00fa\u010dtovn\u00edctvo a v\u00f4bec ve\u013ea d\u00e1t, ktor\u00e9 nie je mo\u017en\u00e9 tak jednoducho prenies\u0165. Mus\u00edme sedie\u0165 na dvoch stoli\u010dk\u00e1ch.<\/li>\n<li>Odosiela sa \u017eiados\u0165 na fiskaliz\u00e1ciu.<\/li>\n<\/ol>\n<p>\nNakoniec sme vytvorili na Kafka ak\u00fdsi event-bus, na ktorom sa v\u0161etko viazalo. Hur\u00e1, teraz m\u00e1me jedin\u00e9 miesto zlyhania (sarkazmus).<\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/674edd7972998b4985071f5250612c7e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPlusy a m\u00ednusy s\u00fa celkom zrejm\u00e9. Urobili sme bus, tak\u017ee teraz v\u0161etky slu\u017eby od neho z\u00e1visia. To zjednodu\u0161uje navrhovanie, ale prin\u00e1\u0161a do syst\u00e9mu jedin\u00e9 miesto zlyhania. Ak sa Kafka zr\u00fati, proces sa zastav\u00ed.<\/p>\n<h2>\u010co je to events-driven API <\/h2>\n<p>\nDobr\u00fa odpove\u010f na t\u00fato ot\u00e1zku d\u00e1va prezent\u00e1cia Martina Fowl\u00e9ra (GOTO 2017) <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/STKCRSUsyPO\">\u201eMnoh\u00e9 v\u00fdznamy event-driven architekt\u00fary\u201c<\/a><\/noindex>. <\/p>\n<p>Stru\u010dne, \u010do sme urobili:<\/p>\n<ol>\n<li>Zabalili sme v\u0161etky asynchronic\u00e9 v\u00fdmeny cez <strong>events storage<\/strong>. Namiesto toho, aby sme oznamovali ka\u017ed\u00e9mu zainteresovan\u00e9mu spotrebite\u013eovi v sieti o zmene stavu, zapisujeme do centralizovan\u00e9ho \u00falo\u017eiska udalos\u0165 o zmene stavu, a zainteresovan\u00ed spotrebitelia \u010d\u00edtaj\u00fa v\u0161etko, \u010do sa tam objav\u00ed.<\/li>\n<li>Udalos\u0165 (event) v tomto pr\u00edpade je ozn\u00e1menie (<strong>notifications<\/strong>) o tom, \u017ee sa nie\u010do niekde zmenilo. Napr\u00edklad, zmenil sa stav objedn\u00e1vky. Spotrebite\u013e, ktor\u00e9mu s\u00fa d\u00f4le\u017eit\u00e9 nejak\u00e9 sprievodn\u00e9 \u00fadaje o zmene stavu, ktor\u00e9 nie s\u00fa v ozn\u00e1men\u00ed, si m\u00f4\u017ee zisti\u0165 ich stav s\u00e1m.<\/li>\n<li>Maxim\u00e1lna varianta je plnohodnotn\u00e9 event sourcing, <strong>state transfer<\/strong>, pri ktorom udalos\u0165 obsahuje v\u0161etky inform\u00e1cie potrebn\u00e9 na spracovanie: odkia\u013e a do ak\u00e9ho stavu pre\u0161li, ako presne sa d\u00e1ta zmenili at\u010f. Ot\u00e1zka je len v \u00fa\u010delnosti a objeme inform\u00e1ci\u00ed, ktor\u00e9 si m\u00f4\u017eete dovoli\u0165 uchov\u00e1va\u0165.<\/li>\n<\/ol>\n<p>\nV r\u00e1mci spustenia Refund Tool sme pou\u017eili tret\u00ed variant. To zjednodu\u0161ilo spracovanie udalost\u00ed, preto\u017ee nebolo potrebn\u00e9 z\u00edskava\u0165 podrobn\u00e9 inform\u00e1cie, navy\u0161e to vyl\u00fa\u010dilo scen\u00e1r, ke\u010f ka\u017ed\u00e1 nov\u00e1 udalos\u0165 vytvor\u00ed n\u00e1val dopl\u0148uj\u00facich GET po\u017eiadaviek od spotrebite\u013eov.<\/p>\n<p>Slu\u017eba Refund Tool <strong>nen\u00ed za\u0165a\u017een\u00e1<\/strong>, tak\u017ee Kafka tam sk\u00f4r sl\u00fa\u017ei ako pokus ako potreba. Nemysl\u00edm si, \u017ee by bol biznis r\u00e1d, keby sa slu\u017eba vr\u00e1tenia pe\u0148az\u00ed stala highload projektom.<\/p>\n<h4>Async exchange AS IS<\/h4>\n<p>\nPre asynchr\u00f3nne v\u00fdmeny be\u017ene pou\u017e\u00edvame RabbitMQ. Zhroma\u017ed\u00edme \u00fadaje na po\u017eiadavku, vlo\u017e\u00edme ich do fronty, a spotrebite\u013e tej istej slu\u017eby ich pre\u010d\u00edta a odo\u0161le (alebo neodo\u0161le). Pre samotn\u00e9 API Lamoda akt\u00edvne vyu\u017e\u00edva Swagger. Navrhujeme API, popisujeme ho v Swaggeri, generujeme klientsky a serverov\u00fd k\u00f3d. E\u0161te pou\u017e\u00edvame mierne roz\u0161\u00edren\u00fd JSON RPC 2.0. <\/p>\n<p>Niekde sa pou\u017e\u00edvaj\u00fa esb-busy, niekto \u017eije na ActiveMQ, ale vo v\u0161eobecnosti, <strong>RabbitMQ \u2014 \u0161tandard<\/strong>.<\/p>\n<h4>Async exchange TO BE<\/h4>\n<p>\nPri navrhovan\u00ed v\u00fdmeny cez events-bus sa d\u00e1 sledova\u0165 anal\u00f3gia. Podobn\u00fdm sp\u00f4sobom popisujeme bud\u00facu v\u00fdmenu \u00fadajov cez popisy \u0161trukt\u00fary eventu. Form\u00e1t yaml, k\u00f3dov\u00e1 gener\u00e1cia musela by\u0165 vykonan\u00e1 ru\u010dne, gener\u00e1tor pod\u013ea \u0161pecifik\u00e1cie vytv\u00e1ra DTO a u\u010d\u00ed klientov a servery s nimi pracova\u0165. Gener\u00e1cia prebieha na dva jazyky \u2014 <strong>golang a php<\/strong>. To umo\u017e\u0148uje udr\u017eiava\u0165 kni\u017enice v s\u00falade. Gener\u00e1tor je nap\u00edsan\u00fd v golangu, za \u010do dostal meno gogi.<\/p>\n<p>Event sourcing na Kafka je typick\u00fd. Existuje rie\u0161enie od hlavnej enterprise verzie Kafka Confluent, je tu aj <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/nakadi\">nakadi<\/a><\/noindex>, rie\u0161enie od na\u0161ich 'bratov' v oblasti dom\u00e9ny Zalando. Na\u0161a <strong>motiv\u00e1cia za\u010da\u0165 s vanilla Kafka<\/strong>\u00a0je ponecha\u0165 rie\u0161enie bezplatn\u00fdm, k\u00fdm sa definit\u00edvne nerozhodneme, \u010di ho budeme v\u0161ade pou\u017e\u00edva\u0165, a taktie\u017e si necha\u0165 priestor na man\u00e9vrovanie a \u00fapravy: chceme podporu svojho <strong>JSON RPC 2.0<\/strong>, gener\u00e1tory pre dva jazyky a uvid\u00edme, \u010do e\u0161te. <\/p>\n<p>Ironicky, aj v takom \u0161\u0165astnom pr\u00edpade, ke\u010f existuje pribli\u017ene podobn\u00fd obchod Zalando, ktor\u00fd urobil pribli\u017ene podobn\u00e9 rie\u0161enie, ho nem\u00f4\u017eeme efekt\u00edvne vyu\u017ei\u0165. <\/p>\n<p>Architektonicky na spusten\u00ed je vzor tak\u00fd: \u010d\u00edtame priamo z Kafka, ale zapisujeme len cez events-bus. Pre \u010d\u00edtanie v Kafka je ve\u013ea hotov\u00e9ho: brokeri, vyva\u017eova\u010de a je viac-menej pripraven\u00e1 na horizont\u00e1lne \u0161k\u00e1lovanie, to sme chceli zachova\u0165. Z\u00e1pis sme chceli obali\u0165 cez jeden Gateway aka Events-bus, a to je aj d\u00f4vod.<\/p>\n<h3>Events-bus<\/h3>\n<p>\nAlebo autobus udalost\u00ed. To je jednoducho stateless http gateway, ktor\u00fd preber\u00e1 na seba nieko\u013eko d\u00f4le\u017eit\u00fdch \u00faloh:<\/p>\n<ul>\n<li><strong>Valid\u00e1cia produkcie<\/strong>\u00a0\u2014 kontrolujeme, \u010di udalosti zodpovedaj\u00fa na\u0161ej \u0161pecifik\u00e1cii.<\/li>\n<li><strong>Hlavn\u00fd syst\u00e9m pre udalosti<\/strong>, to je hlavn\u00fd a jedin\u00fd syst\u00e9m v spolo\u010dnosti, ktor\u00fd odpoved\u00e1 na ot\u00e1zku, ak\u00e9 s\u00fa vlastne validn\u00e9 udalosti s ak\u00fdmi \u0161trukt\u00farami. V valid\u00e1cii s\u00fa zahrnut\u00e9 jednoducho typy \u00fadajov a enums pre pr\u00edsnu \u0161pecifik\u00e1ciu obsahu. <\/li>\n<li><strong>Hash funkcia<\/strong> pre shardovanie \u2014 \u0161trukt\u00fara spr\u00e1vy Kafka je key-value a hash z key sa pou\u017e\u00edva na v\u00fdpo\u010det, kam to umiestni\u0165.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Pre\u010do<\/h3>\n<p>\nPracujeme vo ve\u013ekej spolo\u010dnosti s nastaven\u00fdm procesom. Pre\u010do nie\u010do meni\u0165? <strong>Je to experiment<\/strong>, a m\u00e1me v \u00famysle z\u00edska\u0165 nieko\u013eko v\u00fdhod.<\/p>\n<h4>1:n+1 v\u00fdmeny (jeden ku mnoh\u00fdm)<\/h4>\n<p>\nS Kafka je ve\u013emi jednoduch\u00e9 pripoji\u0165 nov\u00fdch spotrebite\u013eov k API. <\/p>\n<p>Predpokladajme, \u017ee m\u00e1te adres\u00e1r, ktor\u00fd je potrebn\u00e9 udr\u017eiava\u0165 aktu\u00e1lny v nieko\u013ek\u00fdch syst\u00e9moch naraz (a v nejak\u00fdch nov\u00fdch). Predt\u00fdm sme vymysleli bundle, ktor\u00fd realizoval set-API, a master-syst\u00e9mu sme ozn\u00e1mili adresy spotrebite\u013eov. Teraz master-syst\u00e9m zasiela aktualiz\u00e1cie do topiku a v\u0161etci, ktor\u00fdch to zauj\u00edma, to \u010d\u00edtaj\u00fa. Objavil sa nov\u00fd syst\u00e9m \u2013 prihl\u00e1sili sme ho na topik. \u00c1no, tie\u017e bundle, ale jednoduch\u0161ie.<\/p>\n<p>V pr\u00edpade refund-tool, ktor\u00fd je vlastne k\u00fasok BOB, n\u00e1m vyhovuje dr\u017ea\u0165 ich synchronizovan\u00e9 cez Kafka. Payment hovor\u00ed, \u017ee peniaze vr\u00e1tili: BOB, RT sa o tom dozvedeli, zmenili si stavy, Fiscalization Service o tom vedel a vystavil blo\u010dek.<\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b01b22a333b58e87aeef0c52d40e6960.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nM\u00e1me pl\u00e1ny vytvori\u0165 jednotn\u00fd Notifications Service, ktor\u00fd by informoval klienta o novink\u00e1ch k jeho objedn\u00e1vke \/ vr\u00e1teniam. Moment\u00e1lne je t\u00e1to zodpovednos\u0165 rozdelen\u00e1 medzi syst\u00e9my. Sta\u010d\u00ed, ak nau\u010d\u00edme Notifications Service odchyt\u00e1va\u0165 z Kafka relevantn\u00e9 inform\u00e1cie a reagova\u0165 na ne (a vypn\u00fa\u0165 tieto upozornenia v ostatn\u00fdch syst\u00e9moch). Nebud\u00fa potrebn\u00e9 \u017eiadne nov\u00e9 priame v\u00fdmeny.<\/p>\n<h4>Data-driven<\/h4>\n<p>\nInform\u00e1cie medzi syst\u00e9mami sa st\u00e1vaj\u00fa transparentn\u00fdmi \u2013 nech je ak\u00fdko\u013evek \u201ekrvav\u00fd enterprise\u201c u v\u00e1s a ak\u00fdko\u013evek hrub\u00fd v\u00e1\u0161 backlog. V Lamode m\u00e1me oddelenie Data Analytics, ktor\u00e9 zhroma\u017e\u010fuje \u00fadaje zo syst\u00e9mov a prev\u00e1dza ich do opakovane pou\u017eite\u013en\u00e9ho form\u00e1tu, ako pre biznis, tak aj pre inteligentn\u00e9 syst\u00e9my. Kafka umo\u017e\u0148uje r\u00fdchlo im poskytn\u00fa\u0165 ve\u013ea \u00fadajov a udr\u017eiava\u0165 tento informa\u010dn\u00fd tok aktu\u00e1lny.<\/p>\n<h4>Replication log<\/h4>\n<p>\nSpr\u00e1vy nezmizn\u00fa po pre\u010d\u00edtan\u00ed, ako v RabbitMQ. Ke\u010f event obsahuje dostatok inform\u00e1ci\u00ed na spracovanie, m\u00e1me hist\u00f3riu posledn\u00fdch zmien objektu a, pri \u017eelan\u00ed, mo\u017enos\u0165 tieto zmeny aplikova\u0165.<\/p>\n<p>Doba uchov\u00e1vania replication log z\u00e1vis\u00ed od intenzity z\u00e1pisu do tohto topika, Kafka umo\u017e\u0148uje flexibilne nastavi\u0165 limity \u010dasu uchov\u00e1vania a objemu \u00fadajov. Pre intenz\u00edvne topiky je d\u00f4le\u017eit\u00e9, aby v\u0161etci spotrebitelia stihli pre\u010d\u00edta\u0165 inform\u00e1cie sk\u00f4r, ne\u017e zmizn\u00fa, aj v pr\u00edpade kr\u00e1tkodobej nefunk\u010dnosti. Zvy\u010dajne sa podar\u00ed uchov\u00e1va\u0165 \u00fadaje za\u00a0<strong>jednotky dn\u00ed<\/strong>, \u010do je \u00faplne dostato\u010dn\u00e9 pre podporu. <\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e08dd384155289123ebee96430c2370.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n\u010ealej len tro\u0161ku zhrnutia dokument\u00e1cie pre t\u00fdch, ktor\u00ed nie s\u00fa obozn\u00e1men\u00ed s Kafka (obr\u00e1zok tie\u017e z dokument\u00e1cie)<\/p>\n<p>V AMQP s\u00fa fronty: zapisujeme spr\u00e1vy do fronty pre spotrebite\u013ea. Zvy\u010dajne jednu frontu spravuje jeden syst\u00e9m s jednou a tou istou obchodnou logikou. Ak je potrebn\u00e9 upozorni\u0165 viacero syst\u00e9mov, m\u00f4\u017eeme nau\u010di\u0165 aplik\u00e1ciu zapisova\u0165 do viacer\u00fdch front alebo nastavi\u0165 exchange s mechanizmom fanout, ktor\u00fd ich s\u00e1m klonuje.<\/p>\n<p>V Kafke existuje podobn\u00e1 abstrakcia <em>t\u00e9mata<\/em>, do ktor\u00fdch zapisujete spr\u00e1vy, ale po ich pre\u010d\u00edtan\u00ed nezmizn\u00fa. Predvolene, pri pripojen\u00ed na Kafku, dostanete v\u0161etky spr\u00e1vy a pritom je tu mo\u017enos\u0165 ulo\u017ei\u0165 miesto, na ktorom ste sa zastavili. To znamen\u00e1, \u017ee \u010d\u00edtate sekven\u010dne, nemus\u00edte ozna\u010di\u0165 spr\u00e1vu ako pre\u010d\u00edtan\u00fa, ale m\u00f4\u017eete si ulo\u017ei\u0165 id, od ktor\u00e9ho potom pokra\u010dujete v \u010d\u00edtan\u00ed. Id, na ktorom ste sa zastavili, sa naz\u00fdva offset (posun), a mechanizmus je commit offset. <\/p>\n<p>Prisl\u00fachaj\u00faco m\u00f4\u017eete implementova\u0165 r\u00f4znu logiku. Napr\u00edklad, m\u00e1me BOB existuj\u00faci v 4 in\u0161tanci\u00e1ch pre r\u00f4zne krajiny \u2013 Lamoda je v Rusku, Kazachstane, Ukrajine, Bielorusku. Ke\u010f\u017ee sa nasadzuj\u00fa samostatne, maj\u00fa trochu svoje vlastn\u00e9 konfigur\u00e1cie a svoju obchodn\u00fa logiku. V spr\u00e1ve uv\u00e1dzame, k akej krajine patr\u00ed. Ka\u017ed\u00fd spotrebite\u013e BOB v ka\u017edej krajine \u010d\u00edta s r\u00f4znymi groupId, a ak sa spr\u00e1va na neho nevz\u0165ahuje, presko\u010d\u00ed ju, t.j. okam\u017eite commitne offset +1. Ak to ist\u00e9 t\u00e9ma \u010d\u00edta n\u00e1\u0161 Platobn\u00fd servis, tak to rob\u00ed s oddelenou skupinou, a preto sa offsety neprekr\u00fdvaj\u00fa.<\/p>\n<p><b>Po\u017eiadavky na udalosti:<\/b><\/p>\n<ul>\n<li><strong>\u00daplnos\u0165 \u00fadajov. <\/strong>Chceli by sme, aby v udalosti bolo dostatok \u00fadajov, aby ju bolo mo\u017en\u00e9 spracova\u0165. <\/li>\n<\/ul>\n<p><\/p>\n<ul>\n<li><strong>Integrita. <\/strong>Delegujeme Events-bus na kontrolu toho, \u017ee udalos\u0165 je konzistentn\u00e1 a on ju m\u00f4\u017ee spracova\u0165.<\/li>\n<li><strong>Poradie je d\u00f4le\u017eit\u00e9. <\/strong>V pr\u00edpade vr\u00e1tenia sme n\u00faten\u00ed pracova\u0165 s historick\u00fdmi \u00fadajmi. Pri notifik\u00e1ci\u00e1ch poradie nie je d\u00f4le\u017eit\u00e9, ak s\u00fa to homog\u00e9nne notifik\u00e1cie, e-mail bude rovnak\u00fd bez oh\u013eadu na to, ktor\u00fd objedn\u00e1vka pri\u0161la prv\u00fd. V pr\u00edpade vr\u00e1tenia existuje jasn\u00fd proces, ak zmen\u00edte poradie, m\u00f4\u017eu vznikn\u00fa\u0165 v\u00fdnimky, refund\u00e1cia sa nevytvor\u00ed alebo nebude spracovan\u00e1 \u2013 dostaneme sa do in\u00e9ho stavu.<\/li>\n<li><strong>Konzistencia. <\/strong>M\u00e1me \u00falo\u017eisko, a teraz namiesto API vytv\u00e1rame udalosti. Potrebujeme sp\u00f4sob, ako r\u00fdchlo a lacno posiela\u0165 do na\u0161ich slu\u017eieb inform\u00e1cie o nov\u00fdch udalostiach a o zmen\u00e1ch v u\u017e existuj\u00facich. To sa dosahuje pomocou spolo\u010dnej \u0161pecifik\u00e1cie v samostatnom git repozit\u00e1ri a k\u00f3do-gener\u00e1torov. Preto s\u00fa klienti a servery v r\u00f4znych slu\u017eb\u00e1ch u n\u00e1s vz\u00e1jomne dohodnut\u00e9.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Kafka v Lamoda<\/h2>\n<p>\nM\u00e1me tri in\u0161tal\u00e1cie Kafka: <\/p>\n<ol>\n<li>Logs;<\/li>\n<li>R&amp;D;<\/li>\n<li>Events-bus.<\/li>\n<\/ol>\n<p>\nDnes hovor\u00edme len o poslednom bode. V events-bus m\u00e1me nie ve\u013emi ve\u013ek\u00e9 in\u0161tal\u00e1cie - 3 brokeri (servery) a celkom 27 t\u00e9m. Zvy\u010dajne jedna t\u00e9ma je jeden proces. Ale to je jemn\u00fd moment, a teraz sa k nemu dostaneme.<\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/f398852689b31429cc97b4cbffcabab5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNa obr\u00e1zku vy\u0161\u0161ie s\u00fa grafy rps. Proces refund\u00e1ci\u00ed je ozna\u010den\u00fd tyrkysovou \u010diarou (\u00e1no, tou, ktor\u00e1 le\u017e\u00ed na osi X), a ru\u017eovou je proces aktualiz\u00e1cie obsahu. <\/p>\n<p>Katal\u00f3g Lamoda obsahuje mili\u00f3ny produktov, pri\u010dom \u00fadaje sa neust\u00e1le aktualizuj\u00fa. Niektor\u00e9 kolekcie vych\u00e1dzaj\u00fa z m\u00f3dy, na ich miesto sa uv\u00e1dzaj\u00fa nov\u00e9, v katal\u00f3gu sa neust\u00e1le objavuj\u00fa nov\u00e9 modely. Sna\u017e\u00edme sa predpoveda\u0165, \u010do bude zajtra zauj\u00edmav\u00e9 pre na\u0161ich z\u00e1kazn\u00edkov, a preto neust\u00e1le nakupujeme nov\u00e9 veci, fot\u00edme ich a aktualizujeme v\u00fdklad. <\/p>\n<p>Ru\u017eov\u00e9 vrcholy s\u00fa produktov\u00e9 aktualiz\u00e1cie, teda zmeny produktov. Je vidie\u0165, \u017ee chalani fotili, fotili, a potom bum! - nahrali \u0161ar\u017eu udalost\u00ed.<\/p>\n<h2>Use cases Lamoda Events<\/h2>\n<p>\nVytvoren\u00fa architekt\u00faru pou\u017e\u00edvame na tak\u00e9to oper\u00e1cie:<\/p>\n<ul>\n<li><strong>Sledovanie stavov vr\u00e1ten\u00ed<\/strong>: call-to-action a sledovanie stavov zo v\u0161etk\u00fdch zapojen\u00fdch syst\u00e9mov. Platba, stavy, fiskaliz\u00e1cia, notifik\u00e1cie. Tu sme vysk\u00fa\u0161ali pr\u00edstup, vytvorili n\u00e1stroje, zhroma\u017edili v\u0161etky chyby, nap\u00edsali dokument\u00e1ciu a povedali kolegom, ako to pou\u017e\u00edva\u0165.<\/li>\n<li><strong>Aktualiz\u00e1cia kariet produktov: <\/strong>konfigur\u00e1cia, meta-\u00fadaje, charakteristiky. \u010c\u00edta jeden syst\u00e9m (ktor\u00fd zobrazuje), a p\u00ed\u0161u ich viacer\u00e9.<\/li>\n<li><strong>Email, push a sms<\/strong>: objedn\u00e1vka bola zhroma\u017eden\u00e1, objedn\u00e1vka dorazila, vr\u00e1tenie prijat\u00e9 a pod., mnoho ich. <\/li>\n<li><strong>Sklad, aktualiz\u00e1cia stavu<\/strong>\u00a0\u2014 kvantitat\u00edvna aktualiz\u00e1cia n\u00e1zvov, len \u010d\u00edsla: pr\u00edchod na sklad, vr\u00e1tenie. Je potrebn\u00e9, aby v\u0161etky syst\u00e9my spojen\u00e9 s rezervovan\u00edm tovaru pracovaly s \u010do najaktu\u00e1lnej\u0161\u00edmi \u00fadajmi. Teraz je syst\u00e9m aktualiz\u00e1cie skladu dos\u0165 zlo\u017eit\u00fd, Kafka ho umo\u017en\u00ed zjednodu\u0161i\u0165.<\/li>\n<li><strong>Anal\u00fdza d\u00e1t<\/strong> (R&amp;D oddelenie), ML n\u00e1stroje, anal\u00fdza, \u0161tatistika. Chceme, aby bola inform\u00e1cia transparentn\u00e1 \u2013 na to je Kafka ve\u013emi vhodn\u00e1.<\/li>\n<\/ul>\n<p>\nTeraz t\u00e1 zauj\u00edmavej\u0161ia \u010das\u0165 o zbieran\u00ed sk\u00fasenost\u00ed a zauj\u00edmav\u00fdch objavoch, ktor\u00e9 sa stali za pol roka.<\/p>\n<h2>Probl\u00e9my s dizajnom<\/h2>\n<p>\nPredpokladajme, \u017ee chceme spravi\u0165 nov\u00fa vec \u2013 napr\u00edklad prenies\u0165 cel\u00fd proces dod\u00e1vky na Kafka. Moment\u00e1lne je \u010das\u0165 procesu implementovan\u00e1 v Order Processing v BOB. Za prenosom objedn\u00e1vky do dopravnej slu\u017eby, premiestnen\u00edm na medzi\u00falo\u017eisko a podobne stoj\u00ed model stavov. Existuje cel\u00fd monolit, dokonca dva, plus mno\u017estvo API venovan\u00fdch dod\u00e1vke. Vedia ove\u013ea viac o dod\u00e1vke. <\/p>\n<p>Zd\u00e1 sa, \u017ee s\u00fa to podobn\u00e9 oblasti, ale pre Order Processing v BOB a pre syst\u00e9m dod\u00e1vky sa stavy l\u00ed\u0161ia. Napr\u00edklad niektor\u00e9 kuri\u00e9rske slu\u017eby neposielaj\u00fa medzi\u010dasov\u00e9 stavy, ale len kone\u010dn\u00e9: \u201edoru\u010den\u00e9\u201c alebo \u201estraten\u00e9\u201c. In\u00e9 naopak ve\u013emi podrobne informuj\u00fa o pohybe tovaru. Ka\u017ed\u00fd m\u00e1 svoje pravidl\u00e1 valid\u00e1cie: pre niekoho je platn\u00fd email, tak\u017ee ho spracuj\u00fa; pre \u010fal\u0161\u00edch je neplatn\u00fd, ale objedn\u00e1vka sa aj tak spracuje, preto\u017ee je tam telef\u00f3n na kontakt, a niekto povie, \u017ee tak\u00fa objedn\u00e1vku v\u00f4bec spracov\u00e1va\u0165 nebude.<\/p>\n<h3>Tok d\u00e1t<\/h3>\n<p>\nV pr\u00edpade Kafky vznik\u00e1 ot\u00e1zka organiz\u00e1cie toku d\u00e1t. T\u00e1to \u00faloha s\u00favis\u00ed s v\u00fdberom strat\u00e9gie z nieko\u013ek\u00fdch bodov, prejdeme si nimi v\u0161etk\u00fdmi.<\/p>\n<h4>Do jedn\u00e9ho topiku alebo do r\u00f4znych?<\/h4>\n<p>\nM\u00e1me \u0161pecifik\u00e1ciu udalosti. V BOB p\u00ed\u0161eme, \u017ee tak\u00e1 objedn\u00e1vka m\u00e1 by\u0165 doru\u010den\u00e1, a uv\u00e1dzame: \u010d\u00edslo objedn\u00e1vky, jej obsah, nejak\u00e9 SKU a \u010diarov\u00e9 k\u00f3dy at\u010f. Ke\u010f tovar doraz\u00ed na sklad, doru\u010denie m\u00f4\u017ee z\u00edska\u0165 stavy, timestamps a v\u0161etko, \u010do je potrebn\u00e9. Ale \u010falej chceme v BOB prij\u00edma\u0165 aktualiz\u00e1cie t\u00fdchto \u00fadajov. Vznik\u00e1 n\u00e1m sp\u00e4tn\u00fd proces prij\u00edmania d\u00e1t z dod\u00e1vky. Je to t\u00e1 ist\u00e1 udalos\u0165? Alebo je to samostatn\u00e1 v\u00fdmena, ktor\u00e1 si zasl\u00fa\u017ei samostatn\u00fd topik?<\/p>\n<p>Pravdepodobne bud\u00fa ve\u013emi podobn\u00e9 a poku\u0161enie spravi\u0165 jeden topik nie je nezmyseln\u00e9, preto\u017ee samostatn\u00fd topik to s\u00fa samostatn\u00ed konzumenti, samostatn\u00e9 konfigur\u00e1cie, samostatn\u00e1 gener\u00e1cia v\u0161etk\u00e9ho tohto. Ale nie je to ist\u00e9.<\/p>\n<h4>Nov\u00e9 pole alebo nov\u00e1 udalos\u0165?<\/h4>\n<p>\nAle ak pou\u017e\u00edvame tie ist\u00e9 udalosti, vznik\u00e1 in\u00fd probl\u00e9m. Napr\u00edklad, nie v\u0161etky doru\u010dovacie syst\u00e9my m\u00f4\u017eu vygenerova\u0165 tak\u00e9 DTO, ktor\u00e9 dok\u00e1\u017ee generova\u0165 BOB. Posielame im id, a oni ich neuchov\u00e1vaj\u00fa, preto\u017ee ich nepotrebuj\u00fa, a z poh\u013eadu za\u010diatku procesu event-bus je toto pole povinn\u00e9. <\/p>\n<p>Ak zavedieme pre event-bus pravidlo, \u017ee toto pole je povinn\u00e9, mus\u00edme v BOB alebo v spracovate\u013eovi po\u010diato\u010dnej udalosti da\u0165 \u010fal\u0161ie pravidl\u00e1 valid\u00e1cie. Valid\u00e1cia sa za\u010d\u00edna rozpl\u00fdva\u0165 po slu\u017ebe - to nie je ve\u013emi praktick\u00e9.<\/p>\n<p>\u010eal\u0161\u00ed probl\u00e9m - to je l\u00e1kav\u00e1 inkrament\u00e1lna v\u00fdvoj. Povedia n\u00e1m, \u017ee treba nie\u010do prida\u0165 do udalosti a mo\u017eno, ak dobre porozm\u00fd\u0161\u013eame, to malo by\u0165 samostatn\u00e1 udalos\u0165. Ale v na\u0161ej sch\u00e9me je samostatn\u00e1 udalos\u0165 - to samostatn\u00e9 t\u00e9ma. Samostatn\u00e9 t\u00e9ma - to je cel\u00fd ten proces, ktor\u00fd som op\u00edsal vy\u0161\u0161ie. U v\u00fdvoj\u00e1ra sa objavuje poku\u0161enie jednoducho prida\u0165 \u010fal\u0161ie pole do JSON sch\u00e9my a regenerova\u0165.<\/p>\n<p>V pr\u00edpade refunds sme tak za pol roka pri\u0161li k udalosti udalost\u00ed. Mali sme jedno meta-udalos\u0165, ktor\u00e9 sa naz\u00fdva refund update, v ktorom bolo pole type, popisuj\u00face, v \u010dom vlastne tento update spo\u010d\u00edva. Z toho sme mali \"n\u00e1dhern\u00e9\" prep\u00edna\u010de s valid\u00e1tormi, ktor\u00e9 hovorili, ako je potrebn\u00e9 validova\u0165 t\u00fato udalos\u0165 s t\u00fdmto type.<\/p>\n<h4>Verzionovanie udalost\u00ed<\/h4>\n<p>\nNa valid\u00e1ciu spr\u00e1v v Kafka je mo\u017en\u00e9 vyu\u017ei\u0165 <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.confluent.io\/current\/schema-registry\/docs\/index.html\">Avro<\/a><\/noindex>, ale bolo potrebn\u00e9 to hne\u010f zapo\u010d\u00edta\u0165 a pou\u017ei\u0165 Confluent. V na\u0161om pr\u00edpade s verzionovan\u00edm mus\u00edme by\u0165 opatrn\u00ed. Nie v\u017edy bude mo\u017en\u00e9 pre\u010d\u00edta\u0165 spr\u00e1vy z replication log, preto\u017ee model \"odi\u0161iel\". Hlavne, je potrebn\u00e9 stava\u0165 verzie tak, aby bol model sp\u00e4tne kompatibiln\u00fd: napr\u00edklad, urobi\u0165 pole do\u010dasne nepovinn\u00e9. Ak s\u00fa rozdiely pr\u00edli\u0161 siln\u00e9, za\u010dneme p\u00edsa\u0165 do nov\u00e9ho t\u00e9my, a klientov pres\u00favame, ke\u010f do\u010d\u00edtaj\u00fa star\u00e9.<\/p>\n<h4>Z\u00e1ruka poradia \u010d\u00edtania partitions<\/h4>\n<p>\nT\u00e9my vn\u00fatri Kafka s\u00fa rozdelen\u00e9 na partitions. To nie je ve\u013emi d\u00f4le\u017eit\u00e9, k\u00fdm navrhujeme entity a v\u00fdmeny, ale je to d\u00f4le\u017eit\u00e9, ke\u010f sa rozhodujeme, ako to konzumova\u0165 a \u0161k\u00e1lova\u0165.<\/p>\n<p>V\u00a0be\u017en\u00fdch pr\u00edpadoch zapisujete do\u00a0Kafka jednu t\u00e9mu. Predvolen\u00e9 nastavenie pou\u017e\u00edva jeden partition a\u00a0v\u0161etky spr\u00e1vy tejto t\u00e9my kon\u010dia v\u00a0\u0148om. A\u00a0konzument ich potom postupne \u010d\u00edta. Predpokladajme, \u017ee teraz je potrebn\u00e9 syst\u00e9m roz\u0161\u00edri\u0165, aby spr\u00e1vy \u010d\u00edtali dvaja r\u00f4zni konzumenti. Ak\u00a0napr\u00edklad posielate SMS, m\u00f4\u017eete poveda\u0165, aby Kafka vytvorila \u010fal\u0161\u00ed partition, a\u00a0Kafka za\u010dne rozde\u013eova\u0165 spr\u00e1vy do\u00a0dvoch \u010dast\u00ed\u00a0\u2013 polovicu sem, polovicu tam. <\/p>\n<p>Ako ich Kafka del\u00ed? Ka\u017ed\u00e1 spr\u00e1va m\u00e1 telo (v\u00a0ktorom uchov\u00e1vame JSON) a\u00a0m\u00e1 k\u013e\u00fa\u010d. K\u00a0tomuto k\u013e\u00fa\u010du m\u00f4\u017eeme pripoji\u0165 hashovaciu funkciu, ktor\u00e1 ur\u010duje, do\u00a0ktor\u00e9ho partition spr\u00e1va patr\u00ed.<\/p>\n<p>V\u00a0na\u0161om pr\u00edpade s\u00a0refundami je to d\u00f4le\u017eit\u00e9, ak\u00a0vezmeme dva partition, existuje riziko, \u017ee paraleln\u00fd konzument spracuje druh\u00fa udalos\u0165 sk\u00f4r ne\u017e prv\u00fa, \u010do by sp\u00f4sobilo probl\u00e9m. Hashovacia funkcia zaru\u010duje, \u017ee spr\u00e1vy so\u00a0samotn\u00fdm k\u013e\u00fa\u010dom skon\u010dia v\u00a0jednom a\u00a0tom istom partition. <\/p>\n<h4>Udalosti vs pr\u00edkazy<\/h4>\n<p>\nToto je \u010fal\u0161\u00ed probl\u00e9m, s\u00a0ktor\u00fdm sme sa stretli. Udalos\u0165 \u2014 to je ist\u00e1 udalos\u0165: hovor\u00edme, \u017ee nie\u010do niekde nastalo (something_happened), napr\u00edklad polo\u017eka bola zru\u0161en\u00e1 alebo do\u0161lo k\u00a0refund\u00e1cii. Ak\u00a0tieto udalosti niekto po\u010d\u00fava, potom na\u00a0\u201epolo\u017eka bola zru\u0161en\u00e1\u201c sa vytvor\u00ed entita refund, a\u00a0\u201edo\u0161lo k\u00a0refund\u00e1cii\u201c sa zap\u00ed\u0161e niekde v\u00a0nastaveniach.<\/p>\n<p>Ale\u00a0zvy\u010dajne, ke\u010f\u00a0navrhujete udalosti, nechcete ich p\u00edsa\u0165 zbyto\u010dne \u2014 predpoklad\u00e1te, \u017ee ich niekto bude \u010d\u00edta\u0165. Je l\u00e1kav\u00e9 nap\u00edsa\u0165 nie nie\u010do_happened (item_canceled, refund_refunded), ale nie\u010do_should_be_done. Napr\u00edklad, polo\u017eka je pripraven\u00e1 na\u00a0vr\u00e1tenie.<\/p>\n<p>Na\u00a0jednej strane to nazna\u010duje, ako sa udalos\u0165 bude pou\u017e\u00edva\u0165. Na\u00a0druhej strane to ove\u013ea menej pripom\u00edna norm\u00e1lny n\u00e1zov udalosti. Okrem toho, odtia\u013e je u\u017e len kr\u00f4\u010dik k\u00a0pr\u00edkazu do_something. Ale\u00a0nem\u00e1te z\u00e1ruku, \u017ee t\u00fato udalos\u0165 niekto pre\u010d\u00edtal; a\u00a0ak\u00a0ju pre\u010d\u00edtal, pre\u010d\u00edtal ju \u00faspe\u0161ne; a\u00a0ak\u00a0ju pre\u010d\u00edtal \u00faspe\u0161ne, nie\u010do urobil, a\u00a0to nie\u010do pre\u0161lo \u00faspe\u0161ne. V\u00a0momente, ke\u010f sa udalos\u0165 st\u00e1va do_something, je potrebn\u00e1 sp\u00e4tn\u00e1 v\u00e4zba, a\u00a0to je probl\u00e9m.<\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b755d91208092bd9791a41ce4633fb48.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPri asynchr\u00f3nnej v\u00fdmene v\u00a0RabbitMQ, ke\u010f\u00a0pre\u010d\u00edtate spr\u00e1vu, nav\u0161t\u00edvite http, m\u00e1te odpove\u010f \u2014 aspo\u0148, \u017ee spr\u00e1va bola prijat\u00e1. Ke\u010f\u00a0zap\u00ed\u0161ete do\u00a0Kafka, m\u00e1te spr\u00e1vu, \u017ee ste zap\u00edsali do\u00a0Kafka, ale\u00a0o\u00a0tom, ako bola spracovan\u00e1, ni\u010d neviete. <\/p>\n<p>Preto sme museli zavies\u0165 sp\u00e4tn\u00fa udalos\u0165 a nastavi\u0165 monitorovanie, aby sme zistili, \u017ee ak sa udialo to\u013eko udalost\u00ed, v danom \u010dase by sa malo vyskytn\u00fa\u0165 to\u013eko odpovedaj\u00facich udalost\u00ed. Ak sa to nestalo, akoby sme mali pocit, \u017ee nie\u010do sa pokazilo. Napr\u00edklad, ak sme odoslali udalos\u0165 \u201eitem_ready_to_refund\u201c, o\u010dak\u00e1vame, \u017ee refund\u00e1cia sa vytvor\u00ed, peniaze sa vr\u00e1tia klientovi a vyjde n\u00e1m udalos\u0165 \u201emoney_refunded\u201c. Ale to nie je ist\u00e9, preto potrebujeme monitorovanie.<\/p>\n<h3>Nuansy<\/h3>\n<p>\nExistuje dos\u0165 zjavn\u00fd probl\u00e9m: ak \u010d\u00edtate z topiku postupne a m\u00e1te nejak\u00fa zlo\u017eit\u00fa spr\u00e1vu, spotrebite\u013e sa zr\u00fati a \u010falej sa nedostanete. Mus\u00edte <strong>zastavi\u0165 v\u0161etk\u00fdch spotrebite\u013eov<\/strong>, potvrdi\u0165 posun \u010falej, aby ste mohli pokra\u010dova\u0165 v \u010d\u00edtan\u00ed.<\/p>\n<p>O tom sme vedeli, s t\u00fdm sme po\u010d\u00edtali, a aj tak sa to stalo. Stalo sa to preto, \u017ee udalos\u0165 bola platn\u00e1 z poh\u013eadu events-bus, udalos\u0165 bola platn\u00e1 z poh\u013eadu valid\u00e1tora aplik\u00e1cie, ale nebola platn\u00e1 z poh\u013eadu PostgreSQL, preto\u017ee sme mali v jednej s\u00fastave MySQL s UNSIGNED INT, a v novo nap\u00edsanej s\u00fastave bol PostgreSQL jednoducho s INT. Jeho ve\u013ekos\u0165 je o nie\u010do men\u0161ia a ID sa nezmestilo. Symfony sa zr\u00fatilo s v\u00fdnimkou. Samozrejme, zachytili sme t\u00fato v\u00fdnimku, preto\u017ee sme na \u0148u po\u010d\u00edtali a pl\u00e1novali sme potvrdi\u0165 tento posun, ale predt\u00fdm sme chceli zv\u00fd\u0161i\u0165 po\u010det probl\u00e9mov, ke\u010f\u017ee spr\u00e1va bola nespr\u00e1vne spracovan\u00e1. Po\u010d\u00edtadl\u00e1 v tomto projekte s\u00fa tie\u017e ulo\u017een\u00e9 v datab\u00e1ze a Symfony u\u017e uzavrelo komunik\u00e1ciu s datab\u00e1zou, a druh\u00e1 v\u00fdnimka zabila cel\u00fd proces bez \u0161ance na potvrdenie posunu.<\/p>\n<p>N nejak\u00fd \u010das slu\u017eba le\u017eala - na\u0161\u0165astie, s Kafka to nie je tak stra\u0161n\u00e9, preto\u017ee spr\u00e1vy zost\u00e1vaj\u00fa. Ke\u010f sa pr\u00e1ca obnov\u00ed, m\u00f4\u017eete ich do\u010d\u00edta\u0165. Je to pohodln\u00e9.<\/p>\n<p>Kafka m\u00e1 mo\u017enos\u0165 cez tooling nastavi\u0165 \u013eubovo\u013en\u00fd posun. Ale aby ste to mohli urobi\u0165, mus\u00edte zastavi\u0165 v\u0161etk\u00fdch spotrebite\u013eov - v na\u0161om pr\u00edpade pripravi\u0165 samostatn\u00e9 vydanie, v ktorom nebud\u00fa spotrebitelia, redeployments. Potom m\u00f4\u017eete pomocou toolingu v Kafke posun\u00fa\u0165 posun a spr\u00e1va prejde.<\/p>\n<p>\u010eal\u0161\u00ed nuans - <strong>replication log vs rdkafka.so<\/strong>\u00a0\u2014 je spojen\u00e1 so \u0161pecifikami n\u00e1\u0161ho projektu. M\u00e1me PHP a v PHP zvy\u010dajne v\u0161etky kni\u017enice komunikuj\u00fa s Kafka cez repozit\u00e1r rdkafka.so, a \u010falej nasleduje nejak\u00fd wrapper. Mo\u017eno s\u00fa to na\u0161e osobn\u00e9 \u0165a\u017ekosti, ale uk\u00e1zalo sa, \u017ee jednoducho pre\u010d\u00edta\u0165 k\u00fasok u\u017e pre\u010d\u00edtan\u00e9ho nie je tak\u00e9 jednoduch\u00e9. V\u0161eobecne sme mali softv\u00e9rov\u00e9 probl\u00e9my.<\/p>\n<p>N\u00e1vrat k \u0161pecifik\u00e1m pr\u00e1ce s partitions, priamo v dokument\u00e1cii je nap\u00edsan\u00e9 <strong>consumers &gt;= topic partitions<\/strong>. Ale dozvedel som sa o tom ove\u013ea nesk\u00f4r, ako by som chcel. Ak chcete \u0161k\u00e1lova\u0165 a ma\u0165 dvoch konzumentov, potrebujete aspo\u0148 dve partitions. Teda, ak ste mali jeden partition, v ktorom sa nahromadilo 20 000 spr\u00e1v, a vytvorili ste nov\u00fd, po\u010det spr\u00e1v sa nevyrovn\u00e1 tak r\u00fdchlo. Preto, aby ste mali dvoch paraleln\u00fdch konzumentov, treba sa zaobera\u0165 partitions.<\/p>\n<h2>Monitorovanie<\/h2>\n<p>\nMysl\u00edm, \u017ee pod\u013ea toho, ako monitorujeme, bude e\u0161te jasnej\u0161ie, ak\u00e9 probl\u00e9my existuj\u00fa v s\u00fa\u010dasnom pr\u00edstupe.<\/p>\n<p>Napr\u00edklad po\u010d\u00edtame, ko\u013eko produktov v datab\u00e1ze ned\u00e1vno zmenilo stav a pod\u013ea t\u00fdchto zmien mali nasta\u0165 udalosti, a posielame toto \u010d\u00edslo do n\u00e1\u0161ho monitorovacieho syst\u00e9mu. Potom z Kafka dost\u00e1vame druh\u00e9 \u010d\u00edslo, ko\u013eko skuto\u010dne bolo zaznamenan\u00fdch udalost\u00ed. Je zrejm\u00e9, \u017ee rozdiel medzi t\u00fdmito dvoma \u010d\u00edslami by mal by\u0165 v\u017edy nulov\u00fd.<\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/07d8ed08514f2fb97d9019466b96342c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nOkrem toho je potrebn\u00e9 monitorova\u0165, ako si stoj\u00ed producent, \u010di prijal events-bus spr\u00e1vy, a ako si stoj\u00ed konzument. Napr\u00edklad na grafoch ni\u017e\u0161ie je v\u0161etko v poriadku s Refund Tool, ale BOB m\u00e1 jasne nejak\u00e9 probl\u00e9my (modr\u00e9 vrcholy).<\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/57112dbe70d2b388c53f19c34cca6f63.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nU\u017e som spom\u00ednal consumer-group lag. Hovor\u00edm zjednodu\u0161ene, \u017ee to je po\u010det nepre\u010d\u00edtan\u00fdch spr\u00e1v. Celkovo na\u0161i konzumenti funguj\u00fa r\u00fdchlo, tak\u017ee lag je obvykle 0, ale niekedy m\u00f4\u017ee nasta\u0165 kr\u00e1tkodob\u00fd vrchol. Kafka to zvl\u00e1da z krabice, ale mus\u00edte zada\u0165 ur\u010dit\u00fd interval. <\/p>\n<p>Existuje projekt <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/linkedin\/Burrow\">Burrow<\/a><\/noindex>, ktor\u00fd v\u00e1m poskytne viac inform\u00e1ci\u00ed o Kafka. Jednoducho cez API poskytuje stav pod\u013ea consumer-group, ako sa tejto skupine dar\u00ed. Okrem OK a Failed tam existuje aj warning, a zist\u00edte, \u017ee va\u0161e konzumenti nest\u00edhaj\u00fa tempo produkcie - nest\u00edhaj\u00fa \u010d\u00edta\u0165 to, \u010do sa p\u00ed\u0161e. Syst\u00e9m je dos\u0165 inteligentn\u00fd a pohodlne sa pou\u017e\u00edva. <\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/cece8495801e187b155487a802e1a35a.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nTakto vyzer\u00e1 odpove\u010f cez API. Tu je skupina bob-live-fifa, partition refund.update.v1, stav OK, lag 0 - posledn\u00fd kone\u010dn\u00fd offset je tak\u00fdto.<\/p>\n<p><img decoding=\"async\" alt=\"Sk\u00fasenos\u0165 s v\u00fdvojom slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/1538b2ccc390e9b83075f54566f1039b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMonitorovanie <strong>updated_at SLA (stuck)<\/strong> U\u017e som spom\u00ednal. Napr\u00edklad, tovar pre\u0161iel do stavu, \u017ee je pripraven\u00fd na vr\u00e1tenie. Nastavujeme Cron, ktor\u00fd hovor\u00ed, \u017ee ak za 5 min\u00fat tento objekt nepre\u0161iel do refund\u00e1cie (peniaze vraciame prostredn\u00edctvom platobn\u00fdch syst\u00e9mov ve\u013emi r\u00fdchlo), nie\u010do ur\u010dite ne\u0161lo tak, ako malo a je to ur\u010dite pr\u00edpad pre podporu. Preto jednoducho vezmeme Cron, ktor\u00fd \u010d\u00edta tak\u00e9to veci, a ak s\u00fa ich po\u010det v\u00e4\u010d\u0161\u00ed ako 0, posiela upozornenie.<\/p>\n<p><b>Na z\u00e1ver, pou\u017e\u00edvanie udalost\u00ed je pohodln\u00e9, ke\u010f<\/b>:<\/p>\n<ul>\n<li>inform\u00e1cie potrebuj\u00fa nieko\u013eko syst\u00e9mov;<\/li>\n<li>v\u00fdsledok spracovania nie je d\u00f4le\u017eit\u00fd;<\/li>\n<li>je m\u00e1lo udalost\u00ed alebo s\u00fa udalosti mal\u00e9. <\/li>\n<\/ul>\n<blockquote><p>Skoro by sa zdalo, \u017ee \u010dl\u00e1nok m\u00e1 ve\u013emi konkr\u00e9tnu t\u00e9mu \u2013 asynchr\u00f3nne API na Kafka, ale v s\u00favislosti s n\u00edm chcem hne\u010f odporu\u010di\u0165 mno\u017estvo vec\u00ed.<br \/>\nPo prv\u00e9, nasleduj\u00faci <noindex><a rel=\"nofollow\" href=\"https:\/\/www.highload.ru\/\">HighLoad++<\/a><\/noindex> n\u00e1s treba \u010daka\u0165 do novembra, u\u017e v apr\u00edli bude jeho peterbursk\u00e1 verzia, a v j\u00fani sa budeme rozpr\u00e1va\u0165 o vysok\u00fdch z\u00e1\u0165a\u017eiach v Novosibirsku.<br \/>\nPo druh\u00e9, autor spr\u00e1vy Sergej Zaika patr\u00ed do Programov\u00e9ho v\u00fdboru na\u0161ej novej konferencie o spr\u00e1ve znalost\u00ed <noindex><a rel=\"nofollow\" href=\"https:\/\/knowledgeconf.ru\/2019\">KnowledgeConf<\/a><\/noindex>. Konferencia je jednede\u0148n\u00e1, prebehne 26. apr\u00edla, ale m\u00e1 ve\u013emi bohat\u00fd program.<br \/>\nA v m\u00e1ji sa uskuto\u010dn\u00ed <noindex><a rel=\"nofollow\" href=\"https:\/\/phprussia.ru\/2019\">PHP Russia<\/a><\/noindex> a\u00a0<noindex><a rel=\"nofollow\" href=\"https:\/\/ritfest.ru\/2019\">RIT++<\/a><\/noindex> (s DevOpsConf ako s\u00fa\u010das\u0165ou) \u2013 do tejto konferencie je mo\u017en\u00e9 e\u0161te navrhn\u00fa\u0165 svojou t\u00e9mu, podeli\u0165 sa o svoje sk\u00fasenosti a zdie\u013ea\u0165 svoje nabit\u00e9 rany.<\/p><\/blockquote>\n<p>Zdroj: <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\/sk\/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=\"sk_SK\" \/>\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\/sk\/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\udd47Sk\u00fasenos\u0165 s vypracovan\u00edm slu\u017eby Refund Tool s asynchr\u00f3nnym API na Kafka | ProHoster","description":"\u010co m\u00f4\u017ee prin\u00fati\u0165 tak\u00fa ve\u013ek\u00fa spolo\u010dnos\u0165 ako Lamoda s vypracovan\u00fdm procesom a desiatkami vz\u00e1jomne prepojen\u00fdch slu\u017eieb z\u00e1sadne zmeni\u0165 pr\u00edstup?","canonical_url":"https:\/\/prohoster.info\/sk\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"sk_SK","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\/sk\/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\/sk\/wp-json\/wp\/v2\/posts\/30778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/comments?post=30778"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/posts\/30778\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/media\/22763"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/media?parent=30778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/categories?post=30778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/sk\/wp-json\/wp\/v2\/tags?post=30778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}