Etienne Broutin, architecte logiciel chez Meetic, nous a fait un retour d’expérience après deux ans de refonte en micro-services.
En 2013, toute l’application était dans le même dépôt de code. C’était un monolithe inmaintenable, intestable, avec beaucoup de doublons. De plus, il était impossible de surveiller correctement la plateforme.
La véritable difficulté à cette époque était que pour une même fonctionnalité, du code était dupliqué un peu partout et il y avait énormément de conflits de merge des développeurs.
En 2015, Etienne était intervenu au Symfony live pour présenter le début de la refonte et l’état actuel de la plateforme : 400 serveurs, 900 configurations, 4000 tables en base de données. Deux ans plus tard, le nombre de serveurs a été divisé par 4 (mais je n’ai pu poser la question sur l’état de la base de données malgré plusieurs tentatives de prise de micro)
Le but de cette refonte était de passer en SOA (Service Oriented Architecture).
Le principe reste simple : une WebApp Angular accède à une API publique pour gérer la session, la sécurité, la mise en contexte utilisateur qui va se baser sur une API privée pour gérer les utilisateurs.
Tout ce qui est complexe est dans la couche API privée. Certaines fonctionnalités y sont encore développées et maintenables.
Aujourd’hui, les tests automatisés prennent 40 minutes à renvoyer le résultat final, ce qui est appréciable pour les MEP.
Quels avantages et inconvénients Meetic a rencontré en adoptant l’architecture micro-service ?
Avantages :
- phase de design de code plus simple
- impact de refactoring plus simple
- déploiement rapide
- feedback rapide
- changement de technologie de base de données aisé
- système de cache efficace
Inconvénients :
- validation (lors de la soumission)
- serialization, deserialization
- utilisation CPU du réseau
- complexité croissante au fur et à mesure de la création des micro-service
L’avantage également était que le backoffice allait également appeler ces micro-services.
Afin de faire communiquer efficacement ces micro-services entre eux, Meetic fonctionne en asynchrone : bus RabbitMQ, consumers des messages des différentes queues (pour l’envoi d’un mail, par exemple, l’optin).
Deux ans plus tard, où en est Meetic avec sa refonte ?
La complexité a été divisée en plusieurs micro-services. Le découpage a été fonctionnel plutôt que technique, autour d’un concept (l’upload photo, par exemple).
Certaines fonctionnalités sont payantes, d’autres gratuites. Le droit d’accès est géré par un micro-service.
Pour Meetic, un micro service doit être compréhensible en 1 ou 2 jours par un développeur. Cela comprend le code, les règles métier, l’enregistrement en base de données.
A ce jour, Meetic dispose de 25 micro-services, 9 routes en moyenne par micro-service, 4 tables en moyenne en base de données par micro-service. Les micro-services suivent une logique REST (GET, POST, PUT, DELETE), mais d’autres actions ne collent pas au métier.
Isolation des données
Meetic a décidé de pratiquer l’isolation des données. Une donnée est privée pour un micro-service. Il ne peut appeler la base de données et doit appeler la couche HTTP pour récupérer les informations. Un micro-service peut cependant écrire en base.
Grâce à cette pratique, il leur est possible de savoir quels micro-services sont les plus sollicités par un autre. Le défaut de cette pratique est qu’un micro-service pouvait en appeler un autre qui en appelaient d’autres et ainsi de suite. C’était trop long et difficile à maintenir.
Il a été décidé par l’équipe qu’un micro-service pouvait seulement en appeler un autre. A ce jour, 2/3 des micro-services de Meetic ne dépendent pas d’un autre micro-service. Leur but : un micro-service doit être isolé, indépendant.
La clef de cette isolation des données fut de réussir à isoler le périmètre d’un micro-service. Dans celui de la connexion, par exemple, on valide l’email, le mot de passe, mais on ne vérifie pas que le compte est actif ni que l’on peut se connecter.
Meetic voulait un micro-service pour la photo, un pour l’annonce, un pour la modération. Il a fallu créer deux micro-services :
- un gérant la photo et la modération
- un autre gérant l’annonce et la modération
Traitement des appels entre micro-services
Trois méthodes, avec leurs inconvénients et leurs avantages:
- Pour l’upload photo, par exemple, la couche d’exposition appelle le micro-service des droits qui appelle celui des photos. Les règles métiers sont cachées (les clients n’y ont pas accès) et peuvent être modifiées. Mais le micro-service étant couplé à un autre, il est plus difficilement réutilisable et le temps de réponse est long.
- Il est possible de pousser la donnée par le client. Par exemple, dans certains cas sur Meetic, l’envoi du premier message est gratuit. L’utilisateur appelle la couche d’exposition pour envoyer un message. La couche d’exposition va chercher à savoir si c’est le premier message ou pas. Puis elle va appeler le micro-service des droits pour savoir si le client peut entamer une nouvelle conversation. L’information du statut de conversation est donc poussée par le client. C’est bien car Meetic avait besoin d’informations sur la couche d’exposition pour pouvoir faire d’autres traitements. Faire pousser l’information par le client permet d’éviter de faire deux fois l’appel au micro-service « message ». L’inconvénient, c’est que tous les clients doivent pousser plusieurs informations à chaque fois. C’est plus difficile à utiliser, et il sera plus dur de faire évoluer la logique métier.
- Enfin, s’il y a des problèmes de performance, Meetic pratique la recopie de données. Quand ils consument la liste des mails reçus, par exemple, ils excluent les personnes blacklistées. Si un client met quelqu’un en indésirable, un message est envoyé sur le bus d’événement. Cette information va alors être transmise au micro-service « message » qui va aller flaguer les messages en base pour qu’ils soient masqués. Quand la personne va vouloir afficher ses messages, il va y avoir un recalcul des messages visibles. Ce a un coût en terme de performance, surtout s’il y a de la pagination. La donnée est donc répliquée pour ne pas impacter la performance et favoriser la réutilisabilité du micro-service.
L’inconvénient est que c’est un pattern long à implémenter.
Une autre astuce pour la performance : passer plusieurs identifiants dans la route (séparés par des virgules, par exemple) pour récupérer plusieurs photos, Ou alors, plusieurs appels sont fait et les promesses de Guzzle sont utilisées pour paralléliser les appels.
Ce qui a changé pour Meetic
Le déploiement est à présent continu : 15 MEP par jour. Les équipes sont réduites.
Cependant, Meetic a choisi de ne pas avoir de versionning pour leurs API des micro-services. En effet, ils gèrent 15 projets différents/jour/semaine, par 40 personnes, ce qui se révèle être ingérable si l’on cherche à analyser les dépendances entre micro-services. Ils n’ont donc pas de synchronisation forte entre les équipes. Chacune peut évoluer indépendamment des autres. Ils se sont permis de faire ça car les micro-services sont utilisés seulement en interne. Ils ont la main sur les clients, peuvent adapter les micro-services, les contrôler. Cela permet d’avoir beaucoup moins de maintenance à faire, de cas de figure à traiter.
Chaque changement d’un micro-service doit être rétro compatible avec son interface. Des paramètres optionnels peuvent être rajoutés dans les requêtes, il est possible d’ajouter des éléments à une réponse, mais on ne peut en supprimer, ni modifier le type d’une valeur de la réponse.
Quand il y avait deux-trois micro-services, les développeurs pouvaient les mettre à jour à la main. Cependant, c’est devenu rapidement pénible. Tous les micro-services sont donc passés sur des images docker afin de tous les mettre à jour aisément pour travailler..
La question du micro-service en a apporté d’autres : n’importe quel framework possible par micro-service? N’importe quelle SGBD ? N’importe quel langage ? Le choix de la standardisation a primé pour pouvoir passer d’un micro-service à un autre facilement: REST + JSON, conventions de nommage communes, la gestion de la sécurité commune, recettes de builds et de déploiement identiques, etc.
Pour chaque micro-service, les développeurs ont dû mettre en place la securité, les logs, l’interface HTTP, la base de données, le cache component, le traitement des erreurs, le formatage. Au bout du 3ème micro-service, un template a été créé pour pouvoir s’appliquer à tous les micro-services qui allaient suivre.
Ce fonctionnement en micro-service a été un progrès significatif sur la communication dans l’entreprise car cela met en place un vocabulaire commun : on expose des ressources par des micro-services. On parle de notions métier. La gestion de projet s’en est trouvée simplifiée. Ca a permis de découper un gros projet en différentes tâches, sur différents micro-services. Il est alors facile de savoir où on en est pour chaque micro-service.
Pour chaque micro-service, un inventaire peut être fait : ce qu’il contient, les tables sur lesquelles il est propriétaire. Il y a également une documentation générée pour pouvoir échanger la documentation entre développeurs.
Des logs d’accès indexés dans ElasticSearch ont été mis en place pour savoir qui appelle quoi, quelle route. C’est aussi très utile pour les tests de non régression, pour savoir où les tests doivent être accentués (si certains micro-services sont davantage appelés que d’autres)
Au final, plus d’initiatives ont pu être prises, la refactorisation s’en est trouvée plus aisée, il y a eu davantage d’upgrades, moins de monitoring, plus de petits changements à portée de main. Les développeurs ont développé une fierté des micro-services sur lesquels ils travaillent, avec le désir qu’ils soient performants, aient un très bon taux de réponse. Ils s’approprient les micro-services au fur et à mesure.
En terme de production
Quand Meetic est passé en micro-services, ils savaient que le réseau allait être beaucoup plus utilisé, que le besoin en serveurs allait également être plus important (overhead), que le temps de configuration allait être important.
A ce jour, un milliard d’appels de ces micro-services est fait par jour. Ils répondent en moyenne en 25ms. Meetic gère à présent 100 serveurs. A 50% du temps, c’est la couche d’exposition qui appelle ces micro-services, les utilisateurs directement. A 25 % du temps, d’autres micro-services les appellent. Les derniers 25 % du temps, c’est en réaction à certains événements qui se sont produits.
Pas de problème réseau, tous les micro-services sont déployés sur tous les serveurs.
Meetic avait une petite inquiétude sur l’overhead. Ils ont observé un surcoût de 25% sur les appels HTTP (Symfony prenant déjà 8ms avant de traiter une requête). C’est quelque chose qu’ils ont accepté car cela leur a apporté beaucoup plus de choses. Pour compenser les pertes en terme de performance, ils ont parallélisé les appels.
Toutes les erreurs sont indexées dans elastic search et remontées sur kibana. Il devient alors facile de localiser les erreurs, d’analyser les performances.
Meetic a aussi un dashboard automatisé qui monitore sur toutes leurs routes un temps de réponse moyen et un taux d’erreurs. Quand ils savent qu’ils vont faire appel à une fonction, en développant une nouvelle fonctionnalité, ils peuvent anticiper le coût que cela va avoir, en combien de temps le micro-service va répondre.
Une route a été mise en place sur chaque micro-service pour l’alerting. Elle teste toutes les dépendances du micro-service (connexion à une base, à un autre micro-service, etc.), et remonte l’information dans l’alerting.
Chaque micro-service est surveillé sur chaque serveur. S’il y a un souci de configuration, de réseau, c’est détecté tout de suite.
La tolérance à la panne est un point qui devient de plus en plus important dans un cadre de micro-services. Pour y pallier, Meetic analyse les logs internet.
La rentabilité doit être supérieure à 97%. Objectif à atteindre: 99,999%.
Extérieurement, pour ceux qui appellent ces micro-services, un fallback behavior a été implémenté. Si, par exemple, le statut de connexion d’une personne ne peut être récupéré, on la considère alors hors-ligne et la suite du traitement est effectuée.
En conclusion
L’un des gains important fut la réutilisabilité des micro-services. Quand Meetic a racheté scout24 en 2015 (leader en Allemagne sur la rencontre), les bases ont été fusionnées, et certains de leurs micro-services ont été repris, ce qui a été un gain de temps, Cela n’aurait pas été possible avant avec une application monolitique.
Les micro-services de Meetic peuvent aussi être utilisés pour Meetic Affinity pour les fonctionnalités communes (en réimplémentant juste la connexion à la base).
Passer en micro-services a été aussi une belle opportunité pour la scalabilité. La meilleure technologie de stockage peut alors être choisie pour chaque micro-service.
Ce choix d’architecture en micro-services est, pour Meetic, un bon choix.
Utiliser Symfony leur a permis de standardiser les micro-services, de faciliter la relecture des autres micro-services et de faciliter le passage des développeurs de l’un à l’autre.
Cela a permis de tenir jusqu’à 40 développeurs backend.
Le cas d’une refonte importante s’est avéré être plus simple, effectué dans un temps réduit.
Découvrez les derniers articles d'alter way
- Big Data & AI Paris 2024 : L'IA au cœur de toutes les transformations.
- Un retour vers l'open-source ? Vos outils DevOps préférés et leurs equivalents open-source.
- Prowler : L'outil de sécurité multi-cloud indispensable pour renforcer votre infrastructure
- Kubernetes : plateforme "star" de l'IT et levier d'innovation des entreprises
- AI_dev2024
- : DirectPV : Avoir du stockage bloc distribué facilement dans kubernetes