"Je marche à ma place" - attendez, sont-ils marqués ?

Depuis 2019, la Russie dispose d’une loi sur l’étiquetage obligatoire. La loi ne s'applique pas à tous les groupes de produits et les dates d'entrée en vigueur de l'étiquetage obligatoire pour les groupes de produits sont différentes. Le tabac, les chaussures et les médicaments seront les premiers à être soumis à un étiquetage obligatoire ; d'autres produits viendront s'y ajouter ultérieurement, par exemple les parfums, les textiles et le lait. Cette innovation législative a donné lieu au développement de nouvelles solutions informatiques qui permettront de suivre toute la chaîne de vie d'un produit, de la production à l'achat par le consommateur final, jusqu'à tous les acteurs du processus : tant l'État lui-même que toutes les organisations vendant des biens avec étiquetage obligatoire.

Dans X5, le système qui suivra les marchandises étiquetées et échangera des données avec l’État et les fournisseurs s’appelle « Marcus ». Voyons dans l'ordre comment et qui l'a développé, quelle est sa pile technologique et pourquoi nous avons de quoi être fiers.

"Je marche à ma place" - attendez, sont-ils marqués ?

Charge élevée réelle

"Marcus" résout de nombreux problèmes, le principal étant l'interaction d'intégration entre les systèmes d'information X5 et le système d'information de l'État pour les produits labellisés (GIS MP) pour suivre le mouvement des produits labellisés. La plate-forme stocke également tous les codes d'étiquetage que nous recevons et l'historique complet du mouvement de ces codes à travers les objets, et aide à éliminer le reclassement des produits étiquetés. En prenant l'exemple des produits du tabac, qui faisaient partie des premiers groupes de produits étiquetés, un seul camion de cigarettes contient environ 600 000 paquets, chacun possédant son propre code unique. Et la tâche de notre système est de suivre et de vérifier la légalité des mouvements de chacun de ces packs entre les entrepôts et les magasins, et finalement de vérifier l'admissibilité de leur vente à l'acheteur final. Et nous enregistrons environ 125 000 transactions en espèces par heure, et nous devons également enregistrer comment chacun de ces paquets est arrivé dans le magasin. Ainsi, en prenant en compte tous les mouvements entre objets, on s’attend à des dizaines de milliards d’enregistrements par an.

Équipe M

Bien que Marcus soit considéré comme un projet au sein de X5, il est mis en œuvre selon une approche produit. L'équipe travaille selon Scrum. Le projet a démarré l'été dernier, mais les premiers résultats ne sont arrivés qu'en octobre : notre propre équipe était entièrement constituée, l'architecture du système a été développée et l'équipement a été acheté. L'équipe compte désormais 16 personnes, dont six sont impliquées dans le développement backend et frontend, dont trois sont impliquées dans l'analyse du système. Six autres personnes sont impliquées dans les tests manuels, de charge, automatisés et la maintenance des produits. De plus, nous disposons d'un spécialiste SRE.

Non seulement les développeurs écrivent du code dans notre équipe ; presque tous les gars savent programmer et écrire des autotests, des scripts de chargement et des scripts d'automatisation. Nous y prêtons une attention particulière, car même le support produit nécessite un haut niveau d’automatisation. Nous essayons toujours de conseiller et d'aider les collègues qui n'ont jamais programmé auparavant, et de leur confier quelques petites tâches à réaliser.

En raison de la pandémie de coronavirus, nous avons transféré toute l'équipe au travail à distance ; la disponibilité de tous les outils de gestion du développement, le workflow intégré dans Jira et GitLab ont permis de passer facilement cette étape. Les mois passés à distance ont montré que la productivité de l’équipe n’en a pas souffert ; pour beaucoup, le confort de travail a augmenté, la seule chose qui manquait était la communication en direct.

Réunion d'équipe à distance

"Je marche à ma place" - attendez, sont-ils marqués ?

Réunions pendant le travail à distance

"Je marche à ma place" - attendez, sont-ils marqués ?

Pile technologique de la solution

Le référentiel standard et l'outil CI/CD pour X5 est GitLab. Nous l'utilisons pour le stockage de code, les tests continus et le déploiement sur des serveurs de test et de production. Nous utilisons également la pratique de la révision du code, lorsqu'au moins 2 collègues doivent approuver les modifications apportées au code par le développeur. Les analyseurs de code statique SonarQube et JaCoCo nous aident à garder notre code propre et à garantir le niveau requis de couverture de tests unitaires. Toutes les modifications apportées au code doivent passer par ces contrôles. Tous les scripts de test exécutés manuellement sont ensuite automatisés.

Pour la mise en œuvre réussie des processus commerciaux par « Marcus », nous avons dû résoudre un certain nombre de problèmes technologiques, chacun dans l'ordre.

Tâche 1. La nécessité d'une évolutivité horizontale du système

Pour résoudre ce problème, nous avons choisi une approche d’architecture microservice. Dans le même temps, il est très important de comprendre les domaines de responsabilité des services. Nous avons essayé de les diviser en opérations commerciales, en tenant compte des spécificités des processus. Par exemple, l'acceptation dans un entrepôt n'est pas une opération très fréquente, mais à très grande échelle, au cours de laquelle il est nécessaire d'obtenir rapidement du régulateur de l'État des informations sur les unités de marchandises acceptées, dont le nombre dans une livraison atteint 600000 XNUMX. , vérifiez l'admissibilité de l'acceptation de ce produit dans l'entrepôt et renvoyez toutes les informations nécessaires au système d'automatisation de l'entrepôt. Mais l'expédition depuis les entrepôts a une intensité beaucoup plus grande, mais fonctionne en même temps avec de petits volumes de données.

Nous mettons en œuvre tous les services sans état et essayons même de diviser les opérations internes en étapes, en utilisant ce que nous appelons les auto-sujets Kafka. C'est à ce moment-là qu'un microservice s'envoie un message, ce qui vous permet d'équilibrer la charge sur des opérations plus gourmandes en ressources et simplifie la maintenance du produit, mais nous y reviendrons plus tard.

Nous avons décidé de séparer les modules d'interaction avec les systèmes externes en services distincts. Cela a permis de résoudre le problème des API changeantes fréquemment des systèmes externes, avec pratiquement aucun impact sur les services dotés de fonctionnalités métier.

"Je marche à ma place" - attendez, sont-ils marqués ?

Tous les microservices sont déployés dans un cluster OpenShift, ce qui résout à la fois le problème de mise à l'échelle de chaque microservice et nous permet de ne pas utiliser d'outils tiers de découverte de services.

Tâche 2. La nécessité de maintenir une charge élevée et un échange de données très intensif entre les services de la plateforme : Rien que pendant la phase de lancement du projet, environ 600 opérations par seconde sont effectuées. Nous nous attendons à ce que cette valeur augmente jusqu'à 5000 XNUMX opérations/s à mesure que les points de vente se connectent à notre plateforme.

Ce problème a été résolu en déployant un cluster Kafka et en abandonnant presque complètement l’interaction synchrone entre les microservices de la plateforme. Cela nécessite une analyse très minutieuse des exigences du système, car toutes les opérations ne peuvent pas être asynchrones. Dans le même temps, nous transmettons non seulement les événements via le courtier, mais nous transmettons également toutes les informations commerciales requises dans le message. Ainsi, la taille du message peut atteindre plusieurs centaines de kilo-octets. La limite de taille des messages dans Kafka nous oblige à prédire avec précision la taille des messages et, si nécessaire, à les diviser, mais la division est logique et liée aux opérations commerciales.
Par exemple, nous répartissons les marchandises qui arrivent dans une voiture dans des cartons. Pour les opérations synchrones, des microservices distincts sont alloués et des tests de charge approfondis sont effectués. L'utilisation de Kafka nous a présenté un autre défi : tester le fonctionnement de notre service en tenant compte de l'intégration de Kafka rend tous nos tests unitaires asynchrones. Nous avons résolu ce problème en écrivant nos propres méthodes utilitaires à l'aide d'Embedded Kafka Broker. Cela n'élimine pas la nécessité d'écrire des tests unitaires pour des méthodes individuelles, mais nous préférons tester des cas complexes à l'aide de Kafka.

Une grande attention a été accordée aux journaux de traçage afin que leur TraceId ne soit pas perdu lorsque des exceptions se produisent lors du fonctionnement des services ou lors de l'utilisation du batch Kafka. Et s'il n'y a pas eu de problèmes particuliers avec le premier, alors dans le second cas, nous sommes obligés de consigner tous les TraceIds fournis avec le lot et d'en sélectionner un pour continuer le traçage. Ensuite, lors de la recherche par TraceId d'origine, l'utilisateur saura facilement avec lequel le traçage s'est poursuivi.

Tâche 3. La nécessité de stocker une grande quantité de données : Plus d'un milliard d'étiquettes par an, rien que pour le tabac, arrivent à X1. Ils nécessitent un accès constant et rapide. Au total, le système doit traiter environ 5 milliards d’enregistrements de l’historique des mouvements de ces marchandises étiquetées.

Pour résoudre le troisième problème, la base de données NoSQL MongoDB a été choisie. Nous avons construit un fragment de 5 nœuds et chaque nœud dispose d'un ensemble de répliques de 3 serveurs. Cela vous permet de faire évoluer le système horizontalement, en ajoutant de nouveaux serveurs au cluster et de garantir sa tolérance aux pannes. Ici, nous avons rencontré un autre problème : assurer la transactionnalité dans le cluster mongo, en tenant compte de l'utilisation de microservices évolutifs horizontalement. Par exemple, l'une des tâches de notre système est d'identifier les tentatives de revente de produits portant les mêmes codes d'étiquetage. Ici, des superpositions apparaissent avec des scans erronés ou des opérations erronées des caissiers. Nous avons constaté que de tels doublons peuvent se produire à la fois dans un lot Kafka en cours de traitement et dans deux lots traités en parallèle. Ainsi, la vérification des doublons en interrogeant la base de données n'a rien donné. Pour chaque microservice, nous avons résolu le problème séparément en fonction de la logique métier de ce service. Par exemple, pour les chèques, nous avons ajouté un chèque à l'intérieur du lot et un traitement séparé pour l'apparition de doublons lors de l'insertion.

Pour garantir que le travail des utilisateurs avec l'historique des opérations n'affecte en rien la chose la plus importante - le fonctionnement de nos processus métier, nous avons séparé toutes les données historiques dans un service distinct avec une base de données distincte, qui reçoit également des informations via Kafka. . De cette façon, les utilisateurs travaillent avec un service isolé sans affecter les services qui traitent les données pour les opérations en cours.

Tâche 4 : Retraitement et surveillance de la file d'attente :

Dans les systèmes distribués, des problèmes et des erreurs surviennent inévitablement en termes de disponibilité des bases de données, des files d'attente et des sources de données externes. Dans le cas de Marcus, la source de ces erreurs est l’intégration avec des systèmes externes. Il était nécessaire de trouver une solution qui permettrait des demandes répétées de réponses erronées avec un délai d'attente spécifié, sans pour autant arrêter le traitement des demandes réussies dans la file d'attente principale. À cette fin, le concept dit de « nouvelle tentative basée sur un sujet » a été choisi. Pour chaque sujet principal, un ou plusieurs sujets de nouvelle tentative sont créés auxquels des messages erronés sont envoyés et en même temps le retard dans le traitement des messages du sujet principal est éliminé. Schéma d'interaction -

"Je marche à ma place" - attendez, sont-ils marqués ?

Pour mettre en œuvre un tel schéma, nous avions besoin des éléments suivants : intégrer cette solution à Spring et éviter la duplication de code. En surfant sur le Web, nous sommes tombés sur une solution similaire basée sur Spring BeanPostProccessor, mais elle nous a semblé inutilement lourde. Notre équipe a créé une solution plus simple qui nous permet de l'intégrer dans le cycle Spring pour créer des consommateurs et d'ajouter en outre des réessais de consommateurs. Nous avons proposé un prototype de notre solution à l'équipe Spring, vous pouvez le voir ici. Le nombre de Retry Consumers et le nombre de tentatives pour chaque consommateur sont configurés via des paramètres, en fonction des besoins du processus métier, et pour que tout fonctionne, il ne reste plus qu'à ajouter l'annotation org.springframework.kafka.annotation.KafkaListener , qui est familier à tous les développeurs Spring.

Si le message n'a pas pu être traité après toutes les tentatives, il est envoyé au DLT (sujet des lettres mortes) à l'aide de Spring DeadLetterPublishingRecoverer. À la demande du support, nous avons étendu cette fonctionnalité et créé un service distinct qui vous permet d'afficher les messages inclus dans DLT, stackTrace, traceId et d'autres informations utiles à leur sujet. De plus, une surveillance et des alertes ont été ajoutées à tous les sujets DLT, et désormais, en fait, l'apparition d'un message dans un sujet DLT est une raison pour analyser et corriger un défaut. C'est très pratique - par le nom du sujet, nous comprenons immédiatement à quelle étape du processus le problème est survenu, ce qui accélère considérablement la recherche de sa cause profonde.

"Je marche à ma place" - attendez, sont-ils marqués ?

Plus récemment, nous avons implémenté une interface qui nous permet de renvoyer des messages via notre support après avoir éliminé leurs causes (par exemple, restaurer la fonctionnalité du système externe) et, bien sûr, avoir établi le défaut correspondant pour analyse. C'est là que nos self-topics sont utiles : afin de ne pas relancer une longue chaîne de traitement, vous pouvez la redémarrer à partir de l'étape souhaitée.

"Je marche à ma place" - attendez, sont-ils marqués ?

Fonctionnement de la plateforme

La plate-forme est déjà en fonctionnement productif, nous effectuons chaque jour des livraisons et des expéditions, connectons de nouveaux centres de distribution et magasins. Dans le cadre du projet pilote, le système fonctionne avec les groupes de produits « Tabac » et « Chaussures ».

Toute notre équipe participe à la réalisation de pilotes, analyse les problèmes émergents et fait des suggestions pour améliorer notre produit, de l'amélioration des journaux à la modification des processus.

Afin de ne pas répéter nos erreurs, tous les cas détectés lors du pilote sont reflétés dans des tests automatisés. La présence d'un grand nombre d'autotests et de tests unitaires vous permet d'effectuer des tests de régression et d'installer un correctif en quelques heures seulement.

Nous continuons désormais à développer et à améliorer notre plateforme et sommes constamment confrontés à de nouveaux défis. Si vous êtes intéressé, nous parlerons de nos solutions dans les articles suivants.

Source: habr.com

Ajouter un commentaire