Utilisation de Golang pour créer des microservices dans The Economist: A Retrospective

Bonjour à tous! Déjà le 28 mai, nous lançons le premier groupe au cours de développeur de Golang . Et aujourd'hui, nous partageons avec vous la première publication consacrée au lancement de ce cours. Allons-y.



Extraits clés

  • The Economist avait besoin de plus de flexibilité pour distribuer du contenu sur une chaîne numérique de plus en plus diversifiée. Pour atteindre cet objectif et maintenir un haut niveau de performances et de fiabilité, la plateforme est passée d'une architecture monolithique à une architecture de microservices.
  • Les outils écrits en Go étaient un élément clé du nouveau système, qui a permis à The Economist de fournir des services évolutifs et performants et de créer rapidement de nouveaux produits.
  • Go, destiné à la prise en charge simultanée et à l'API, ainsi qu'à sa construction d'un langage compilé statique, a facilité le développement de systèmes de traitement d'événements distribués qui pouvaient évoluer. Le support des tests était également un plus.
  • En général, l'expérience de l'équipe The Economist avec Go a été positive, et cela a été l'un des facteurs décisifs qui ont aidé à faire évoluer la plateforme de contenu.
  • Go ne sera pas toujours un outil approprié, et c'est normal. The Economist a une plate-forme polyglotte et utilise différents langages lorsque cela a du sens.

J'ai rejoint l' équipe de développement de The Economist en tant que développeur Drupal. Cependant, ma véritable tâche était de participer à un projet qui changerait fondamentalement la technologie de diffusion de contenu Economist. Les premiers mois, j'ai passé à apprendre le Go, plusieurs mois à travailler avec un consultant externe pour créer un MVP (produit minimum viable), puis j'ai rejoint l'équipe à nouveau pour superviser leur immersion dans le Go.
Ce changement technologique a été déclenché par la mission de The Economist d'élargir son audience numérique, alors que la consommation de nouvelles s'est éloignée de la presse écrite. The Economist avait besoin de plus de flexibilité pour fournir du contenu à des canaux numériques de plus en plus diversifiés. Pour atteindre cet objectif et maintenir un haut niveau de performances et de fiabilité, la plateforme est passée d'une architecture monolithique à une architecture de microservices. Les outils écrits en Go étaient un élément clé du nouveau système, qui a permis à The Economist de fournir des services évolutifs et performants et de créer rapidement de nouveaux produits.

Implémentation de Go dans The Economist:

  • Permis aux ingénieurs de développer et de mettre en œuvre rapidement de nouvelles fonctionnalités.
  • Meilleures pratiques approuvées pour les services à défaillance rapide avec gestion intelligente des erreurs.
  • Prise en charge fiable de la simultanéité et du fonctionnement du réseau dans un système distribué.
  • Il a montré un manque de maturité et de soutien dans certains domaines nécessaires pour le contenu et les médias.
  • Facilitation d'une plate-forme pouvant évoluer pour la publication numérique.

Pourquoi The Economist a-t-il choisi Go?

Pour répondre à cette question, il sera utile de mettre en évidence l'architecture globale de la nouvelle plateforme. La plateforme, appelée Content Platform, est un système de gestion d'événements. Il répond aux événements de différentes plates-formes de création de contenu et lance un flux de processus exécutés dans des microservices fonctionnant séparément. Ces services remplissent des fonctions telles que la standardisation des données, l'analyse des balises sémantiques, l'indexation dans ElasticSearch et l'envoi de contenu vers des plateformes externes telles qu'Apple News ou Facebook. La plate-forme dispose également d'une API RESTful, qui en combinaison avec GraphQL est le principal point d'entrée pour les clients et produits frontaux.

En développant une architecture commune, l'équipe a recherché quelles langues répondraient aux besoins de la plateforme. Go a été comparé à Python, Ruby, Node, PHP et Java. Bien que chaque langue ait ses propres atouts, Go est le mieux adapté à l'architecture de la plate-forme. Go, destiné à la prise en charge simultanée et à l'API, ainsi qu'à sa construction d'un langage compilé statique, a facilité le développement de systèmes de traitement d'événements distribués qui pouvaient évoluer. De plus, la syntaxe relativement simple de Go a permis de s'impliquer facilement dans le développement et de commencer à écrire du code de travail, ce qui a promis des avantages immédiats pour une équipe en pleine transition technologique. En général, il a été déterminé que Go est le langage le plus approprié pour la convivialité et l'efficacité dans un système cloud distribué.

Trois ans plus tard: Go correspondait-il à ces objectifs ambitieux?

Certains éléments de conception de la plateforme étaient bien alignés sur le langage Go. Failing Fast était un élément essentiel du système car il consistait en des services indépendants distribués. Conformément aux principes de l'application à douze facteurs ("application à 12 facteurs"), l'application devait démarrer rapidement et échouer rapidement (échec rapide). La conception de Go en tant que langage compilé statique fournit des temps de démarrage rapides et les performances du compilateur s'améliorent constamment et n'ont jamais été un problème pour la conception ou le déploiement. De plus, la conception de gestion des erreurs de Go a permis aux applications de tomber en panne non seulement plus rapidement, mais aussi plus intelligemment.

Gestion des erreurs

Une caractéristique que les ingénieurs remarquent rapidement dans Go est qu'il s'agit d'un type d'erreur plutôt que d'un système d'exception. Dans Go, toutes les erreurs sont des valeurs. Le type d'erreur est prédéfini et est une interface. Une interface dans Go est essentiellement une collection nommée de méthodes, et tout autre type d'utilisateur peut satisfaire une interface si elle a les mêmes méthodes. Le type d'erreur est une interface qui peut se décrire comme une chaîne.

type error interface { Error() string } 

Cela donne aux ingénieurs plus de contrôle et de fonctionnalités dans la gestion des erreurs. En ajoutant une méthode Error qui renvoie une chaîne dans n'importe quel module utilisateur, vous pouvez créer vos propres erreurs et les générer, par exemple, en utilisant la fonction Nouveau ci-dessous, qui provient du package Errors.

 type errorString struct { s string } func (e *errorString) Error() string { return es } 

Qu'est-ce que cela signifie dans la pratique? Dans Go, les fonctions autorisent plusieurs valeurs de retour, donc si votre fonction ne fonctionne pas, elle renverra très probablement une valeur d'erreur. Le langage vous encourage à vérifier explicitement les erreurs là où elles se produisent (par opposition à lever et intercepter une exception), donc votre code devrait généralement inclure une vérification «si err! = Néant. " Au début, cette gestion fréquente des erreurs peut sembler monotone. Cependant, error en tant que valeur vous permet d'utiliser Error pour simplifier la gestion des erreurs. Par exemple, dans un système distribué, vous pouvez facilement implémenter des tentatives de nouvelle tentative de requêtes en encapsulant des erreurs.

Des problèmes de réseau se produiront toujours dans le système, qu'il s'agisse d'envoyer des données à d'autres services internes ou de les transférer vers des outils tiers. Cet exemple de paquet net montre comment vous pouvez utiliser l'erreur comme type pour distinguer les erreurs réseau temporaires des erreurs permanentes. L'équipe Economist a utilisé un wrapper d'erreur similaire pour créer des tentatives incrémentielles lors de l'envoi de contenu à des API externes.

 package net type Error interface { error Timeout() bool // Is the error a timeout? Temporary() bool // Is the error temporary? } if nerr, ok := err.(net.Error); ok && nerr.Temporary() { time.Sleep(1e9) continue } if err != nil { log.Fatal(err) } 

Les auteurs de Go croient que toutes les exceptions ne sont pas exceptionnelles. Les ingénieurs sont plus susceptibles de récupérer intelligemment des erreurs que de planter l'application. De plus, la gestion des erreurs Go vous permet de mieux contrôler les erreurs, ce qui peut améliorer des aspects tels que le débogage ou la convivialité des erreurs. Au sein de la plateforme de contenu, cette fonctionnalité de conception de Go a permis aux développeurs de prendre des décisions éclairées concernant les erreurs, ce qui a entraîné une augmentation de la fiabilité du système dans son ensemble.

Cohérence des données

La cohérence des données est un facteur critique dans la plateforme de contenu. Chez The Economist, le contenu est le fondement de l'entreprise et l'objectif de la plateforme de contenu est de garantir que le contenu peut être publié une fois et est disponible partout. Par conséquent, il est important que chaque produit et consommateur ait une cohérence des données avec l'API Content Platform. Les produits utilisent principalement GraphQL pour les requêtes API, ce qui nécessite un schéma statique, qui sert de sorte de contrat entre les consommateurs et la plateforme. Le contenu traité par la plateforme doit être conforme à ce schéma. Le langage statique a aidé à l'implémenter et a facilité la cohérence des données.

Tester avec Go

Une autre fonctionnalité qui favorise la cohérence est la suite de tests Go. Le temps de compilation rapide de Go, combiné à des tests de première classe en tant que caractéristique du langage, a permis à l'équipe d'incorporer des méthodes de test efficaces dans les workflows de conception et des défaillances rapides dans les pipelines d'assemblage. Les outils de test de Go facilitent la configuration et l'exécution. L'exécution de «go test» exécutera tous les tests dans le répertoire courant et la commande test a plusieurs indicateurs utiles. L'indicateur de couverture fournit un rapport de couverture de code détaillé. Le test de banc exécute des tests de référence, qui sont indiqués en exécutant le nom de la fonction de test avec le mot «banc» plutôt que «test». La fonction TestMain fournit des méthodes pour une configuration de test supplémentaire, comme un serveur d'authentification factice.

De plus, Go a la possibilité de créer des tests tabulaires avec des structures anonymes et des stubs avec des interfaces, améliorant la couverture des tests. Bien que les tests ne soient pas nouveaux en termes de fonctionnalités linguistiques, Go facilite la création de tests robustes et leur intégration aisée dans les workflows. Dès le début, les ingénieurs de The Economist ont pu exécuter des tests dans le cadre des pipelines d'assemblage sans configuration spéciale, et ont même ajouté Git Hooks pour exécuter des tests avant de pousser le code dans Github.

Cependant, le projet n'a pas été sans effort pour assurer la cohérence des données. Le premier problème majeur pour la plateforme était la gestion de contenu dynamique à partir de backends imprévisibles. La plate-forme consomme le contenu des systèmes CMS d'origine principalement via des points de terminaison JSON, où la structure et les types de données ne sont pas garantis. Cela signifie que la plate-forme ne peut pas utiliser le package Go standard pour interpréter json, qui prend en charge la désérialisation JSON en structures, mais cela déclenche une alarme si les types des champs de données de structure et d'entrée ne correspondent pas.

Pour surmonter ce problème, une méthode spéciale était nécessaire pour mapper la partie serveur au format standard. Après plusieurs itérations de l'approche choisie, l'équipe a introduit son propre processus de désérialisation. Bien que cette approche ressemble un peu à la refonte d'un package de bibliothèque standard, elle a donné aux ingénieurs un contrôle complet sur le traitement des données source.

Support réseau

L'évolutivité était au premier plan de la nouvelle plate-forme, et cela a été fourni par les bibliothèques Go standard pour la mise en réseau et les API. Dans Go, vous pouvez rapidement implémenter des points de terminaison HTTP évolutifs sans avoir besoin de frameworks. Dans l'exemple ci-dessous, le package de bibliothèque standard net / http est utilisé pour configurer un gestionnaire qui accepte un rédacteur de demandes et de réponses. Lorsque l'API Content Platform a été implémentée pour la première fois, elle utilisait le framework API. Elle a finalement été remplacée par une bibliothèque standard, car l'équipe a reconnu qu'elle répond à tous les besoins de leur réseau sans compromis supplémentaires inutiles. Les gestionnaires HTTP Golang évoluent parce que chaque demande au gestionnaire est exécutée en parallèle dans Goroutine, un thread léger sans besoin de personnalisation.

 package main import ( "fmt" "log" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") } func main() { http.HandleFunc("/", handler) log.Fatal(http.ListenAndServe(":8080", nil)) } 

Modèle de simultanéité

Le modèle d'accès simultané Go a fourni de multiples avantages en améliorant les performances sur la plate-forme. Travailler avec des données distribuées implique de s'occuper des garanties promises aux consommateurs. Selon le théorème de CAP, il n'est pas possible de fournir plus de deux des trois garanties suivantes en même temps: Cohérence des données. La disponibilité Résistant à la séparation. Dans la plateforme The Economist, la cohérence a finalement été adoptée, ce qui signifie que la lecture à partir des sources de données sera finalement cohérente, et des retards modérés dans toutes les sources de données atteignant un état cohérent sont acceptables. Une façon de minimiser cet écart est d'utiliser les Goroutines.

Les goroutines sont des threads légers gérés par le runtime Go pour les empêcher de manquer de threads. Les goroutines ont permis d'optimiser les tâches asynchrones sur la plateforme. Par exemple, l'un des référentiels de données de la plate-forme est Elasticsearch. Lorsque le contenu est mis à jour sur le système, le contenu qui fait référence à cet élément dans Elasticsearch est mis à jour et réindexé. Grâce à la mise en œuvre de Goroutines, le temps de traitement a été réduit, ce qui a assuré une cohérence rapide des éléments. Cet exemple montre comment les éléments adaptés au retraitement sont retraités dans Goroutine.

 func reprocess(searchResult *http.Response) (int, error) { responses := make([]response, len(searchResult.Hits)) var wg sync.WaitGroup wg.Add(len(responses)) for i, hit := range searchResult.Hits { wg.Add(1) go func(i int, item elastic.SearchHit) { defer wg.Done() code, err := reprocessItem(item) responses[i].code = code responses[i].err = err }(i, *hit) } wg.Wait return http.StatusOK, nil } 

La conception de systèmes ne se limite pas à la programmation. Les ingénieurs doivent comprendre quels outils, où et quand, sont appropriés. Alors que Go était un outil puissant pour la plupart des besoins de la plateforme de contenu de The Economist, certaines limitations nécessitaient d'autres solutions.

Gestion des dépendances

Lorsque Go vient de sortir, il ne disposait pas d'un système de gestion des dépendances. Au sein de la communauté, plusieurs outils ont été développés pour répondre à ce besoin. The Economist a utilisé les sous-modules Git, ce qui était logique à un moment où la communauté faisait activement la promotion d'un outil standard de gestion des dépendances. Aujourd'hui, bien que la communauté soit déjà beaucoup plus proche d'une approche cohérente de la gestion de la dépendance, elle n'est pas là. L'approche The Economist utilisant des sous-modules n'a pas posé de problèmes sérieux, mais elle a été difficile pour les autres développeurs de Go, et cela doit être pris en compte lors du passage à Go.

Il y avait également des exigences de plate-forme pour lesquelles les fonctionnalités ou la conception de Go n'étaient pas la meilleure solution. Étant donné que la plate-forme a ajouté la prise en charge du traitement audio, les outils de Go pour l'extraction des métadonnées étaient limités à l'époque, et l'équipe a donc choisi Exiftool Python à la place. Les services de plateforme fonctionnent dans des conteneurs Docker, ce qui a permis d'installer Exiftool et de le lancer à partir de l'application Go.

 func runExif(args []string) ([]byte, error) { cmdOut, err := exec.Command("exiftool", args...).Output() if err != nil { return nil, err } return cmdOut, nil } 

Un autre scénario courant pour la plate-forme est la réception de code HTML non fonctionnel à partir des systèmes CMS source, l'analyse du code HTML pour l'exactitude et l'assainissement du code HTML. Initialement, Go était utilisé pour ce processus, mais comme la bibliothèque HTML standard de Go nécessite un code HTML correct, il a fallu une grande quantité de code personnalisé pour analyser le HTML avant le traitement. Ce code est rapidement devenu fragile et a manqué des cas limites, donc une nouvelle solution a été implémentée en Javascript. Javascript a fourni une grande flexibilité et adaptabilité pour contrôler le processus de vérification et de désinfection du HTML.

Javascript a également été un choix courant pour filtrer et router les événements dans la plate-forme. Les événements sont filtrés à l'aide d'AWS Lambdas, qui sont des fonctions légères qui ne s'exécutent que lorsqu'elles sont appelées. Un cas d'utilisation est le filtrage des événements sur différentes bandes, telles que rapides et lentes. Ce filtrage est basé sur un seul champ de métadonnées dans l'objet JSON du shell du gestionnaire d'événements. L'implémentation du filtrage a utilisé un package de pointeur Javascript JSON pour capturer un élément dans un objet JSON. Cette approche était beaucoup plus efficace que le démontage complet du JSON dont Go aurait besoin. Bien que des fonctionnalités de ce type puissent être obtenues avec Go, l'utilisation de Javascript était plus facile pour les ingénieurs et fournissait des lambdas plus simples.

Rétrospective Go

Après avoir implémenté la plate-forme de contact et l'avoir soutenue dans la production, si je devais mener une rétrospective de Go et de la plate-forme de contenu, mes commentaires seraient les suivants:

Qu'est-ce qui est déjà bon?

  • Éléments de conception du langage clé pour les systèmes distribués.
  • Un modèle de simultanéité relativement facile à implémenter.
  • Belle communauté de codage et de plaisir.

Que peut-on améliorer?

  • Poursuite des progrès dans les normes de contrôle de version et de vente.
  • Pas assez de maturité dans certaines régions.
  • Détails pour des cas d'utilisateurs spécifiques.

En général, ce fut une expérience positive, et Go est l'un des éléments les plus importants qui a permis de faire évoluer la plateforme de contenu. Go ne sera pas toujours un outil approprié, et c'est normal. The Economist a une plate-forme polyglotte et utilise différents langages lorsque cela a du sens. Go ne sera probablement jamais le meilleur choix lorsque vous avez besoin de jouer avec des objets texte et du contenu dynamique, donc Javascript est toujours dans la boîte à outils. Cependant, les points forts de Go sont le fondement qui permet au système de s'adapter et de croître.
Pour déterminer si Go vous convient, tenez compte des principaux problèmes de conception du système:

  • Quelles sont les tâches de votre système?
  • Quelles garanties offrez-vous à vos consommateurs?
  • Quelle architecture et quels modèles conviennent à votre système?
  • Comment votre système doit-il évoluer?

Si vous développez un système qui relève les défis des données distribuées, des flux de travail asynchrones et des performances et une évolutivité élevées, je vous recommande de considérer Go et ses capacités pour accélérer les objectifs de votre système.

Amis, nous attendons vos commentaires et invitons tout le monde au webinaire ouvert , qui sera organisé le 16 par le développeur senior de Yandex et, en combinaison, notre professeur est Dmitry Smal .

Source: https://habr.com/ru/post/fr451760/


All Articles