{"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\/it\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","title":{"rendered":"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Cosa pu\u00f2 spingere una grande azienda come Lamoda, con un processo collaudato e decine di servizi interconnessi, a cambiare radicalmente approccio? Le motivazioni possono essere le pi\u00f9 varie: dalla legislazione al desiderio innato di ogni programmatore di sperimentare.<\/p>\n<p>Ma questo non significa affatto che non si possa contare su un ulteriore vantaggio. In cosa si pu\u00f2 guadagnare concretamente introducendo un API basato su eventi su Kafka, lo racconter\u00e0 Sergey Zaika (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/fewald\/\" class=\"user_link\">fewald<\/a><\/noindex>). Ci saranno sicuramente anche esperienze di errori e scoperte interessanti \u2014 un esperimento non pu\u00f2 prescinderne.<\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/7ab959ab45ec5c6565b35b18b361c0ea.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<em>Disclaimer: Questo articolo \u00e8 basato sui materiali di un meetup che Sergey ha tenuto a novembre 2018 su HighLoad++. L'esperienza pratica di Lamoda nell'utilizzo di Kafka ha attratto il pubblico non meno degli altri interventi in programma. Ci sembra un ottimo esempio di come sia sempre possibile e necessario trovare affinit\u00e0, e gli organizzatori di HighLoad++ continueranno a lavorare per creare un'atmosfera ideale per questo.<\/em><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Sul processo<\/h2>\n<p>\nLamoda\u00a0\u00e8 una grande piattaforma e-commerce che dispone di un proprio centro contatti, un servizio di consegna (e numerose partnership), uno studio fotografico, un enorme magazzino e tutto ci\u00f2 \u00e8 gestito con il proprio software. Sono disponibili decine di metodi di pagamento, partner B2B che possono utilizzare parte o tutti questi servizi e vogliono avere informazioni aggiornate sui propri prodotti. Inoltre, Lamoda opera in tre paesi oltre alla Russia, e l\u00ec tutto funziona in modo leggermente diverso. In totale, ci sono probabilmente pi\u00f9 di un centinaio di modi per configurare un nuovo ordine, il quale deve essere elaborato in modo specifico. Tutto questo funziona attraverso decine di servizi che comunicano in modi a volte non evidenti. C'\u00e8 anche un sistema centrale, la cui principale responsabilit\u00e0 \u00e8 gestire gli stati degli ordini. Noi la chiamiamo BOB, e io ci lavoro.<\/p>\n<h2>Strumento di rimborso con API basata su eventi <\/h2>\n<p>\nLa parola \"basata su eventi\" \u00e8 piuttosto inflazionata, e pi\u00f9 avanti definiremo meglio cosa si intende. Inizier\u00f2 dal contesto in cui abbiamo deciso di testare l'approccio dell'API basata su eventi con Kafka. <\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/3bfdce47dd8420fc63d84645e76de647.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIn qualsiasi negozio, oltre agli ordini per i quali i clienti pagano, ci sono momenti in cui \u00e8 necessario restituire denaro perch\u00e9 il prodotto non \u00e8 adatto al cliente. Questo \u00e8 un processo relativamente breve: verifichiamo le informazioni, se necessario, e trasferiamo il denaro. <\/p>\n<p>Ma il processo di restituzione \u00e8 diventato complicato a causa delle modifiche legislative, e abbiamo dovuto implementare un microservizio separato per gestirlo.<\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e358df5476e7448976e0f1147a103fb.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nLa nostra motivazione:<\/p>\n<ol>\n<li><strong>Legge FZ-54<\/strong>\u00a0\u2014 in sintesi, la legge richiede di comunicare all'agenzia fiscale ogni operazione monetaria, che si tratti di un rimborso o di un incasso, in un SLA piuttosto breve di alcuni minuti. Noi, come e-commerce, svolgiamo molte operazioni. Questo significa una nuova responsabilit\u00e0 (e quindi un nuovo servizio) e modifiche in tutti i sistemi coinvolti.<\/li>\n<li><strong>BOB split<\/strong>\u00a0\u2014 un progetto interno dell'azienda per liberare BOB da un gran numero di responsabilit\u00e0 non core e ridurre la sua complessit\u00e0 complessiva.<\/li>\n<\/ol>\n<p>\n<img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/d3ecf9961bdb372fc5f84ee9389f73ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIn questo schema sono rappresentati i principali sistemi di Lamoda. Attualmente, la maggior parte di essi rappresenta piuttosto <strong>una costellazione di 5-10 microservizi attorno a un monolite in riduzione.<\/strong>. Crescono lentamente, ma ci sforziamo di ridurli, perch\u00e9 \u00e8 spaventoso eseguire il deploy di un frammento critico \u2014 non possiamo permettere che si interrompa. Siamo costretti a riservare tutti gli scambi (le frecce) e a considerarli sempre disponibili.<\/p>\n<p>In BOB ci sono anche molti scambi: sistemi di pagamento, consegne, notifiche, ecc. <\/p>\n<p>Tecnicamente BOB \u00e8:<\/p>\n<ul>\n<li>~150k righe di codice + ~100k righe di test;<\/li>\n<li>php7.2 + Zend 1 &amp; Symfony Components 3;<\/li>\n<li>&gt;100 API &amp; ~50 integrazioni in uscita;<\/li>\n<li>4 paesi con la propria logica di business. <\/li>\n<\/ul>\n<p>\nEseguire il deploy di BOB \u00e8 costoso e doloroso, la quantit\u00e0 di codice e le sfide coinvolte sono tali che nessuno pu\u00f2 tenere tutto in mente. Insomma, ci sono molti motivi per semplificarlo.<\/p>\n<h2>Processo di rimborso<\/h2>\n<p>\nInizialmente sono coinvolti due sistemi: BOB e Payment. Ora ne emergono altri due:<\/p>\n<ul>\n<li>Fiscalization Service, che si occuper\u00e0 dei problemi di fiscalizzazione e della comunicazione con i servizi esterni.<\/li>\n<li>Refund Tool, dove vengono semplicemente gestiti i nuovi scambi, per non gonfiare BOB.<\/li>\n<\/ul>\n<p>\nOra il processo appare cos\u00ec:<\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/13c02975881ad35c61304053c604cda3.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ol>\n<li>A BOB arriva una richiesta di rimborso.<\/li>\n<li>BOB informa il Refund Tool.<\/li>\n<li>Il Refund Tool comunica a Payment: \u00abRestituisci i soldi\u00bb.<\/li>\n<li>Payment restituisce i soldi.<\/li>\n<li>Refund Tool e BOB sincronizzano i loro stati perch\u00e9 entrambi ne hanno bisogno. Non siamo ancora pronti a passare completamente a Refund Tool, poich\u00e9 BOB ha un'interfaccia utente, report per la contabilit\u00e0 e molti dati che non si possono trasferire cos\u00ec facilmente. Dobbiamo restare su due fronti.<\/li>\n<li>Viene inviata una richiesta di fiscalizzazione.<\/li>\n<\/ol>\n<p>\nAlla fine abbiamo creato su Kafka un certo bus di eventi \u2014 un event-bus, sul quale tutto si basa. Evviva, ora abbiamo un punto unico di fallimento (sarcasmo).<\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/674edd7972998b4985071f5250612c7e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPro e contro sono piuttosto evidenti. Abbiamo creato un bus, quindi ora tutti i servizi ne dipendono. Questo semplifica la progettazione, ma introduce un punto unico di fallimento nel sistema. Se Kafka crolla, il processo si ferma.<\/p>\n<h2>Cos'\u00e8 l'API event-driven? <\/h2>\n<p>\nUna buona risposta a questa domanda si trova nella presentazione di Martin Fowler (GOTO 2017) <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/STKCRSUsyPO\">\u00abThe Many Meanings of Event-Driven Architecture\u00bb<\/a><\/noindex>. <\/p>\n<p>In sintesi, ci\u00f2 che abbiamo fatto:<\/p>\n<ol>\n<li>Abbiamo incapsulato tutti gli scambi asincroni tramite <strong>storage degli eventi<\/strong>. Invece di comunicare in rete a ogni consumatore interessato una modifica di stato, scriviamo in un archivio centralizzato un evento di cambiamento di stato, e i consumatori interessati all'argomento leggono da l\u00ec tutto ci\u00f2 che appare.<\/li>\n<li>L'evento in questo caso \u00e8 una notifica (<strong>notifiche<\/strong>) che indica che qualcosa \u00e8 cambiato da qualche parte. Ad esempio, \u00e8 cambiato lo stato di un ordine. Il consumatore, che ha bisogno di ulteriori dati sul cambiamento di stato che non sono presenti nella notifica, pu\u00f2 verificare il proprio stato autonomamente.<\/li>\n<li>La soluzione massima \u00e8 un event sourcing completo, <strong>state transfer<\/strong>, in cui l'evento contiene tutte le informazioni necessarie per il trattamento: da dove e in quale stato si \u00e8 passati, come sono stati modificati i dati, e cos\u00ec via. La questione riguarda solo la fattibilit\u00e0 e il volume di informazioni che si pu\u00f2 permettere di conservare.<\/li>\n<\/ol>\n<p>\nNell'ambito del lancio di Refund Tool abbiamo utilizzato la terza opzione. Questo ha semplificato l'elaborazione degli eventi, poich\u00e9 non \u00e8 necessario estrarre informazioni dettagliate, inoltre ha escluso il scenario in cui ogni nuovo evento genera un aumento di richieste get di chiarimento da parte dei consumatori.<\/p>\n<p>Il servizio Refund Tool <strong>non \u00e8 sovraccarico<\/strong>, quindi Kafka \u00e8 pi\u00f9 una prova di concetto che una necessit\u00e0. Non penso che se il servizio di rimborso diventasse un progetto ad alto carico, il business sarebbe contento.<\/p>\n<h4>Async exchange AS IS<\/h4>\n<p>\nPer gli scambi asincroni, il dipartimento PHP utilizza solitamente RabbitMQ. Abbiamo raccolto i dati per la richiesta, li abbiamo messi in coda e il consumatore dello stesso servizio li ha letti e inviati (o non inviati). Per l'API, Lamoda utilizza attivamente Swagger. Progettiamo l'API, la descriviamo in Swagger, generiamo codice client e server. Utilizziamo anche un JSON RPC 2.0 leggermente esteso. <\/p>\n<p>In alcuni casi vengono utilizzate le bus ESB, qualcuno vive su ActiveMQ, ma in generale, <strong>RabbitMQ \u00e8 lo standard<\/strong>.<\/p>\n<h4>Scambio asincrono TO BE<\/h4>\n<p>\nProgettando lo scambio tramite events-bus, si traccia un'analogia. Descriviamo in modo simile il futuro scambio di dati attraverso le descrizioni della struttura dell'evento. Il formato \u00e8 YAML, abbiamo dovuto scrivere noi la code generation, il generatore secondo la specifica crea DTO e insegna ai client e ai server a lavorare con essi. La generazione avviene in due lingue - <strong>golang e php<\/strong>. Questo consente di mantenere le librerie allineate. Il generatore \u00e8 scritto in golang, da cui il nome gogi.<\/p>\n<p>Event-sourcing su Kafka \u00e8 una pratica comune. Esiste una soluzione dalla principale versione enterprise Kafka Confluent, ci sono <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/nakadi\">nakadi<\/a><\/noindex>, una soluzione dai nostri 'fratelli' nel campo di Zalando. La nostra <strong>motivazione per iniziare con vanilla Kafka<\/strong>\u00a0\u2014 \u00e8 lasciare la soluzione gratuita finch\u00e9 non decideremo se utilizzarla ovunque, oltre a mantenere spazio per manovre e miglioramenti: vogliamo supportare il nostro <strong>JSON RPC 2.0<\/strong>, generatori per due lingue e vediamo cos'altro. <\/p>\n<p>Ironico che anche in un caso cos\u00ec felice, quando c'\u00e8 un'attivit\u00e0 simile come Zalando che ha fatto una soluzione simile, non possiamo utilizzarla in modo efficace. <\/p>\n<p>Architettonicamente, all'avvio il pattern \u00e8 il seguente: leggiamo direttamente da Kafka, ma scriviamo solo tramite events-bus. Per la lettura in Kafka ci sono molte soluzioni pronte: broker, bilanciatori e \u00e8 pi\u00f9 o meno pronta per la scalabilit\u00e0 orizzontale, questo volevamo mantenere. Per la scrittura, invece, abbiamo voluto avvolgerla tramite un Gateway alias Events-bus, ed ecco perch\u00e9.<\/p>\n<h3>Events-bus<\/h3>\n<p>\nO bus degli eventi. \u00c8 semplicemente un gateway http stateless che assume su di s\u00e9 diversi ruoli importanti:<\/p>\n<ul>\n<li><strong>Validazione della produzione<\/strong>\u00a0\u2014 controlliamo che gli eventi corrispondano alla nostra specifica.<\/li>\n<li><strong>Sistema master per gli eventi<\/strong>, cio\u00e8 \u00e8 l'unico e principale sistema dell'azienda che risponde alla domanda su quali eventi con quali strutture siano considerati validi. La validazione include semplicemente i tipi di dati e gli enum per una specifica rigorosa del contenuto. <\/li>\n<li><strong>Funzione di hash<\/strong> per il partizionamento \u2014 la struttura del messaggio Kafka \u00e8 key-value e si calcola dove metterlo in base all'hash della chiave.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Perch\u00e9<\/h3>\n<p>\nLavoriamo in una grande azienda con un processo ben definito. Perch\u00e9 cambiare qualcosa? <strong>\u00c8 un esperimento<\/strong>, e ci aspettiamo di ottenere diversi vantaggi.<\/p>\n<h4>Scambi 1:n+1 (uno a molti)<\/h4>\n<p>\nCon Kafka \u00e8 molto semplice connettere nuove API dei consumatori. <\/p>\n<p>Supponiamo di avere un catalogo che deve essere mantenuto aggiornato in pi\u00f9 sistemi contemporaneamente (anche in nuovi sistemi). In precedenza, inventavamo un bundle che implementava le set-API, e comunicavamo gli indirizzi dei consumatori al sistema principale. Ora il sistema principale invia aggiornamenti al topic, e tutti quelli interessati possono leggerli. \u00c8 stato creato un nuovo sistema \u2014 l'abbiamo iscritto al topic. S\u00ec, anche questo \u00e8 un bundle, ma pi\u00f9 semplice.<\/p>\n<p>Nel caso dello strumento di rimborso, che \u00e8 essenzialmente un pezzo di BOB, ci \u00e8 comodo tenerli sincronizzati tramite Kafka. Il sistema di pagamento comunica che i soldi sono stati restituiti: BOB e RT ne sono stati informati, hanno aggiornato i loro stati, il Servizio di Fiscalizzazione \u00e8 stato avvisato e ha emesso lo scontrino.<\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b01b22a333b58e87aeef0c52d40e6960.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAbbiamo piani per creare un unico Servizio Notifiche, che informer\u00e0 il cliente sulle novit\u00e0 riguardanti il suo ordine\/ritorni. Attualmente questa responsabilit\u00e0 \u00e8 sparsa tra i diversi sistemi. Sar\u00e0 sufficiente insegnare al Servizio Notifiche a monitorare Kafka per le informazioni pertinenti e reagire di conseguenza (e disabilitare queste notifiche negli altri sistemi). Non saranno necessari nuovi scambi diretti.<\/p>\n<h4>Data-driven<\/h4>\n<p>\nLe informazioni tra i sistemi diventano trasparenti, indipendentemente da quale 'enterprise draconiano' abbiate o quanto sia consistente il vostro backlog. In Lamoda esiste un dipartimento di Data Analytics che raccoglie dati dai sistemi e li rende riutilizzabili, sia per il business che per i sistemi intelligenti. Kafka consente di fornire rapidamente a loro una grande quantit\u00e0 di dati e di mantenere questo flusso informativo aggiornato.<\/p>\n<h4>Registro di replicazione<\/h4>\n<p>\nI messaggi non scompaiono dopo essere stati letti, come in RabbitMQ. Quando un evento contiene informazioni sufficienti per essere elaborato, abbiamo una cronologia degli ultimi cambiamenti dell'oggetto e, se lo desideriamo, la possibilit\u00e0 di applicare queste modifiche.<\/p>\n<p>Il periodo di conservazione del replication log dipende dall'intensit\u00e0 della scrittura in questo argomento; Kafka consente di configurare flessibilmente i limiti di tempo di conservazione e di volume dei dati. Per argomenti intensivi, \u00e8 importante che tutti i consumatori riescano a leggere le informazioni prima che scompaiano, anche in caso di brevi periodi di inattivit\u00e0. Di solito riesce a conservare i dati per\u00a0<strong>un numero di giorni<\/strong>, il che \u00e8 pi\u00f9 che sufficiente per il supporto. <\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e08dd384155289123ebee96430c2370.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDi seguito un breve riassunto della documentazione, per coloro che non sono familiari con Kafka (l'immagine \u00e8 anch'essa tratta dalla documentazione)<\/p>\n<p>In AMQP ci sono code: scriviamo messaggi in una coda per il consumatore. In generale, una coda \u00e8 gestita da un solo sistema con la stessa logica di business. Se \u00e8 necessario avvertire pi\u00f9 sistemi, \u00e8 possibile insegnare all'applicazione a scrivere in pi\u00f9 code o configurare uno exchange con meccanismo fanout, che le clona automaticamente.<\/p>\n<p>In Kafka esiste un'astrazione simile <em>topic<\/em>, in cui scrivi messaggi che non scompaiono dopo la lettura. Per impostazione predefinita, collegandoti a Kafka, ricevi tutti i messaggi e hai la possibilit\u00e0 di salvare il punto in cui ti sei fermato. Puoi quindi leggere in modo sequenziale, senza contrassegnare il messaggio come letto, ma conservando l'id da cui continuerai a leggere. L'id, dove ti sei fermato, si chiama offset, e il meccanismo \u00e8 il commit offset. <\/p>\n<p>Di conseguenza, \u00e8 possibile implementare logiche diverse. Ad esempio, abbiamo BOB che esiste in 4 istanze per diversi paesi: Lamoda \u00e8 presente in Russia, Kazakistan, Ucraina e Bielorussia. Poich\u00e9 vengono distribuiti separatamente, hanno leggermente le loro configurazioni e la loro logica aziendale. Indichiamo nel messaggio a quale paese si riferisce. Ogni consumatore BOB in ogni paese legge con groupId diversi e, se il messaggio non lo riguarda, lo salta, ovvero committa immediatamente offset +1. Se lo stesso topic viene letto dal nostro Payment Service, allora lo fa con un gruppo separato, quindi gli offset non si sovrappongono.<\/p>\n<p><b>Requisiti per gli eventi:<\/b><\/p>\n<ul>\n<li><strong>Completezza dei dati. <\/strong>Vorremmo che ci fossero abbastanza dati nell'evento affinch\u00e9 possa essere elaborato. <\/li>\n<\/ul>\n<p><\/p>\n<ul>\n<li><strong>Integrit\u00e0. <\/strong>Delega a Events-bus il compito di verificare che l'evento sia coerente e che possa elaborarlo.<\/li>\n<li><strong>L'ordine \u00e8 importante. <\/strong>Nel caso di un ritorno, dobbiamo fare i conti con la storia. Con le notifiche, invece, l'ordine non ha importanza, se si tratta di notifiche omogenee; l'email sar\u00e0 la stessa indipendentemente da quale ordine \u00e8 arrivato per primo. Per un ritorno, c'\u00e8 un processo chiaro: cambiare l'ordine pu\u00f2 portare a eccezioni, il rimborso non verr\u00e0 creato o elaborato, e finiremo in uno stato diverso.<\/li>\n<li><strong>Coerenza. <\/strong>Abbiamo uno storage e ora stiamo creando eventi invece di API. Abbiamo bisogno di un modo per trasmettere rapidamente e a basso costo informazioni sui nuovi eventi e sulle modifiche a quelli esistenti ai nostri servizi. Questo viene raggiunto grazie a una specifica comune in un repository git separato e a generatori di codice. Cos\u00ec i clienti e i server in diversi servizi sono allineati.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Kafka in Lamoda<\/h2>\n<p>\nAbbiamo tre installazioni di Kafka: <\/p>\n<ol>\n<li>Logs;<\/li>\n<li>R&amp;D;<\/li>\n<li>Events-bus.<\/li>\n<\/ol>\n<p>\nOggi parliamo solo dell'ultimo punto. Nel nostro events-bus non abbiamo installazioni molto grandi: 3 broker (server) e solo 27 topic. Di solito, un topic corrisponde a un processo. Ma \u00e8 un momento delicato, e ora lo affronteremo.<\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/f398852689b31429cc97b4cbffcabab5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nSopra \u00e8 il grafico rps. Il processo di restituzione \u00e8 contrassegnato dalla linea turchese (s\u00ec, quella che si trova sull'asse X), e rosa - il processo di aggiornamento dei contenuti. <\/p>\n<p>Il catalogo Lamoda contiene milioni di articoli, e i dati vengono aggiornati continuamente. Alcune collezioni escono di moda, mentre altre nuove entrano in catalogo. Continuiamo a cercare di prevedere cosa potrebbe interessare ai nostri clienti domani, quindi acquistiamo costantemente nuovi articoli, li fotografiamo e aggiorniamo la vetrina. <\/p>\n<p>I picchi rosa rappresentano gli aggiornamenti dei prodotti, ovvero modifiche relative agli articoli. \u00c8 evidente che il team ha continuato a fotografare e a un certo punto ha caricato un mucchio di eventi.<\/p>\n<h2>Casi d'uso di Lamoda Events<\/h2>\n<p>\nUtilizziamo l'architettura costruita per i seguenti operazioni:<\/p>\n<ul>\n<li><strong>Monitoraggio degli stati di restituzione<\/strong>: call-to-action e tracciamento degli stati da tutti i sistemi coinvolti. Pagamenti, stati, fiscalizzazione, notifiche. Qui abbiamo testato l'approccio, creato gli strumenti, raccolto tutti i bug, scritto la documentazione e spiegato ai colleghi come usarli.<\/li>\n<li><strong>Aggiornamento delle schede prodotto: <\/strong>configurazione, metadati, caratteristiche. Viene letta da un sistema (che visualizza), mentre ne scrivono diversi.<\/li>\n<li><strong>Email, push e sms<\/strong>: ordine raccolto, ordine consegnato, reso accettato, ecc., ce ne sono molti. <\/li>\n<li><strong>Stock, aggiornamento magazzino<\/strong>\u00a0\u2014 aggiornamento quantitativo delle denominazioni, solo numeri: arrivi in magazzino, resi. Tutti i sistemi legati alla riserva del prodotto devono operare con i dati pi\u00f9 aggiornati. Attualmente, il sistema di aggiornamento dello stock \u00e8 piuttosto complesso, Kafka lo semplificher\u00e0.<\/li>\n<li><strong>Analisi dei dati<\/strong> (dipartimento R&amp;D), strumenti di ML, analisi, statistiche. Vogliamo che le informazioni siano trasparenti \u2014 per questo Kafka \u00e8 molto adatto.<\/li>\n<\/ul>\n<p>\nOra la parte pi\u00f9 interessante riguarda le esperienze e le scoperte che sono avvenute nel corso di sei mesi.<\/p>\n<h2>Problemi di progettazione<\/h2>\n<p>\nSupponiamo di voler introdurre una novit\u00e0: ad esempio, trasferire l'intero processo di consegna su Kafka. Attualmente, parte del processo \u00e8 gestita nell'Order Processing di BOB. Dietro il passaggio dell'ordine al servizio di consegna, il movimento verso un magazzino intermedio e altro, ci sono modelli di stato. Esiste un intero monolite, addirittura due, oltre a una serie di API dedicate alla consegna. Queste sanno molto di pi\u00f9 sulla consegna. <\/p>\n<p>Sembra che queste siano aree simili, ma gli stati per l'Order Processing in BOB e per il sistema di consegna sono differenti. Ad esempio, alcuni servizi di corriere non inviano stati intermedi, ma solo finali: \"consegnato\" o \"perso\". Altri, al contrario, forniscono dettagli molto accurati sul movimento della merce. Ognuno ha le proprie regole di validazione: per qualcuno un'email valida significa che verr\u00e0 elaborata; per altri, anche se non \u00e8 valida, l'ordine verr\u00e0 comunque gestito perch\u00e9 c'\u00e8 un numero di telefono per contattare, mentre qualcun altro dir\u00e0 che un tale ordine non verr\u00e0 elaborato affatto.<\/p>\n<h3>Flusso di dati<\/h3>\n<p>\nNel caso di Kafka, emerge la questione dell'organizzazione del flusso di dati. Questa attivit\u00e0 \u00e8 legata alla scelta di una strategia su diversi punti, esamineremo tutti questi aspetti.<\/p>\n<h4>In un topic o in diversi?<\/h4>\n<p>\nAbbiamo una specifica dell'evento. In BOB scriviamo che un determinato ordine deve essere consegnato, indicando: il numero dell'ordine, il suo contenuto, alcuni SKU e codici a barre, ecc. Quando la merce arriva al magazzino, la consegna pu\u00f2 ricevere stati, timestamp e tutto ci\u00f2 che serve. Ma poi desideriamo ricevere aggiornamenti su questi dati in BOB. Si genera un processo inverso per ottenere i dati dalla consegna. \u00c8 lo stesso evento? O \u00e8 uno scambio separato che merita un tema a parte?<\/p>\n<p>Probabilmente saranno molto simili, e la tentazione di creare un unico argomento non \u00e8 infondata, poich\u00e9 un tema separato comporterebbe consumatori distinti, configurazioni diverse e la generazione separata di tutto ci\u00f2. Ma non \u00e8 un dato di fatto.<\/p>\n<h4>Campo nuovo o evento nuovo?<\/h4>\n<p>\nMa se utilizziamo gli stessi eventi, emerge un altro problema. Ad esempio, non tutti i sistemi di consegna possono generare un DTO che possa essere elaborato da BOB. Inviamo loro l'ID, ma loro non lo memorizzano perch\u00e9 non ne hanno bisogno, mentre dal punto di vista dell'inizio del processo dell'event-bus, questo campo \u00e8 obbligatorio. <\/p>\n<p>Se imponiamo una regola per l'event-bus che rende questo campo obbligatorio, allora siamo costretti a inserire regole di validazione aggiuntive in BOB o nel gestore dell'evento di avvio. La validazione tende a diffondersi nel servizio \u2014 il che non \u00e8 molto comodo.<\/p>\n<p>Un altro problema \u00e8 la tentazione dello sviluppo incrementale. Ci dicono che dobbiamo aggiungere qualcosa all'evento, e, forse, se ci pensiamo bene, avrebbe dovuto essere un evento separato. Ma nel nostro schema un evento separato \u00e8 un topic separato. Un topic separato rappresenta l'intero processo che ho descritto sopra. Il programmatore \u00e8 tentato di aggiungere semplicemente un altro campo nello schema JSON e rigenerarlo.<\/p>\n<p>Nel caso dei rimborsi, in sei mesi siamo arrivati all'evento eventi. Avevamo un meta-evento chiamato aggiornamento rimborso, nel quale era presente il campo tipo, che descriveva di cosa si trattasse questo aggiornamento. Da ci\u00f2 abbiamo avuto 'fantastici' switch con i validatori che indicavano come validare questo evento con questo tipo.<\/p>\n<h4>Versioning degli eventi<\/h4>\n<p>\nPer la validazione dei messaggi in Kafka si pu\u00f2 usare <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.confluent.io\/current\/schema-registry\/docs\/index.html\">Avro<\/a><\/noindex>, ma era necessario pianificare fin da subito e utilizzare Confluent. Nel nostro caso con la versione bisogna essere cauti. Non sempre sar\u00e0 possibile rileggere i messaggi dal replication log, perch\u00e9 il modello potrebbe \u201candare in confusione\u201d. In generale, si riesce a costruire versioni in modo che il modello sia retrocompatibile: ad esempio, rendere temporaneamente un campo non obbligatorio. Se le differenze sono troppo forti, iniziamo a scrivere in un nuovo topic e migriamo i clienti quando hanno terminato di leggere quello vecchio.<\/p>\n<h4>Garanzia dell'ordine di lettura delle partitions<\/h4>\n<p>\nI topic all'interno di Kafka sono suddivisi in partitions. Questo non \u00e8 molto importante mentre progettiamo entit\u00e0 e scambi, ma lo diventa quando decidiamo come consumarli e scalare.<\/p>\n<p>In a typical scenario, you write to a single topic in Kafka. By default, one partition is used, and all messages for this topic go into it. The consumer, in turn, reads these messages sequentially. Now, suppose you need to scale the system so that two different consumers read the messages. For example, when you send an SMS, you can instruct Kafka to create an additional partition, and Kafka will start distributing messages into two parts\u2014half here, half there. <\/p>\n<p>How does Kafka divide them? Each message has a body (where we store JSON) and a key. A hash function can be applied to this key, which will determine which partition the message will enter.<\/p>\n<p>In our case with refunds, this is important; if we take two partitions, there is a chance that a parallel consumer will process the second event before the first, which could lead to issues. The hash function ensures that messages with the same key land in the same partition. <\/p>\n<h4>Events vs commands<\/h4>\n<p>\nQuesto \u00e8 un altro problema con cui ci siamo imbattuti. Un evento \u00e8 un verificarsi di qualcosa: diciamo che qualcosa \u00e8 successo (something_happened), ad esempio, un articolo \u00e8 stato annullato o c'\u00e8 stato un rimborso. Se qualcuno ascolta questi eventi, allora per \"articolo annullato\" verr\u00e0 creata un'entit\u00e0 di rimborso, e \"c'\u00e8 stato un rimborso\" verr\u00e0 registrato da qualche parte nelle impostazioni.<\/p>\n<p>Ma di solito, quando progetti eventi, non vuoi scriverli a vuoto - stai contando sul fatto che qualcuno li legger\u00e0. C'\u00e8 una forte tentazione di non scrivere something_happened (item_canceled, refund_refunded), ma something_should_be_done. Ad esempio, l'articolo \u00e8 pronto per il rimborso.<\/p>\n<p>Da un lato, questo suggerisce come verr\u00e0 utilizzato l'evento. Dall'altro lato, assomiglia molto meno a un normale nome di evento. Inoltre, da qui \u00e8 solo un passo fino al comando do_something. Ma non hai la garanzia che questo evento sia stato letto da qualcuno; e se \u00e8 stato letto, che sia stato letto con successo; e se \u00e8 stato letto con successo, che sia stata compiuta un'azione, e che questa azione sia andata a buon fine. Nel momento in cui l'evento diventa do_something, \u00e8 necessaria una retroazione, e questo \u00e8 un problema.<\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b755d91208092bd9791a41ce4633fb48.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNella comunicazione asincrona in RabbitMQ, quando leggi un messaggio e fai una richiesta http, hai una risposta \u2014 almeno quella che il messaggio \u00e8 stato ricevuto. Quando scrivi in Kafka, hai un messaggio che attesta che hai scritto in Kafka, ma non sai nulla su come sia stato elaborato. <\/p>\n<p>Pertanto, nel nostro caso, \u00e8 stato necessario introdurre un evento di risposta e configurare il monitoraggio affinch\u00e9, se si verifica un certo numero di eventi entro un determinato intervallo di tempo, dovrebbero arrivare altrettanti eventi di risposta. Se ci\u00f2 non accade, sembra che qualcosa sia andato storto. Ad esempio, se inviamo l'evento \u00abitem_ready_to_refund\u00bb, ci aspettiamo che venga creata una refund, il cliente ricever\u00e0 indietro i soldi, e noi riceveremo l'evento \u00abmoney_refunded\u00bb. Ma non \u00e8 certo, quindi \u00e8 necessario il monitoraggio.<\/p>\n<h3>Sfumature<\/h3>\n<p>\nC'\u00e8 un problema piuttosto ovvio: se leggi i messaggi da un topic in modo sequenziale e hai un messaggio problematico, il consumer si blocca e non puoi pi\u00f9 andare avanti. Devi <strong>fermare tutti i consumer<\/strong>, committare l'offset pi\u00f9 avanti per poter continuare a leggere.<\/p>\n<p>Ne eravamo a conoscenza, lo avevamo previsto, eppure \u00e8 successo. Questo \u00e8 accaduto perch\u00e9 l'evento era valido secondo l'events-bus, l'evento era valido secondo il validatore dell'applicazione, ma non era valido secondo PostgreSQL, poich\u00e9 in un sistema abbiamo MySQL con UNSIGNED INT, mentre nel sistema appena scritto c'era PostgreSQL con INT. Ha una dimensione leggermente pi\u00f9 piccola, e l'Id non \u00e8 passato. Symfony \u00e8 andato in errore con un'eccezione. Ovviamente abbiamo catturato l'eccezione, perch\u00e9 eravamo gi\u00e0 preparati, e volevamo commettere questo offset, ma prima volevamo incrementare il contatore dei problemi, visto che il messaggio \u00e8 stato elaborato con esito negativo. I contatori in questo progetto sono anch'essi memorizzati nel database, e Symfony aveva gi\u00e0 interrotto la comunicazione con il database, e la seconda eccezione ha ucciso il processo senza possibilit\u00e0 di commettere l'offset.<\/p>\n<p>Un po' di tempo, il servizio \u00e8 rimasto inattivo - per fortuna, con Kafka non \u00e8 poi cos\u00ec grave, poich\u00e9 i messaggi rimangono. Quando il lavoro riprender\u00e0, sar\u00e0 possibile leggerli di nuovo. Questo \u00e8 comodo.<\/p>\n<p>Kafka offre la possibilit\u00e0 di impostare un offset arbitrario tramite tooling. Tuttavia, per farlo, \u00e8 necessario fermare tutti i consumatori \u2014 nel nostro caso, preparare una release separata in cui non ci siano consumatori, redeployments. A quel punto, potremo spostare l'offset in Kafka tramite tooling e il messaggio verr\u00e0 elaborato.<\/p>\n<p>Un altro aspetto \u2014 <strong>registro di replicazione vs rdkafka.so<\/strong>\u00a0\u2014 \u00e8 legato alle specificit\u00e0 del nostro progetto. Noi utilizziamo PHP, e in PHP, di solito, tutte le librerie comunicano con Kafka tramite il repository rdkafka.so, e successivamente c'\u00e8 qualche tipo di wrapper. Potrebbe essere una nostra difficolt\u00e0 personale, ma ci siamo resi conto che rileggere un pezzo gi\u00e0 letto non \u00e8 affatto semplice. In generale, ci sono stati problemi di programmazione.<\/p>\n<p>Ritornando alle peculiarit\u00e0 del lavoro con le partizioni, \u00e8 scritto chiaramente nella documentazione <strong>consumers &gt;= topic partitions<\/strong>. Ma ho appreso di questo molto pi\u00f9 tardi di quanto avrei voluto. Se desiderate scalare e avere due consumatori, \u00e8 necessario avere almeno due partizioni. Cio\u00e8, se avevate una sola partizione, in cui si sono accumulati 20.000 messaggi, e avete creato una nuova partizione, il numero di messaggi si equilibrer\u00e0 ugualmente solo dopo un bel po'. Quindi, per avere due consumatori paralleli, \u00e8 necessario lavorare sulle partizioni.<\/p>\n<h2>Monitoraggio<\/h2>\n<p>\nPenso che, in base a come monitoriamo, sar\u00e0 ancora pi\u00f9 chiaro quali problemi ci siano nell'approccio attuale.<\/p>\n<p>Ad esempio, contiamo quanti prodotti nello stock hanno recentemente cambiato stato e, di conseguenza, in base a queste modifiche, dovrebbero verificarsi eventi, e inviamo questo numero al nostro sistema di monitoraggio. Poi, da Kafka, riceviamo un secondo numero che indica quanti eventi sono effettivamente stati registrati. \u00c8 evidente che la differenza tra questi due numeri dovrebbe sempre essere zero.<\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/07d8ed08514f2fb97d9019466b96342c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nInoltre, dobbiamo monitorare come sta andando il produttore, se l'events-bus ha ricevuto i messaggi, e come sta andando il consumatore. Ad esempio, nei grafici qui sotto, per il Refund Tool tutto va bene, mentre per BOB ci sono chiaramente problemi (picchi blu).<\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/57112dbe70d2b388c53f19c34cca6f63.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nHo gi\u00e0 menzionato il consumer-group lag. In termini semplici, si tratta di quanti messaggi non sono stati letti. In generale, i nostri consumatori lavorano rapidamente, quindi il lag \u00e8 solitamente pari a 0, ma a volte pu\u00f2 esserci un picco temporaneo. Kafka gestisce questo in modo nativo, ma \u00e8 necessario impostare un intervallo. <\/p>\n<p>C'\u00e8 un progetto <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/linkedin\/Burrow\">Burrow<\/a><\/noindex>, che ti dar\u00e0 maggiori informazioni su Kafka. Restituisce semplicemente lo stato della consumer-group tramite API, come sta andando quel gruppo. Oltre a OK e Failed, ci sono anche warning, e potrai scoprire che i tuoi consumer non riescono a tenere il passo con la produzione \u2014 non riescono a leggere ci\u00f2 che viene scritto. Il sistema \u00e8 piuttosto intelligente e facile da usare. <\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/cece8495801e187b155487a802e1a35a.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nEcco come appare la risposta tramite API. Qui ci sono gruppo bob-live-fifa, partizione refund.update.v1, stato OK, lag 0 \u2014 l'ultimo offset finale \u00e8 questo.<\/p>\n<p><img decoding=\"async\" alt=\"Esperienza nello sviluppo del servizio Refund Tool con API asincroni su Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/1538b2ccc390e9b83075f54566f1039b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMonitoraggio <strong>updated_at SLA (stuck)<\/strong> l'ho gi\u00e0 menzionato. Ad esempio, il prodotto \u00e8 passato allo stato di essere pronto per il reso. Impostiamo un Cron che dice che se dopo 5 minuti questo oggetto non \u00e8 passato a refund (restituiamo i soldi attraverso i sistemi di pagamento molto rapidamente), allora qualcosa sicuramente \u00e8 andato storto, e questo \u00e8 sicuramente un caso per il supporto. Quindi prendiamo semplicemente un Cron che legge queste cose, e se sono pi\u00f9 di 0, invia un alert.<\/p>\n<p><b>In sintesi, \u00e8 comodo utilizzare eventi quando<\/b>:<\/p>\n<ul>\n<li>l'informazione \u00e8 necessaria a pi\u00f9 sistemi;<\/li>\n<li>il risultato dell'elaborazione non \u00e8 importante;<\/li>\n<li>ci sono pochi eventi o gli eventi sono di piccole dimensioni. <\/li>\n<\/ul>\n<blockquote><p>A prima vista, l'articolo ha un tema molto specifico: l'API asincrono su Kafka, ma mi vengono subito in mente molte raccomandazioni da fare al riguardo.<br \/>\nInnanzitutto, il prossimo <noindex><a rel=\"nofollow\" href=\"https:\/\/www.highload.ru\/\">HighLoad++<\/a><\/noindex> si dovr\u00e0 aspettare fino a novembre, ma gi\u00e0 ad aprile ci sar\u00e0 la sua versione a San Pietroburgo, e a giugno discuteremo di carichi elevati a Novosibirsk.<br \/>\nIn secondo luogo, l'autore della relazione, Sergey Zaika, fa parte del Comitato Programma della nostra nuova conferenza sulla gestione della conoscenza <noindex><a rel=\"nofollow\" href=\"https:\/\/knowledgeconf.ru\/2019\">KnowledgeConf<\/a><\/noindex>. La conferenza \u00e8 di un giorno e si terr\u00e0 il 26 aprile, ma il programma \u00e8 molto ricco.<br \/>\nE a maggio ci sar\u00e0 <noindex><a rel=\"nofollow\" href=\"https:\/\/phprussia.ru\/2019\">PHP Russia<\/a><\/noindex> e\u00a0<noindex><a rel=\"nofollow\" href=\"https:\/\/ritfest.ru\/2019\">RIT++<\/a><\/noindex> (con DevOpsConf inclusa) \u2013 l\u00ec si pu\u00f2 ancora proporre un proprio tema, raccontare la propria esperienza e lamentarsi delle proprie difficolt\u00e0.<\/p><\/blockquote>\n<p>Fonte: <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.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441 \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434? \u041c\u043e\u0442\u0438\u0432\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u0430\u044f: \u043e\u0442 \u0437\u0430\u043a\u043e\u043d\u043e\u0434\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0434\u043e \u043f\u0440\u0438\u0441\u0443\u0449\u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041d\u043e \u044d\u0442\u043e \u0432\u043e\u0432\u0441\u0435 \u043d\u0435 \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0432\u044b\u0433\u043e\u0434\u0443. \u0412 \u0447\u0435\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c events-driven API \u043d\u0430 Kafka, \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u0421\u0435\u0440\u0433\u0435\u0439 \u0417\u0430\u0438\u043a\u0430 (fewald). \u041f\u0440\u043e \u043d\u0430\u0431\u0438\u0442\u044b\u0435 \u0448\u0438\u0448\u043a\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u043e\u0436\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041e\u043f\u044b\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 Refund Tool \u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c API \u043d\u0430 Kafka | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441 \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434? \u041c\u043e\u0442\u0438\u0432\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u0430\u044f: \u043e\u0442 \u0437\u0430\u043a\u043e\u043d\u043e\u0434\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0434\u043e \u043f\u0440\u0438\u0441\u0443\u0449\u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041d\u043e \u044d\u0442\u043e \u0432\u043e\u0432\u0441\u0435 \u043d\u0435 \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0432\u044b\u0433\u043e\u0434\u0443. \u0412 \u0447\u0435\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c events-driven API \u043d\u0430 Kafka, \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u0421\u0435\u0440\u0433\u0435\u0439 \u0417\u0430\u0438\u043a\u0430 (fewald). \u041f\u0440\u043e \u043d\u0430\u0431\u0438\u0442\u044b\u0435 \u0448\u0438\u0448\u043a\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u043e\u0436\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/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\udd47Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka | ProHoster","description":"Cosa pu\u00f2 spingere un'azienda cos\u00ec grande come Lamoda, con processi collaudati e decine di servizi interconnessi, a cambiare sostanzialmente approccio? Le motivazioni possono essere diverse: da quelle legislative al desiderio innato di tutti i programmatori di sperimentare. Ma ci\u00f2 non significa che non si possa sperare in un beneficio aggiuntivo. In che modo specifico si pu\u00f2 guadagnare implementando un'API event-driven su Kafka, ce lo spiegher\u00e0 Sergey Zaika (fewald). Condivider\u00e0 anche le difficolt\u00e0 incontrate e le scoperte interessanti.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041e\u043f\u044b\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 Refund Tool \u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c API \u043d\u0430 Kafka | ProHoster","og:description":"\u0427\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044e \u043a\u0430\u043a Lamoda \u0441 \u043e\u0442\u043b\u0430\u0436\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c \u0438 \u0434\u0435\u0441\u044f\u0442\u043a\u0430\u043c\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043c\u0435\u043d\u044f\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434? \u041c\u043e\u0442\u0438\u0432\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u0430\u044f: \u043e\u0442 \u0437\u0430\u043a\u043e\u043d\u043e\u0434\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0434\u043e \u043f\u0440\u0438\u0441\u0443\u0449\u0435\u0433\u043e \u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442\u0430\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c. \u041d\u043e \u044d\u0442\u043e \u0432\u043e\u0432\u0441\u0435 \u043d\u0435 \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0432\u044b\u0433\u043e\u0434\u0443. \u0412 \u0447\u0435\u043c \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0438\u0433\u0440\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0432\u043d\u0435\u0434\u0440\u0438\u0442\u044c events-driven API \u043d\u0430 Kafka, \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u0421\u0435\u0440\u0433\u0435\u0439 \u0417\u0430\u0438\u043a\u0430 (fewald). \u041f\u0440\u043e \u043d\u0430\u0431\u0438\u0442\u044b\u0435 \u0448\u0438\u0448\u043a\u0438 \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u043e\u0436\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e","og:url":"https:\/\/prohoster.info\/it\/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\/it\/wp-json\/wp\/v2\/posts\/30778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=30778"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/30778\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/22763"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=30778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=30778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=30778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}