{"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\/fr\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","title":{"rendered":"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Qu'est-ce qui peut pousser une grande entreprise comme Lamoda, avec un processus bien rod\u00e9 et des dizaines de services interconnect\u00e9s, \u00e0 changer radicalement d'approche ? Les motivations peuvent \u00eatre vari\u00e9es : de la l\u00e9gislation au d\u00e9sir d'exp\u00e9rimentation qui caract\u00e9rise tous les programmeurs.<\/p>\n<p>Mais cela ne signifie pas qu'il n'y a pas d'avantages suppl\u00e9mentaires \u00e0 en attendre. En quoi peut-on r\u00e9ellement b\u00e9n\u00e9ficier de l'impl\u00e9mentation d'une API bas\u00e9e sur des \u00e9v\u00e9nements avec Kafka, nous l'expliquera Sergue\u00ef Za\u00efka (<noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/users\/fewald\/\" class=\"user_link\">fewald<\/a><\/noindex>). Des le\u00e7ons tir\u00e9es d'exp\u00e9riences pass\u00e9es et des d\u00e9couvertes fascinantes seront \u00e9galement partag\u00e9es \u2013 une exp\u00e9rience ne peut se passer d'elles.<\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/7ab959ab45ec5c6565b35b18b361c0ea.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n<em>Avertissement : Cet article est bas\u00e9 sur les mat\u00e9riaux d'un meetup que Sergue\u00ef a tenu en novembre 2018 lors de HighLoad++. L\u2019exp\u00e9rience concr\u00e8te de Lamoda avec Kafka a captiv\u00e9 l\u2019auditoire tout autant que d'autres pr\u00e9sentations de l'\u00e9v\u00e9nement. Nous pensons que c'est un excellent exemple de la n\u00e9cessit\u00e9 de toujours trouver des parties prenantes, et les organisateurs de HighLoad++ continueront \u00e0 cr\u00e9er une ambiance propice \u00e0 cela.<\/em><br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h2>Concernant le processus<\/h2>\n<p>\nLamoda est une grande plateforme e-commerce disposant de son propre centre de contact, de son service de livraison (ainsi que de nombreux partenaires), d'un studio photo, d'un immense entrep\u00f4t, et tout cela fonctionne avec son propre logiciel. Il existe des dizaines de m\u00e9thodes de paiement, des partenaires B2B qui peuvent utiliser une partie ou la totalit\u00e9 de ces services et qui souhaitent conna\u00eetre les informations actualis\u00e9es sur leurs produits. De plus, Lamoda op\u00e8re dans trois pays en plus de la Russie et chaque march\u00e9 a ses sp\u00e9cificit\u00e9s. En tout, il y a probablement plus d'une centaine de fa\u00e7ons de configurer une nouvelle commande qui doit \u00eatre trait\u00e9e d'une certaine mani\u00e8re. Tout cela fonctionne gr\u00e2ce \u00e0 des dizaines de services qui communiquent parfois de mani\u00e8re peu \u00e9vidente. Il y a \u00e9galement un syst\u00e8me central dont la principale responsabilit\u00e9 est de g\u00e9rer les statuts des commandes. Nous l'appelons BOB, et je travaille avec lui.<\/p>\n<h2>Outil de Remboursement avec API bas\u00e9e sur des \u00e9v\u00e9nements <\/h2>\n<p>\nLe terme 'bas\u00e9 sur des \u00e9v\u00e9nements' est assez r\u00e9pandu, et nous pr\u00e9ciserons un peu plus tard ce que cela signifie. Je commencerai par le contexte dans lequel nous avons d\u00e9cid\u00e9 de tester l'approche de l'API bas\u00e9e sur des \u00e9v\u00e9nements avec Kafka. <\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/3bfdce47dd8420fc63d84645e76de647.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDans tout magasin, en plus des commandes pour lesquelles les clients paient, il arrive que le magasin doive rembourser de l'argent parce que le produit ne convient pas au client. Ce processus relativement court consiste \u00e0 v\u00e9rifier les informations, si n\u00e9cessaire, puis \u00e0 transf\u00e9rer les fonds. <\/p>\n<p>Cependant, le retour est devenu plus compliqu\u00e9 en raison des changements l\u00e9gislatifs, et nous avons d\u00fb mettre en place un microservice s\u00e9par\u00e9 pour cela.<\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e358df5476e7448976e0f1147a103fb.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNotre motivation :<\/p>\n<ol>\n<li><strong>Loi FZ-54<\/strong>\u00a0\u2014 en r\u00e9sum\u00e9, la loi exige de signaler \u00e0 l'administration fiscale chaque op\u00e9ration financi\u00e8re, qu'il s'agisse d'un retour ou d'une entr\u00e9e, dans un SLA assez court de quelques minutes. Nous, en tant qu'e-commerce, effectuons de nombreuses op\u00e9rations. Techniquement, cela signifie une nouvelle responsabilit\u00e9 (et donc un nouveau service) et des modifications dans tous les syst\u00e8mes concern\u00e9s.<\/li>\n<li><strong>BOB split<\/strong>\u00a0\u2014 un projet interne de l'entreprise visant \u00e0 lib\u00e9rer BOB d'un grand nombre de responsabilit\u00e9s non essentielles et \u00e0 r\u00e9duire sa complexit\u00e9 globale.<\/li>\n<\/ol>\n<p>\n<img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/d3ecf9961bdb372fc5f84ee9389f73ef.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nCe sch\u00e9ma repr\u00e9sente les principaux syst\u00e8mes de Lamoda. Actuellement, la plupart d'entre eux sont plut\u00f4t <strong>un regroupement de 5 \u00e0 10 microservices autour d'un monolithe en r\u00e9duction.<\/strong>Ils croissent lentement, mais nous essayons de les r\u00e9duire, car d\u00e9ployer un fragment isol\u00e9 au milieu est effrayant \u2014 il ne faut pas permettre qu'il \u00e9choue. Tous les \u00e9changes (fl\u00e8ches) doivent \u00eatre r\u00e9serv\u00e9s, anticipant que l'un d'eux pourrait \u00eatre indisponible.<\/p>\n<p>Il y a aussi pas mal d'\u00e9changes dans BOB : syst\u00e8mes de paiement, de livraison, de notification, etc. <\/p>\n<p>Techniquement, BOB c'est :<\/p>\n<ul>\n<li>~150k lignes de code + ~100k lignes de tests ;<\/li>\n<li>php7.2 + Zend 1 &amp; Symfony Components 3 ;<\/li>\n<li>&gt;100 API &amp; ~50 int\u00e9grations sortantes ;<\/li>\n<li>4 pays avec leur propre logique commerciale. <\/li>\n<\/ul>\n<p>\nD\u00e9ployer BOB co\u00fbte cher et est difficile, la quantit\u00e9 de code et les t\u00e2ches qu'il g\u00e8re sont telles que personne ne peut le garder en t\u00eate enti\u00e8rement. En somme, il y a beaucoup de raisons de le simplifier.<\/p>\n<h2>Processus de retour<\/h2>\n<p>\nAu d\u00e9part, deux syst\u00e8mes sont impliqu\u00e9s : BOB et Payment. Maintenant, deux autres apparaissent :<\/p>\n<ul>\n<li>Fiscalization Service, qui s'attaquera aux probl\u00e8mes de fiscalisation et communiquera avec des services externes.<\/li>\n<li>Refund Tool, o\u00f9 de nouveaux \u00e9changes sont simplement transf\u00e9r\u00e9s pour ne pas alourdir BOB.<\/li>\n<\/ul>\n<p>\nMaintenant, le processus ressemble \u00e0 ceci :<\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/13c02975881ad35c61304053c604cda3.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<ol>\n<li>Une demande de remboursement arrive \u00e0 BOB.<\/li>\n<li>BOB en informe le Refund Tool.<\/li>\n<li>Refund Tool informe Payment : \u00ab Remboursez l'argent \u00bb.<\/li>\n<li>Payment rembourse l'argent.<\/li>\n<li>Refund Tool et BOB synchronisent leurs statuts, car pour le moment, cela leur est n\u00e9cessaire \u00e0 tous les deux. Nous ne sommes pas encore pr\u00eats \u00e0 nous d\u00e9connecter compl\u00e8tement dans Refund Tool, car BOB a une interface utilisateur, des rapports pour la comptabilit\u00e9, et beaucoup de donn\u00e9es qui ne peuvent pas \u00eatre transf\u00e9r\u00e9es facilement. Nous devons rester sur deux chaises.<\/li>\n<li>Une demande de fiscalisation est envoy\u00e9e.<\/li>\n<\/ol>\n<p>\nEn fin de compte, nous avons cr\u00e9\u00e9 une sorte de bus d'\u00e9v\u00e9nements sur Kafka, qui est devenu notre point de r\u00e9f\u00e9rence. Hourra, maintenant nous avons un point de d\u00e9faillance unique (sarcasme).<\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/674edd7972998b4985071f5250612c7e.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nLes avantages et les inconv\u00e9nients sont assez \u00e9vidents. Nous avons cr\u00e9\u00e9 un bus, donc maintenant tous les services en d\u00e9pendent. Cela simplifie la conception, mais introduit un point de d\u00e9faillance unique dans le syst\u00e8me. Si Kafka tombe, le processus s'arr\u00eate.<\/p>\n<h2>Qu'est-ce qu'une API bas\u00e9e sur les \u00e9v\u00e9nements ? <\/h2>\n<p>\nUne bonne r\u00e9ponse \u00e0 cette question se trouve dans le rapport de Martin Fowler (GOTO 2017). <noindex><a rel=\"nofollow\" href=\"https:\/\/youtu.be\/STKCRSUsyPO\">\u00abLes multiples significations de l'architecture pilot\u00e9e par les \u00e9v\u00e9nements\u00bb<\/a><\/noindex>. <\/p>\n<p>En r\u00e9sum\u00e9, voici ce que nous avons fait :<\/p>\n<ol>\n<li>Nous avons encapsul\u00e9 tous les \u00e9changes asynchrones via <strong>le stockage des \u00e9v\u00e9nements.<\/strong>Au lieu de communiquer par le r\u00e9seau avec chaque consommateur concern\u00e9 concernant un changement de statut, nous \u00e9crivons dans un stockage centralis\u00e9 un \u00e9v\u00e9nement de changement d'\u00e9tat, et les consommateurs int\u00e9ress\u00e9s par le sujet lisent tout ce qui appara\u00eet l\u00e0-bas.<\/li>\n<li>Un \u00e9v\u00e9nement dans ce contexte est une notification (<strong>notifications<\/strong>) que quelque chose a chang\u00e9 quelque part. Par exemple, le statut d'une commande a chang\u00e9. Un consommateur qui a besoin de certaines donn\u00e9es accompagnant le changement de statut, et qui ne sont pas dans la notification, peut v\u00e9rifier son \u00e9tat lui-m\u00eame.<\/li>\n<li>La version maximale serait un event sourcing complet, <strong>transfert d'\u00e9tat<\/strong>, o\u00f9 un \u00e9v\u00e9nement contient toutes les informations n\u00e9cessaires au traitement : d'o\u00f9 et dans quel statut cela a chang\u00e9, comment les donn\u00e9es ont \u00e9t\u00e9 modifi\u00e9es, etc. La question ne concerne que la faisabilit\u00e9 et le volume d'informations que vous pouvez vous permettre de stocker.<\/li>\n<\/ol>\n<p>\nDans le cadre du lancement de l'outil de remboursement, nous avons utilis\u00e9 la troisi\u00e8me option. Cela a simplifi\u00e9 le traitement des \u00e9v\u00e9nements, car aucune information d\u00e9taill\u00e9e n'a besoin d'\u00eatre r\u00e9cup\u00e9r\u00e9e, et cela a exclu le sc\u00e9nario o\u00f9 chaque nouvel \u00e9v\u00e9nement d\u00e9clenche une vague de requ\u00eates GET d'\u00e9claircissement de la part des consommateurs.<\/p>\n<p>Le service de remboursement <strong>n'est pas charg\u00e9<\/strong>, donc Kafka est plut\u00f4t un essai qu'une n\u00e9cessit\u00e9. Je ne pense pas que si le service de remboursement devenait un projet \u00e0 fort trafic, l'entreprise serait contente.<\/p>\n<h4>\u00c9change asynchrone tel quel<\/h4>\n<p>\nPour les \u00e9changes asynchrones, le d\u00e9partement PHP utilise g\u00e9n\u00e9ralement RabbitMQ. Nous rassemblons les donn\u00e9es pour la demande, les mettons dans une file d'attente, et le consommateur de ce m\u00eame service les lit et les envoie (ou ne les envoie pas). Pour l'API, Lamoda utilise activement Swagger. Nous concevons l'API, la d\u00e9crivons dans Swagger, et g\u00e9n\u00e9rons le code client et serveur. Nous utilisons aussi un JSON RPC 2.0 l\u00e9g\u00e8rement \u00e9tendu. <\/p>\n<p>Des bus ESB sont utilis\u00e9s par certains, d'autres vivent sur ActiveMQ, mais dans l'ensemble, <strong>RabbitMQ - standard<\/strong>.<\/p>\n<h4>\u00c9change asynchrone \u00c0 FAIRE<\/h4>\n<p>\nEn concevant l'\u00e9change via le bus d'\u00e9v\u00e9nements, on peut faire une analogie. Nous d\u00e9crivons de mani\u00e8re similaire l'\u00e9change futur de donn\u00e9es \u00e0 travers des descriptions de la structure de l'\u00e9v\u00e9nement. Le format yaml, la g\u00e9n\u00e9ration de code devait \u00eatre effectu\u00e9e par nos soins, le g\u00e9n\u00e9rateur selon la sp\u00e9cification cr\u00e9e des DTO et enseigne aux clients et aux serveurs comment travailler avec eux. La g\u00e9n\u00e9ration se fait dans deux langages - <strong>golang et php<\/strong>. Cela permet de garder les biblioth\u00e8ques coh\u00e9rentes. Le g\u00e9n\u00e9rateur est \u00e9crit en golang, ce qui lui a valu le nom de gogi.<\/p>\n<p>Event sourcing sur Kafka est quelque chose de typique. Il existe une solution de la version enterprise principale Kafka Confluent, il y a <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/zalando\/nakadi\">nakadi<\/a><\/noindex>, une solution de nos \u00ab fr\u00e8res \u00bb dans le domaine de Zalando. Notre <strong>motivation pour commencer avec Kafka vanilla<\/strong>\u00a0est de garder la solution gratuite, tant que nous n'avons pas d\u00e9cid\u00e9 de l'utiliser de mani\u00e8re g\u00e9n\u00e9ralis\u00e9e, et \u00e9galement de nous laisser de la place pour man\u0153uvrer et am\u00e9liorer : nous voulons le support de notre <strong>JSON RPC 2.0<\/strong>, des g\u00e9n\u00e9rateurs pour deux langages et voir ce qu'il y a d'autre. <\/p>\n<p>Ironiquement, m\u00eame dans un tel cas heureux, o\u00f9 il existe une entreprise \u00e0 peu pr\u00e8s similaire \u00e0 Zalando, qui a fait une solution \u00e0 peu pr\u00e8s similaire, nous ne pouvons pas l'utiliser efficacement. <\/p>\n<p>Architecturalement, au lancement, le mod\u00e8le est le suivant : nous lisons directement \u00e0 partir de Kafka, mais \u00e9crivons uniquement via le bus d'\u00e9v\u00e9nements. Pour la lecture, il y a beaucoup de choses pr\u00eates : brokers, \u00e9quilibreur de charge et elle est plus ou moins pr\u00eate pour le scaling horizontal, c'est quelque chose que nous voulions conserver. L'\u00e9criture, en revanche, nous avons voulu l'encapsuler via un Gateway alias Events-bus, et voici pourquoi.<\/p>\n<h3>Events-bus<\/h3>\n<p>\nOu bus d'\u00e9v\u00e9nements. C'est simplement un gateway http sans \u00e9tat, qui prend plusieurs r\u00f4les importants :<\/p>\n<ul>\n<li><strong>Validation du production<\/strong>\u00a0\u2014 nous v\u00e9rifions que les \u00e9v\u00e9nements r\u00e9pondent \u00e0 notre sp\u00e9cification.<\/li>\n<li><strong>Syst\u00e8me ma\u00eetre des \u00e9v\u00e9nements<\/strong>, c'est-\u00e0-dire que c'est le syst\u00e8me principal et unique dans l'entreprise, qui r\u00e9pond \u00e0 la question, quels \u00e9v\u00e9nements avec quelles structures sont consid\u00e9r\u00e9s comme valides. Dans la validation, sont inclus simplement des types de donn\u00e9es et des enums pour sp\u00e9cifier strictement le contenu. <\/li>\n<li><strong>Fonction de hachage<\/strong> pour le partitionnement - la structure du message Kafka est de type key-value et c'est \u00e0 partir du hachage de la cl\u00e9 que l'on calcule o\u00f9 mettre cela.<\/li>\n<\/ul>\n<p><\/p>\n<h3>Pourquoi<\/h3>\n<p>\nNous travaillons dans une grande entreprise avec un processus bien rod\u00e9. Pourquoi changer quelque chose ? <strong>C'est une exp\u00e9rience<\/strong>, et nous pr\u00e9voyons d'en tirer plusieurs avantages.<\/p>\n<h4>\u00c9changes 1:n+1 (un \u00e0 plusieurs)<\/h4>\n<p>\nAvec Kafka, il est tr\u00e8s simple de connecter de nouveaux consommateurs \u00e0 l'API. <\/p>\n<p>Supposons que vous ayez un annuaire qui doit \u00eatre mis \u00e0 jour dans plusieurs syst\u00e8mes en m\u00eame temps (y compris dans de nouveaux). Auparavant, nous avions invent\u00e9 un bundle qui impl\u00e9mentait le set-API, et la master-system informait les adresses des consommateurs. Maintenant, la master-system envoie des mises \u00e0 jour dans un topic, et tous ceux que cela int\u00e9resse les lisent. Un nouveau syst\u00e8me est apparu \u2014 il a \u00e9t\u00e9 abonn\u00e9 au topic. Oui, c'est aussi un bundle, mais plus simple.<\/p>\n<p>Dans le cas de l'outil de remboursement, qui est en fait une petite partie de BOB, il nous convient de les synchroniser via Kafka. Le paiement indique que l'argent a \u00e9t\u00e9 rembours\u00e9 : BOB et RT en ont \u00e9t\u00e9 inform\u00e9s, ont mis \u00e0 jour leurs statuts, le service de fiscalisation en a \u00e9t\u00e9 inform\u00e9 et a \u00e9mis le re\u00e7u.<\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b01b22a333b58e87aeef0c52d40e6960.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nNous avons des plans pour cr\u00e9er un service de notifications unifi\u00e9, qui informerait le client des nouvelles concernant sa commande\/retours. Actuellement, cette responsabilit\u00e9 est dispers\u00e9e entre les syst\u00e8mes. Il nous suffira d'apprendre au service de notifications \u00e0 extraire les informations pertinentes de Kafka et \u00e0 y r\u00e9agir (et \u00e0 d\u00e9sactiver ces notifications dans les autres syst\u00e8mes). Aucun nouvel \u00e9change direct ne sera n\u00e9cessaire.<\/p>\n<h4>Ax\u00e9 sur les donn\u00e9es<\/h4>\n<p>\nL'information entre les syst\u00e8mes devient transparente \u2014 quel que soit le \u00ab gros entreprise \u00bb que vous ayez et peu importe la taille de votre backlog. Lamoda dispose d'un d\u00e9partement d'analyse de donn\u00e9es qui collecte des donn\u00e9es sur les syst\u00e8mes et les normalise pour les rendre r\u00e9utilisables, tant pour le business que pour les syst\u00e8mes intelligents. Kafka permet de leur fournir rapidement beaucoup de donn\u00e9es et de maintenir ce flux d'information \u00e0 jour.<\/p>\n<h4>Journal de r\u00e9plication<\/h4>\n<p>\nLes messages ne disparaissent pas apr\u00e8s avoir \u00e9t\u00e9 lus, comme dans RabbitMQ. Lorsque l'\u00e9v\u00e9nement contient suffisamment d'informations pour \u00eatre trait\u00e9, nous avons un historique des derni\u00e8res modifications apport\u00e9es \u00e0 l'objet, et, si vous le souhaitez, la possibilit\u00e9 d'appliquer ces modifications.<\/p>\n<p>La dur\u00e9e de conservation du journal de r\u00e9plication d\u00e9pend de l'intensit\u00e9 des \u00e9critures dans ce topic. Kafka permet de configurer de mani\u00e8re flexible les limites de temps de stockage et le volume des donn\u00e9es. Pour les topics \u00e0 fort trafic, il est important que tous les consommateurs puissent lire les informations avant qu'elles ne disparaissent, m\u00eame en cas de d\u00e9faillance temporaire. En g\u00e9n\u00e9ral, nous arrivons \u00e0 conserver les donn\u00e9es pendant\u00a0<strong>un certain nombre de jours<\/strong>, ce qui est tout \u00e0 fait suffisant pour le support. <\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/0e08dd384155289123ebee96430c2370.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nUn petit r\u00e9sum\u00e9 de la documentation pour ceux qui ne sont pas familiers avec Kafka (l'image provient \u00e9galement de la documentation)<\/p>\n<p>Dans AMQP, il existe des files d'attente : nous \u00e9crivons des messages dans une file d'attente pour le consommateur. En r\u00e8gle g\u00e9n\u00e9rale, une seule file d'attente est g\u00e9r\u00e9e par un syst\u00e8me avec la m\u00eame logique commerciale. Si plusieurs syst\u00e8mes doivent \u00eatre notifi\u00e9s, l'application peut \u00eatre configur\u00e9e pour \u00e9crire dans plusieurs files d'attente ou configurer un exchange avec un m\u00e9canisme fanout, qui les clone automatiquement.<\/p>\n<p>Dans Kafka, il existe une abstraction similaire <em>topic<\/em>, dans laquelle vous \u00e9crivez des messages, mais ils ne disparaissent pas apr\u00e8s lecture. Par d\u00e9faut, lorsque vous vous connectez \u00e0 Kafka, vous recevez tous les messages, et vous avez la possibilit\u00e9 de conserver la position \u00e0 laquelle vous vous \u00eates arr\u00eat\u00e9. C'est-\u00e0-dire que vous lisez s\u00e9quentiellement, vous n'\u00eates pas oblig\u00e9 de marquer le message comme lu, mais vous pouvez sauvegarder l'id \u00e0 partir duquel vous continuerez \u00e0 lire. L'id \u00e0 laquelle vous vous \u00eates arr\u00eat\u00e9 s'appelle offset, et le m\u00e9canisme est ce qu'on appelle commit offset. <\/p>\n<p>Par cons\u00e9quent, il est possible de mettre en \u0153uvre diff\u00e9rentes logiques. Par exemple, nous avons BOB qui existe en 4 instances pour diff\u00e9rents pays - Lamoda est pr\u00e9sent en Russie, au Kazakhstan, en Ukraine et en Bi\u00e9lorussie. \u00c9tant donn\u00e9 qu'ils sont d\u00e9ploy\u00e9s s\u00e9par\u00e9ment, ils ont un peu leurs propres configurations et leur propre logique commerciale. Nous indiquons dans le message \u00e0 quel pays il se rattache. Chaque consommateur BOB dans chaque pays lit avec diff\u00e9rents groupId, et, si le message ne le concerne pas, il l'ignore, c'est-\u00e0-dire qu'il commet imm\u00e9diatement offset +1. Si le m\u00eame topic est lu par notre Service de Paiement, il le fait avec un groupe s\u00e9par\u00e9, et c'est pourquoi les offsets ne se chevauchent pas.<\/p>\n<p><b>Exigences sur les \u00e9v\u00e9nements :<\/b><\/p>\n<ul>\n<li><strong>Compl\u00e9tude des donn\u00e9es. <\/strong>Il serait souhaitable que l'\u00e9v\u00e9nement contienne suffisamment de donn\u00e9es pour pouvoir \u00eatre trait\u00e9. <\/li>\n<\/ul>\n<p><\/p>\n<ul>\n<li><strong>Int\u00e9grit\u00e9. <\/strong>Nous d\u00e9l\u00e9guons au bus d'\u00e9v\u00e9nements la v\u00e9rification de la coh\u00e9rence de l'\u00e9v\u00e9nement et de sa capacit\u00e9 \u00e0 \u00eatre trait\u00e9.<\/li>\n<li><strong>L'ordre est important. <\/strong>Dans le cas d'un retour, nous devons travailler avec l'historique. Pour les notifications, l'ordre n'est pas important, si ce sont des notifications homog\u00e8nes, l'email sera identique peu importe lequel des ordres est arriv\u00e9 en premier. Dans le cas d'un retour, il y a un processus clair, si l'ordre est modifi\u00e9, cela peut entra\u00eener des exceptions, un remboursement ne sera pas cr\u00e9\u00e9 ou trait\u00e9 - nous entrerons dans un autre statut.<\/li>\n<li><strong>Coh\u00e9rence. <\/strong>Nous avons un stockage, et maintenant nous cr\u00e9ons des \u00e9v\u00e9nements au lieu d'une API. Nous avons besoin d'un moyen rapide et peu co\u00fbteux de transmettre des informations sur de nouveaux \u00e9v\u00e9nements et sur les modifications des \u00e9v\u00e9nements existants \u00e0 nos services. Cela est r\u00e9alis\u00e9 gr\u00e2ce \u00e0 une sp\u00e9cification commune dans un d\u00e9p\u00f4t git s\u00e9par\u00e9 et des g\u00e9n\u00e9rateurs de code. Ainsi, les clients et les serveurs dans diff\u00e9rents services sont synchronis\u00e9s.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Kafka chez Lamoda<\/h2>\n<p>\nNous avons trois installations de Kafka : <\/p>\n<ol>\n<li>Logs ;<\/li>\n<li>R&amp;D ;<\/li>\n<li>Events-bus.<\/li>\n<\/ol>\n<p>\nAujourd'hui, nous ne parlons que du dernier point. Dans l'events-bus, nous avons des installations plut\u00f4t modestes - 3 courtiers (serveurs) et seulement 27 sujets. En g\u00e9n\u00e9ral, un sujet correspond \u00e0 un processus. Mais c'est un point d\u00e9licat, et nous allons y revenir.<\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/f398852689b31429cc97b4cbffcabab5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nCi-dessus, le graphique des rps. Le processus des remboursements est marqu\u00e9 par une ligne turquoise (oui, celle qui est sur l'axe X), et par une ligne rose - le processus de mise \u00e0 jour du contenu. <\/p>\n<p>Le catalogue Lamoda contient des millions de produits, et les donn\u00e9es sont constamment mises \u00e0 jour. Certaines collections sortent de la mode, tandis que de nouvelles sont lanc\u00e9es, de nouveaux mod\u00e8les apparaissent en permanence dans le catalogue. Nous essayons de pr\u00e9dire ce qui pourrait int\u00e9resser nos clients demain, c'est pourquoi nous achetons constamment de nouvelles choses, les photographions et mettons \u00e0 jour la vitrine. <\/p>\n<p>Les pics roses repr\u00e9sentent des mises \u00e0 jour de produits, c'est-\u00e0-dire des modifications concernant les articles. On peut voir que les \u00e9quipes ont photographi\u00e9, photographi\u00e9, puis soudain ! - ils ont t\u00e9l\u00e9charg\u00e9 un lot d'\u00e9v\u00e9nements.<\/p>\n<h2>Cas d'utilisation de Lamoda Events<\/h2>\n<p>\nL'architecture construite est utilis\u00e9e pour de telles op\u00e9rations :<\/p>\n<ul>\n<li><strong>Suivi des statuts des retours<\/strong>: appel \u00e0 l'action et suivi des statuts de tous les syst\u00e8mes impliqu\u00e9s. Paiement, statuts, fiscalisation, notifications. Ici, nous avons essay\u00e9 une approche, cr\u00e9\u00e9 des outils, rassembl\u00e9 tous les bogues, \u00e9crit la documentation et expliqu\u00e9 \u00e0 nos coll\u00e8gues comment les utiliser.<\/li>\n<li><strong>Mise \u00e0 jour des fiches produit : <\/strong>configuration, m\u00e9tadonn\u00e9es, caract\u00e9ristiques. Une seule syst\u00e8me lit (celui qui affiche), tandis que plusieurs \u00e9crivent.<\/li>\n<li><strong>Email, push et sms<\/strong>: commande rassembl\u00e9e, commande arriv\u00e9e, retour accept\u00e9, etc., il y en a beaucoup. <\/li>\n<li><strong>Stock, mise \u00e0 jour des inventaires<\/strong>\u00a0- mise \u00e0 jour quantitative des articles, juste des chiffres : arriv\u00e9e au stock, retour. Il est n\u00e9cessaire que tous les syst\u00e8mes li\u00e9s \u00e0 la r\u00e9servation de produits fonctionnent avec les donn\u00e9es les plus \u00e0 jour possibles. Actuellement, le syst\u00e8me de mise \u00e0 jour des stocks est assez complexe, Kafka permettra de le simplifier.<\/li>\n<li><strong>Analyse des donn\u00e9es<\/strong> (D\u00e9partement R&amp;D), outils ML, analytics, statistiques. Nous souhaitons que les informations soient transparentes \u2014 c'est pourquoi Kafka convient bien.<\/li>\n<\/ul>\n<p>\nMaintenant, la partie la plus int\u00e9ressante concernant les erreurs commises et les d\u00e9couvertes fascinantes qui ont eu lieu au cours des six derniers mois.<\/p>\n<h2>Probl\u00e8mes de conception<\/h2>\n<p>\nSupposons que nous souhaitons cr\u00e9er une nouvelle fonctionnalit\u00e9 \u2014 par exemple, transf\u00e9rer l'ensemble du processus de livraison vers Kafka. Actuellement, une partie du processus est impl\u00e9ment\u00e9e dans Order Processing dans BOB. Dans la transmission de la commande au service de livraison, le d\u00e9placement vers l'entrep\u00f4t interm\u00e9diaire et d'autres \u00e9l\u00e9ments, il y a un mod\u00e8le de statut. Il existe un monolithe entier, m\u00eame deux, plus une multitude d'API d\u00e9di\u00e9es \u00e0 la livraison. Ils en savent beaucoup plus sur la livraison. <\/p>\n<p>Il semble que ce soient des domaines similaires, mais pour Order Processing dans BOB et pour le syst\u00e8me de livraison, les statuts sont diff\u00e9rents. Par exemple, certains services de messagerie n'envoient pas de statuts interm\u00e9diaires, mais seulement finaux : \u00ab livr\u00e9 \u00bb ou \u00ab perdu \u00bb. D'autres, en revanche, informent tr\u00e8s pr\u00e9cis\u00e9ment sur le d\u00e9placement du produit. Chacun a ses propres r\u00e8gles de validation : pour certains, un email valide signifie qu'il sera trait\u00e9 ; pour d'autres \u2014 pas valide, mais la commande sera tout de m\u00eame trait\u00e9e car un num\u00e9ro de t\u00e9l\u00e9phone est disponible, et certains diront qu'une telle commande ne sera pas trait\u00e9e du tout.<\/p>\n<h3>Flux de donn\u00e9es<\/h3>\n<p>\nDans le cas de Kafka, la question de l'organisation du flux de donn\u00e9es se pose. Cette t\u00e2che est li\u00e9e au choix d'une strat\u00e9gie sur plusieurs points, examinons-les tous.<\/p>\n<h4>Dans un seul topic ou dans plusieurs ?<\/h4>\n<p>\nNous avons une sp\u00e9cification d'\u00e9v\u00e9nement. Dans BOB, nous \u00e9crivons que cette commande doit \u00eatre livr\u00e9e, et nous indiquons : le num\u00e9ro de commande, sa composition, certains SKU et codes-barres, etc. Lorsque le produit arrive \u00e0 l'entrep\u00f4t, la livraison peut recevoir des statuts, des timestamps et tout ce qu'il faut. Mais ensuite, nous voulons recevoir des mises \u00e0 jour sur ces donn\u00e9es dans BOB. Un processus inverse de r\u00e9cup\u00e9ration de donn\u00e9es de la livraison se met en place. Est-ce le m\u00eame \u00e9v\u00e9nement ? Ou s'agit-il d'un \u00e9change distinct qui m\u00e9rite un topic s\u00e9par\u00e9 ?<\/p>\n<p>Il est probable qu'ils soient tr\u00e8s similaires, et la tentation de cr\u00e9er un seul topic n'est pas infond\u00e9e, car un topic distinct signifie des consommateurs distincts, des configurations distinctes, une g\u00e9n\u00e9ration s\u00e9par\u00e9e de tout cela. Mais ce n'est pas certain.<\/p>\n<h4>Nouveau champ ou nouvel \u00e9v\u00e9nement ?<\/h4>\n<p>\nMais si nous utilisons les m\u00eames \u00e9v\u00e9nements, un autre probl\u00e8me se pose. Par exemple, tous les syst\u00e8mes de livraison ne peuvent pas g\u00e9n\u00e9rer un DTO qui puisse \u00eatre g\u00e9n\u00e9r\u00e9 par BOB. Nous leur envoyons un id, mais ils ne le conservent pas, car cela ne leur est pas n\u00e9cessaire, alors que du point de vue du d\u00e9marrage du processus event-bus, ce champ est obligatoire. <\/p>\n<p>Si nous \u00e9tablissons pour l\u2019event-bus la r\u00e8gle que ce champ est obligatoire, alors nous sommes contraints d'ajouter des r\u00e8gles de validation suppl\u00e9mentaires dans BOB ou dans le gestionnaire de l'\u00e9v\u00e9nement de d\u00e9marrage. La validation commence \u00e0 se disperser dans le service - ce n'est pas tr\u00e8s pratique.<\/p>\n<p>Un autre probl\u00e8me est la tentation du d\u00e9veloppement incr\u00e9mental. On nous dit qu'il faut ajouter quelque chose \u00e0 l'\u00e9v\u00e9nement et, peut-\u00eatre, si l'on r\u00e9fl\u00e9chit bien, cela aurait d\u00fb \u00eatre un \u00e9v\u00e9nement distinct. Mais dans notre sch\u00e9ma, un \u00e9v\u00e9nement distinct est un sujet distinct. Un sujet distinct correspond \u00e0 tout le processus que j'ai d\u00e9crit ci-dessus. Le d\u00e9veloppeur est tent\u00e9 d'ajouter simplement un autre champ dans le sch\u00e9ma JSON et de r\u00e9g\u00e9n\u00e9rer.<\/p>\n<p>Dans le cas des remboursements, nous avons ainsi abouti en six mois \u00e0 des \u00e9v\u00e9nements d'\u00e9v\u00e9nements. Nous avions un m\u00e9ta-\u00e9v\u00e9nement appel\u00e9 mise \u00e0 jour de remboursement, qui contenait un champ type, d\u00e9crivant en quoi consistait pr\u00e9cis\u00e9ment cette mise \u00e0 jour. De l\u00e0, nous avions des \u00ab\u00a0superbements\u00a0\u00bb des switches avec des validateurs qui indiquaient comment valider cet \u00e9v\u00e9nement avec ce type.<\/p>\n<h4>Versionnage des \u00e9v\u00e9nements<\/h4>\n<p>\nPour valider les messages dans Kafka, on peut utiliser <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.confluent.io\/current\/schema-registry\/docs\/index.html\">Avro<\/a><\/noindex>, mais il fallait d\u00e8s le d\u00e9part pr\u00e9voir cela et utiliser Confluent. Dans notre cas avec le versionnage, il faut \u00eatre prudent. Il ne sera pas toujours possible de relire les messages du journal de r\u00e9plication, car le mod\u00e8le \u00ab est parti \u00bb. En gros, on essaie de construire les versions de mani\u00e8re \u00e0 ce que le mod\u00e8le soit r\u00e9trocompatible : par exemple, rendre un champ temporairement non obligatoire. Si les diff\u00e9rences sont trop importantes, nous commen\u00e7ons \u00e0 \u00e9crire dans un nouveau sujet et nous transf\u00e9rons les clients lorsqu'ils ont fini de lire l'ancien.<\/p>\n<h4>Garantie de l'ordre de lecture des partitions<\/h4>\n<p>\nLes sujets dans Kafka sont divis\u00e9s en partitions. Cela n'est pas tr\u00e8s important tant que nous concevons des entit\u00e9s et des \u00e9changes, mais c'est crucial lorsque nous d\u00e9cidons comment les consommer et les \u00e9tendre.<\/p>\n<p>Dans un cas normal, vous publiez dans un seul topic Kafka. Par d\u00e9faut, un seul partition est utilis\u00e9, et tous les messages de ce topic y sont envoy\u00e9s. Le consommateur lit donc ces messages de mani\u00e8re s\u00e9quentielle. Supposons maintenant qu'il faille \u00e9tendre le syst\u00e8me afin que deux consommateurs diff\u00e9rents lisent les messages. Si, par exemple, vous envoyez un SMS, vous pouvez demander \u00e0 Kafka de cr\u00e9er un partition suppl\u00e9mentaire, et Kafka commencera \u00e0 r\u00e9partir les messages en deux parties - la moiti\u00e9 l\u00e0, la moiti\u00e9 ici. <\/p>\n<p>Comment Kafka les divise-t-il ? Chaque message a un corps (dans lequel nous stockons le JSON) et une cl\u00e9. Une fonction de hachage peut \u00eatre appliqu\u00e9e \u00e0 cette cl\u00e9, d\u00e9terminant ainsi \u00e0 quel partition le message sera affect\u00e9.<\/p>\n<p>Dans notre cas avec les remboursements, c'est important ; si nous prenons deux partitions, il y a une chance qu'un consommateur parall\u00e8le traite le deuxi\u00e8me \u00e9v\u00e9nement avant le premier, ce qui engendrera des probl\u00e8mes. La fonction de hachage garantit que les messages avec la m\u00eame cl\u00e9 iront dans le m\u00eame partition. <\/p>\n<h4>\u00c9v\u00e9nements vs commandes<\/h4>\n<p>\nC'est un autre probl\u00e8me auquel nous avons \u00e9t\u00e9 confront\u00e9s. Un \u00e9v\u00e9nement est quelque chose : nous disons que quelque chose s'est produit (something_happened), par exemple, un article a \u00e9t\u00e9 annul\u00e9 ou un remboursement a eu lieu. Si ces \u00e9v\u00e9nements sont \u00e9cout\u00e9s, alors \u00e0 \u00ab article annul\u00e9 \u00bb, une entit\u00e9 de remboursement sera cr\u00e9\u00e9e, et \u00ab remboursement effectu\u00e9 \u00bb sera consign\u00e9 quelque part dans les param\u00e8tres.<\/p>\n<p>Mais g\u00e9n\u00e9ralement, lorsque vous concevez des \u00e9v\u00e9nements, vous ne voulez pas les \u00e9crire en vain - vous vous attendez \u00e0 ce que quelqu'un les lise. Il y a une forte tentation d'\u00e9crire non pas something_happened (item_canceled, refund_refunded), mais something_should_be_done. Par exemple, article pr\u00eat pour le retour.<\/p>\n<p>D'un c\u00f4t\u00e9, cela indique comment l'\u00e9v\u00e9nement sera utilis\u00e9. D'un autre c\u00f4t\u00e9, cela semble beaucoup moins intitul\u00e9 comme un \u00e9v\u00e9nement normal. De plus, il n'est pas loin de l'ordre do_something. Mais vous n'avez aucune garantie que cet \u00e9v\u00e9nement a \u00e9t\u00e9 lu ; et s'il a \u00e9t\u00e9 lu, il a \u00e9t\u00e9 lu avec succ\u00e8s ; et s'il a \u00e9t\u00e9 lu avec succ\u00e8s, il a \u00e9t\u00e9 fait quelque chose, et ce quelque chose a r\u00e9ussi. Au moment o\u00f9 l'\u00e9v\u00e9nement devient do_something, un retour d'information devient n\u00e9cessaire, et c'est un probl\u00e8me.<\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/b755d91208092bd9791a41ce4633fb48.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDans l'\u00e9change asynchrone avec RabbitMQ, lorsque vous avez lu le message, que vous \u00eates all\u00e9 sur http, vous avez une r\u00e9ponse - au moins, que le message a \u00e9t\u00e9 re\u00e7u. Lorsque vous avez \u00e9crit dans Kafka, il y a un message indiquant que vous avez \u00e9crit dans Kafka, mais vous ne savez rien de son traitement. <\/p>\n<p>Dans notre cas, il a donc fallu mettre en place un \u00e9v\u00e9nement de r\u00e9ponse et configurer la surveillance pour que, si un certain nombre d'\u00e9v\u00e9nements survient, un nombre \u00e9quivalent d'\u00e9v\u00e9nements de r\u00e9ponse arrive dans un certain d\u00e9lai. Si cela ne se produit pas, cela signifie que quelque chose ne va pas. Par exemple, si nous avons envoy\u00e9 l'\u00e9v\u00e9nement \u00ab item_ready_to_refund \u00bb, nous nous attendons \u00e0 ce que le remboursement soit effectu\u00e9, que l'argent soit retourn\u00e9 au client et que nous recevions l'\u00e9v\u00e9nement \u00ab money_refunded \u00bb. Mais ce n'est pas toujours le cas, d'o\u00f9 la n\u00e9cessit\u00e9 d'une surveillance.<\/p>\n<h3>Nuances<\/h3>\n<p>\nIl y a un probl\u00e8me assez \u00e9vident : si vous lisez des messages de mani\u00e8re s\u00e9quentielle et que vous rencontrez un message d\u00e9faillant, le consommateur plante et vous ne pouvez plus avancer. Vous devez <strong>arr\u00eater tous les consommateurs<\/strong>, valider l'offset pour continuer la lecture.<\/p>\n<p>Nous \u00e9tions au courant de cela, nous nous y attendions, et cela s'est quand m\u00eame produit. Cela est arriv\u00e9 parce que l'\u00e9v\u00e9nement \u00e9tait valide du point de vue de l'events-bus, l'\u00e9v\u00e9nement \u00e9tait valide selon le validateur d'application, mais il n'\u00e9tait pas valide du point de vue de PostgreSQL, car nous avons dans un syst\u00e8me MySQL un UNSIGNED INT, tandis que dans le syst\u00e8me r\u00e9cemment d\u00e9velopp\u00e9, il y avait un INT simple dans PostgreSQL. Sa taille est un peu plus petite, et l'Id ne tenait pas. Symfony a plant\u00e9 avec une exception. Nous avons bien s\u00fbr intercept\u00e9 cette exception, car nous nous y attendions, et nous avions l'intention de valider cet offset, mais avant cela, nous voulions incr\u00e9menter le compteur de probl\u00e8mes, puisque le message a \u00e9chou\u00e9 \u00e0 \u00eatre trait\u00e9. Les compteurs de ce projet sont \u00e9galement stock\u00e9s dans la base, et Symfony avait d\u00e9j\u00e0 ferm\u00e9 la communication avec la base, et une deuxi\u00e8me exception a tu\u00e9 tout le processus sans possibilit\u00e9 de valider l'offset.<\/p>\n<p>Le service a \u00e9t\u00e9 hors ligne pendant un certain temps \u2013 heureusement, avec Kafka, ce n'est pas si grave, car les messages restent. Lorsque le travail sera r\u00e9tabli, ils pourront \u00eatre relus. C'est pratique.<\/p>\n<p>Kafka a la possibilit\u00e9, gr\u00e2ce \u00e0 des outils, de d\u00e9finir un offset arbitraire. Mais pour le faire, il faut arr\u00eater tous les consommateurs \u2013 dans notre cas, pr\u00e9parer une version distincte qui n\u2019aura pas de consommateurs, procedure de redeploiement. Alors, avec Kafka \u00e0 travers l'outil, vous pouvez d\u00e9caler l'offset et le message passera.<\/p>\n<p>Une autre nuance \u2013 <strong>journal de r\u00e9plication vs rdkafka.so<\/strong>\u00a0\u2014 est li\u00e9 \u00e0 la sp\u00e9cificit\u00e9 de notre projet. Nous utilisons PHP, et dans PHP, en g\u00e9n\u00e9ral, toutes les biblioth\u00e8ques interagissent avec Kafka via le d\u00e9p\u00f4t rdkafka.so, et ensuite, il y a une sorte d'enveloppe. Peut-\u00eatre que ce sont nos difficult\u00e9s personnelles, mais il s'est av\u00e9r\u00e9 qu'il n'est pas si facile de relire un morceau d\u00e9j\u00e0 lu. En somme, il y avait des probl\u00e8mes logiciels.<\/p>\n<p>En revenant aux particularit\u00e9s de travail avec les partitions, il est clairement indiqu\u00e9 dans la documentation <strong>consumers &gt;= topic partitions<\/strong>. Mais j'ai appris cela bien plus tard que je ne l'aurais voulu. Si vous voulez vous d\u00e9velopper et avoir deux consommateurs, vous avez besoin d'au moins deux partitions. C'est-\u00e0-dire que si vous aviez une seule partition, dans laquelle 20 000 messages se sont accumul\u00e9s, et que vous en avez fait une nouvelle, le nombre de messages ne s'\u00e9galera pas de sit\u00f4t. Donc, pour avoir deux consommateurs parall\u00e8les, il faut se pencher sur les partitions.<\/p>\n<h2>Surveillance<\/h2>\n<p>\nJe pense que, selon notre suivi, il sera encore plus clair quels probl\u00e8mes existent dans l'approche actuelle.<\/p>\n<p>Par exemple, nous comptons combien de produits dans la base ont r\u00e9cemment chang\u00e9 de statut et, par cons\u00e9quent, des \u00e9v\u00e9nements auraient d\u00fb se produire selon ces changements, et nous envoyons ce nombre \u00e0 notre syst\u00e8me de suivi. Ensuite, nous obtenons de Kafka un second nombre, combien d'\u00e9v\u00e9nements ont r\u00e9ellement \u00e9t\u00e9 enregistr\u00e9s. \u00c9videmment, la diff\u00e9rence entre ces deux nombres devrait toujours \u00eatre nulle.<\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/07d8ed08514f2fb97d9019466b96342c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDe plus, il faut surveiller comment \u00e7a se passe du c\u00f4t\u00e9 du producteur, si l'events-bus a re\u00e7u des messages, et comment \u00e7a se passe du c\u00f4t\u00e9 du consommateur. Par exemple, sur les graphes ci-dessous, tout va bien pour le Refund Tool, mais il y a clairement des probl\u00e8mes pour BOB (pics bleus).<\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/57112dbe70d2b388c53f19c34cca6f63.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nJ'ai d\u00e9j\u00e0 mentionn\u00e9 le lag des groupes de consommateurs. Grosso modo, c'est le nombre de messages non lus. En g\u00e9n\u00e9ral, nos consommateurs fonctionnent rapidement, donc le lag est g\u00e9n\u00e9ralement \u00e9gal \u00e0 0, mais il peut parfois y avoir un pic temporaire. Kafka g\u00e8re cela de mani\u00e8re native, mais il faut d\u00e9finir un certain intervalle. <\/p>\n<p>Il y a un projet <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/linkedin\/Burrow\">Burrow<\/a><\/noindex>, qui vous donnera plus d'informations sur Kafka. Il renvoie simplement via l'API le statut de ce groupe de consommateurs, comment ce groupe s'en sort. En plus de OK et Failed, il y a des avertissements, et vous pourrez savoir que vos consommateurs n'arrivent pas \u00e0 suivre le rythme de production \u2014 ils ne parviennent pas \u00e0 lire ce qui est \u00e9crit. Le syst\u00e8me est assez intelligent, il est facile \u00e0 utiliser. <\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/cece8495801e187b155487a802e1a35a.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nVoici \u00e0 quoi ressemble la r\u00e9ponse de l'API. Ici, le groupe bob-live-fifa, la partition refund.update.v1, statut OK, lag 0 \u2014 le dernier offset final est tel quel.<\/p>\n<p><img decoding=\"async\" alt=\"Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec une API asynchrone sur Kafka\" src=\"\/wp-content\/uploads\/2019\/04\/1538b2ccc390e9b83075f54566f1039b.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nSurveillance <strong>updated_at SLA (stuck)<\/strong> J'ai d\u00e9j\u00e0 mentionn\u00e9. Par exemple, un produit est pass\u00e9 au statut indiquant qu'il est pr\u00eat pour le retour. Nous mettons en place un Cron qui dit que si cet objet n'est pas pass\u00e9 en remboursement dans les 5 minutes (nous remboursons tr\u00e8s rapidement via les syst\u00e8mes de paiement), alors quelque chose ne va clairement pas et c'est un cas pour le support. Nous prenons donc simplement un Cron qui lit ces \u00e9l\u00e9ments, et s'ils sont sup\u00e9rieurs \u00e0 0, il envoie une alerte.<\/p>\n<p><b>En r\u00e9sum\u00e9, il est pratique d'utiliser des \u00e9v\u00e9nements lorsque<\/b>:<\/p>\n<ul>\n<li>l'information est n\u00e9cessaire \u00e0 plusieurs syst\u00e8mes ;<\/li>\n<li>le r\u00e9sultat du traitement n'est pas important ;<\/li>\n<li>il y a peu d'\u00e9v\u00e9nements ou les \u00e9v\u00e9nements sont petits. <\/li>\n<\/ul>\n<blockquote><p>\u00c0 premi\u00e8re vue, l'article semble avoir un sujet assez concret - API asynchrone sur Kafka, mais en rapport avec cela, j'ai beaucoup de recommandations \u00e0 faire.<br \/>\nTout d'abord, le suivant <noindex><a rel=\"nofollow\" href=\"https:\/\/www.highload.ru\/\">HighLoad++<\/a><\/noindex> ne n\u00e9cessite pas d'attendre jusqu'\u00e0 novembre, sa version \u00e0 Saint-P\u00e9tersbourg sera d\u00e9j\u00e0 en avril, et en juin, nous parlerons des charges \u00e9lev\u00e9es \u00e0 Novossibirsk.<br \/>\nDeuxi\u00e8mement, l'auteur de la pr\u00e9sentation, Sergey Zaika, fait partie du comit\u00e9 de notre nouvelle conf\u00e9rence sur la gestion des connaissances. <noindex><a rel=\"nofollow\" href=\"https:\/\/knowledgeconf.ru\/2019\">KnowledgeConf<\/a><\/noindex>La conf\u00e9rence est d'un jour, se d\u00e9roulera le 26 avril, mais son programme est tr\u00e8s riche.<br \/>\nEt aussi en mai, il y aura <noindex><a rel=\"nofollow\" href=\"https:\/\/phprussia.ru\/2019\">PHP Russia<\/a><\/noindex> et\u00a0<noindex><a rel=\"nofollow\" href=\"https:\/\/ritfest.ru\/2019\">RIT++<\/a><\/noindex> (avec DevOpsConf en tant que partie) - vous pouvez encore proposer votre propre sujet, partager votre exp\u00e9rience et parler de vos erreurs.<\/p><\/blockquote>\n<p>Source : <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.2 - 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\/fr\/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.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"fr_FR\" \/>\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\/fr\/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\udd47 Exp\u00e9rience de d\u00e9veloppement du service Refund Tool avec API asynchrone sur Kafka | ProHoster","description":"Qu'est-ce qui pourrait pousser une si grande entreprise comme Lamoda, avec un processus bien \u00e9tabli et des dizaines de services interconnect\u00e9s, \u00e0 modifier substantiellement son approche ?","canonical_url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/opyt-razrabotki-servisa-refund-tool-s-asinhronnym-api-na-kafka","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"fr_FR","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\/fr\/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\/fr\/wp-json\/wp\/v2\/posts\/30778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/comments?post=30778"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/30778\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media\/22763"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media?parent=30778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/categories?post=30778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/tags?post=30778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}