Mit Golang Microservices in The Economist erstellen: Eine Retrospektive

Hallo allerseits! Bereits am 28. Mai starten wir die erste Gruppe beim Golang Developer Kurs. Und heute teilen wir Ihnen die erste Veröffentlichung mit, die dem Start dieses Kurses gewidmet ist. Lass uns gehen.



Schlüsselauszüge

  • Der Economist brauchte mehr Flexibilität, um Inhalte auf einen immer vielfältiger werdenden digitalen Kanal zu verteilen. Um dieses Ziel zu erreichen und ein hohes Maß an Leistung und Zuverlässigkeit aufrechtzuerhalten, hat sich die Plattform von einer monolithischen zu einer Microservice-Architektur entwickelt.
  • In Go geschriebene Tools waren eine Schlüsselkomponente des neuen Systems, mit dem The Economist skalierbare, leistungsstarke Services bereitstellen und schnell neue Produkte erstellen konnte.
  • Go, das auf Parallelität und API-Unterstützung abzielte, sowie die Erstellung einer statischen kompilierten Sprache ermöglichten die Entwicklung verteilter Ereignisverarbeitungssysteme, die skaliert werden konnten. Testunterstützung war auch ein Plus.
  • Im Allgemeinen war die Teamerfahrung von The Economist mit Go positiv, und dies war einer der entscheidenden Faktoren für die Skalierung der Content-Plattform.
  • Go wird nicht immer ein geeignetes Werkzeug sein, und das ist normal. Der Economist hat eine polyglotte Plattform und verwendet verschiedene Sprachen, wo es Sinn macht.

Ich bin als Drupal-Entwickler zum Entwicklungsteam von The Economist gekommen . Meine eigentliche Aufgabe bestand jedoch darin, an einem Projekt teilzunehmen, das die Technologie zur Bereitstellung von Inhalten des Ökonomen grundlegend verändern würde. In den ersten Monaten habe ich Go studiert, mehrere Monate mit einem externen Berater zusammengearbeitet, um ein MVP (Minimum Viable Product) zu erstellen, und bin dann erneut dem Team beigetreten, um deren Eintauchen in Go zu überwachen.
Dieser technologische Wandel wurde durch die Mission von The Economist ausgelöst, das digitale Publikum zu erweitern, da sich der Nachrichtenverbrauch von den Printmedien abwandte. Der Economist brauchte mehr Flexibilität, um Inhalte für immer vielfältigere digitale Kanäle bereitzustellen. Um dieses Ziel zu erreichen und ein hohes Maß an Leistung und Zuverlässigkeit aufrechtzuerhalten, hat sich die Plattform von einer monolithischen zu einer Microservice-Architektur entwickelt. In Go geschriebene Tools waren eine Schlüsselkomponente des neuen Systems, mit dem The Economist skalierbare, leistungsstarke Services bereitstellen und schnell neue Produkte erstellen konnte.

Implementierung von Go in The Economist:

  • Ermöglicht Ingenieuren die schnelle Entwicklung und Implementierung neuer Funktionen.
  • Zugelassene Best Practices für schnell ausfallende Services mit intelligenter Fehlerbehandlung.
  • Zuverlässige Unterstützung für Parallelität und Netzwerkbetrieb in einem verteilten System.
  • Es zeigte einen Mangel an Reife und Unterstützung in einigen Bereichen, die für Inhalte und Medien benötigt werden.
  • Ermöglicht eine Plattform, die für das digitale Publizieren skaliert werden kann.

Warum hat sich The Economist für Go entschieden?

Um diese Frage zu beantworten, ist es hilfreich, die Gesamtarchitektur der neuen Plattform hervorzuheben. Die als Content Platform bezeichnete Plattform ist ein Ereignisbehandlungssystem. Es reagiert auf Ereignisse von verschiedenen Plattformen zur Erstellung von Inhalten und startet eine Reihe von Prozessen, die in separat funktionierenden Microservices ausgeführt werden. Diese Dienste führen Funktionen wie das Standardisieren von Daten, das Analysieren semantischer Tags, das Indizieren in ElasticSearch und das Senden von Inhalten an externe Plattformen wie Apple News oder Facebook aus. Die Plattform verfügt außerdem über eine RESTful-API, die in Kombination mit GraphQL der wichtigste Einstiegspunkt für Front-End-Clients und -Produkte ist.

Bei der Entwicklung einer gemeinsamen Architektur untersuchte das Team, welche Sprachen für die Plattform geeignet sind. Go wurde mit Python, Ruby, Node, PHP und Java verglichen. Während jede Sprache ihre eigenen Stärken hat, eignet sich Go am besten für die Plattformarchitektur. Go, das auf Parallelität und API-Unterstützung abzielte, sowie die Erstellung einer statischen kompilierten Sprache ermöglichten die Entwicklung verteilter Ereignisverarbeitungssysteme, die skaliert werden konnten. Darüber hinaus machte es die relativ einfache Syntax von Go einfach, sich an der Entwicklung zu beteiligen und mit dem Schreiben von Arbeitscode zu beginnen, was einem Team, das sich in einem so großen technologischen Wandel befindet, unmittelbare Vorteile versprach. Im Allgemeinen wurde festgestellt, dass Go die Sprache ist, die für Benutzerfreundlichkeit und Effizienz in einem verteilten Cloud-System am besten geeignet ist.

Drei Jahre später: Passte Go zu diesen ehrgeizigen Zielen?

Einige Plattformdesignelemente waren gut auf die Go-Sprache abgestimmt. Das schnelle Scheitern war ein kritischer Teil des Systems, da es aus verteilten unabhängigen Diensten bestand. In Übereinstimmung mit den Prinzipien der Zwölf-Faktoren-App ("12-Faktor-Anwendung") musste die Anwendung schnell ausgeführt werden und schnell fehlschlagen (schnell fehlschlagen). Das Design von Go als statische, kompilierte Sprache bietet schnelle Startzeiten, und die Compilerleistung wird ständig verbessert und war nie ein Problem für Design oder Bereitstellung. Darüber hinaus ermöglichte das Fehlerbehandlungsdesign von Go, dass Anwendungen nicht nur schneller, sondern auch intelligenter ausfielen.

Fehlerbehandlung

Eine Funktion, die Ingenieure in Go schnell bemerken, ist, dass es sich eher um einen Fehler vom Typ Fehler als um ein Ausnahmesystem handelt. In Go sind alle Fehler Werte. Der Fehlertyp ist vordefiniert und eine Schnittstelle. Eine Schnittstelle in Go ist im Wesentlichen eine benannte Sammlung von Methoden, und jeder andere Benutzertyp kann eine Schnittstelle erfüllen, wenn sie über dieselben Methoden verfügt. Der Fehlertyp ist eine Schnittstelle, die sich selbst als Zeichenfolge beschreiben kann.

type error interface { Error() string } 

Dies gibt den Ingenieuren mehr Kontrolle und Funktionalität bei der Fehlerbehandlung. Durch Hinzufügen einer Fehlermethode, die eine Zeichenfolge in einem beliebigen Benutzermodul zurückgibt, können Sie eigene Fehler erstellen und diese generieren, z. B. mithilfe der folgenden neuen Funktion, die aus dem Fehlerpaket stammt.

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

Was bedeutet das in der Praxis? In Go erlauben Funktionen mehrere Rückgabewerte. Wenn Ihre Funktion also möglicherweise nicht funktioniert, gibt sie höchstwahrscheinlich einen Fehlerwert zurück. Die Sprache fordert Sie dazu auf, explizit nach Fehlern zu suchen, wo sie auftreten (im Gegensatz zum Auslösen und Abfangen einer Ausnahme). Daher sollte Ihr Code normalerweise die Prüfung „if err! = Null. " Diese häufige Fehlerbehandlung mag zunächst eintönig erscheinen. Mit Fehler als Wert können Sie jedoch Fehler verwenden, um die Fehlerbehandlung zu vereinfachen. In einem verteilten System können Sie beispielsweise problemlos Versuche implementieren, Abfragen erneut zu versuchen, indem Sie Fehler einschließen.

Netzwerkprobleme treten immer im System auf, unabhängig davon, ob Daten an andere interne Dienste gesendet oder an Tools von Drittanbietern übertragen werden. Dieses Beispiel für ein Netzpaket zeigt, wie Sie den Fehler als Typ verwenden können, um temporäre Netzwerkfehler von permanenten Fehlern zu unterscheiden. Das Economist-Team verwendete einen ähnlichen Fehler-Wrapper, um inkrementelle Wiederholungsversuche beim Senden von Inhalten an externe APIs zu erstellen.

 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) } 

Go-Autoren glauben, dass nicht alle Ausnahmen außergewöhnlich sind. Ingenieure können Fehler eher intelligent beheben als die Anwendung zum Absturz bringen. Darüber hinaus können Sie mit der Go-Fehlerbehandlung Fehler besser kontrollieren, wodurch Aspekte wie das Debuggen oder die Benutzerfreundlichkeit von Fehlern verbessert werden können. Innerhalb der Content Platform ermöglichte dieses Designmerkmal von Go Entwicklern, fundierte Entscheidungen in Bezug auf Fehler zu treffen, was zu einer Erhöhung der Zuverlässigkeit des Gesamtsystems führte.

Datenkonsistenz

Datenkonsistenz ist ein kritischer Faktor in der Content Platform. Bei The Economist bilden Inhalte die Grundlage des Geschäfts. Ziel der Inhaltsplattform ist es, sicherzustellen, dass Inhalte einmal veröffentlicht werden können und überall verfügbar sind. Daher ist es wichtig, dass jedes Produkt und jeder Verbraucher Datenkonsistenz mit der Content Platform-API aufweist. Produkte verwenden GraphQL hauptsächlich für API-Anforderungen, für die ein statisches Schema erforderlich ist, das als eine Art Vertrag zwischen Verbrauchern und der Plattform dient. Von der Plattform verarbeitete Inhalte müssen mit diesem Schema übereinstimmen. Die statische Sprache half bei der Implementierung und machte es einfach, Datenkonsistenz zu erreichen.

Testen mit Go

Ein weiteres Merkmal, das die Konsistenz fördert, ist die Go-Testsuite. Die schnelle Kompilierungszeit von Go in Kombination mit erstklassigen Tests als Merkmal der Sprache ermöglichte es dem Team, effektive Testmethoden in Entwurfsworkflows und schnelle Fehler in Baugruppen zu implementieren. Die Testtools von Go erleichtern das Einrichten und Ausführen. Wenn Sie "go test" ausführen, werden alle Tests im aktuellen Verzeichnis ausgeführt, und der Testbefehl verfügt über mehrere nützliche Flags. Das Cover-Flag bietet einen detaillierten Code-Coverage-Bericht. Der Bench-Test führt Benchmark-Tests durch, die durch Ausführen des Namens der Testfunktion mit dem Wort "Bench" anstelle von "Test" angezeigt werden. Die TestMain-Funktion bietet Methoden für zusätzliche Testkonfigurationen, z. B. einen Dummy-Authentifizierungsserver.

Darüber hinaus kann Go tabellarische Tests mit anonymen Strukturen und Stubs mit Schnittstellen erstellen, um die Testabdeckung zu verbessern. Obwohl das Testen in Bezug auf Sprachfunktionen nicht neu ist, macht es Go einfach, robuste Tests zu erstellen und diese einfach in Workflows zu integrieren. Von Anfang an konnten die Economist-Ingenieure Tests als Teil der Assembly-Pipelines ohne spezielle Konfiguration ausführen und sogar Git Hooks hinzufügen, um Tests auszuführen, bevor Code in Github übertragen wurde.

Das Projekt war jedoch nicht ohne Aufwand, um Datenkonsistenz zu erreichen. Das erste große Problem für die Plattform war die Verwaltung dynamischer Inhalte aus unvorhersehbaren Backends. Die Plattform verwendet Inhalte von den ursprünglichen CMS-Systemen hauptsächlich über JSON-Endpunkte, bei denen die Struktur und die Datentypen nicht garantiert sind. Dies bedeutete, dass die Plattform das Standard-Go-Paket nicht zur Interpretation von json verwenden kann, das die JSON-Deserialisierung in Strukturen unterstützt. Es ertönt jedoch ein Alarm, wenn die Typen der Struktur- und Eingabedatenfelder nicht übereinstimmen.

Um dieses Problem zu lösen, war eine spezielle Methode erforderlich, um den Serverteil dem Standardformat zuzuordnen. Nach mehreren Iterationen des gewählten Ansatzes führte das Team einen eigenen Deserialisierungsprozess ein. Obwohl dieser Ansatz ein bisschen wie die Überarbeitung eines Standardbibliothekspakets war, gab er den Ingenieuren die vollständige Kontrolle über die Verarbeitung der Quelldaten.

Netzwerkunterstützung

Die Skalierbarkeit stand bei der neuen Plattform im Vordergrund, und dies wurde durch Standard-Go-Bibliotheken für Netzwerke und APIs bereitgestellt. In Go können Sie schnell skalierbare HTTP-Endpunkte implementieren, ohne dass Frameworks erforderlich sind. Im folgenden Beispiel wird das Standardbibliothekspaket net / http verwendet, um einen Handler zu konfigurieren, der einen Anforderungs- und Antwortschreiber akzeptiert. Bei der ersten Implementierung der Content Platform-API wurde das API-Framework verwendet. Es wurde schließlich durch eine Standardbibliothek ersetzt, da das Team anerkannte, dass es alle Netzwerkanforderungen ohne zusätzliche unnötige Kompromisse erfüllt. Golang HTTP-Handler skalieren, da jede Anforderung an den Handler in Goroutine, einem kompakten Thread ohne Anpassung, parallel ausgeführt wird.

 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)) } 

Parallelitätsmodell

Das Go-Parallelitätsmodell bietet mehrere Vorteile bei der Verbesserung der Leistung auf der gesamten Plattform. Bei der Arbeit mit verteilten Daten müssen die den Verbrauchern versprochenen Garantien eingehalten werden. Nach dem CAP-Theorem ist es nicht möglich, mehr als zwei der folgenden drei Garantien gleichzeitig bereitzustellen: Datenkonsistenz. Verfügbarkeit Beständig gegen Trennung. In der Economist-Plattform wurde letztendlich die Konsistenz übernommen, was bedeutet, dass das Lesen aus Datenquellen letztendlich konsistent ist und moderate Verzögerungen bei allen Datenquellen, die einen konsistenten Zustand erreichen, akzeptabel sind. Eine Möglichkeit, diese Lücke zu minimieren, ist die Verwendung von Goroutinen.

Goroutinen sind einfache Threads, die von der Go-Laufzeit verwaltet werden, um zu verhindern, dass ihnen die Threads ausgehen. Goroutinen ermöglichten die Optimierung asynchroner Aufgaben auf der Plattform. Eines der Datenrepositorys der Plattform ist beispielsweise Elasticsearch. Wenn Inhalte auf dem System aktualisiert werden, werden Inhalte, die auf dieses Element in Elasticsearch verweisen, aktualisiert und neu indiziert. Dank der Implementierung von Goroutines konnte die Verarbeitungszeit verkürzt werden, was eine schnelle Konsistenz der Elemente sicherstellte. Dieses Beispiel zeigt, wie Elemente, die für die Wiederaufbereitung geeignet sind, in Goroutine wiederaufbereitet werden.

 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 } 

Das Entwerfen von Systemen ist mehr als nur Programmieren. Ingenieure müssen verstehen, welche Werkzeuge wo und wann geeignet sind. Während Go ein leistungsstarkes Tool für die meisten Anforderungen der Content Platform von The Economist war, erforderten einige Einschränkungen andere Lösungen.

Abhängigkeitsmanagement

Als Go gerade veröffentlicht wurde, gab es kein Abhängigkeitsverwaltungssystem. Innerhalb der Community wurden verschiedene Tools entwickelt, um diesen Bedarf zu decken. Der Economist verwendete Git-Submodule, was zu einer Zeit sinnvoll war, als die Community aktiv ein Standard-Tool für das Abhängigkeitsmanagement bewarb. Obwohl die Community einem kohärenten Ansatz für das Abhängigkeitsmanagement bereits viel näher ist, ist sie heute nicht mehr da. Der The Economist-Ansatz mit Submodulen stellte keine ernsthaften Probleme dar, war jedoch für andere Go-Entwickler schwierig, und dies sollte bei der Umstellung auf Go berücksichtigt werden.

Es gab auch Plattformanforderungen, für die Go-Funktionen oder -Design nicht die beste Lösung waren. Da die Plattform die Audioverarbeitung unterstützte, waren die Tools von Go zum Extrahieren von Metadaten zu diesem Zeitpunkt begrenzt. Daher entschied sich das Team stattdessen für Exiftool Python. Plattformdienste arbeiten in Docker-Containern, mit denen Exiftool installiert und über die Go-Anwendung gestartet werden konnte.

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

Ein weiteres häufiges Szenario für die Plattform ist der Empfang von nicht funktionierendem HTML-Code aus den CMS-Quellsystemen, die Analyse des HTML-Codes auf Richtigkeit und die Bereinigung des HTML-Codes. Ursprünglich wurde Go für diesen Prozess verwendet. Da jedoch für die Standard-HTML-Bibliothek von Go korrektes HTML erforderlich ist, war vor der Verarbeitung eine große Menge an benutzerdefiniertem Code erforderlich, um HTML zu analysieren. Dieser Code wurde schnell fragil und verfehlte Grenzfälle, sodass eine neue Lösung in Javascript implementiert wurde. Javascript bietet große Flexibilität und Anpassungsfähigkeit, um den Prozess der Überprüfung und Bereinigung von HTML zu steuern.

Javascript war auch eine häufige Wahl zum Filtern und Weiterleiten von Ereignissen in der Plattform. Ereignisse werden mithilfe von AWS Lambdas gefiltert. Hierbei handelt es sich um einfache Funktionen, die nur beim Aufruf ausgeführt werden. Ein Anwendungsfall ist das Filtern von Ereignissen in verschiedenen Bändern, z. B. schnell und langsam. Diese Filterung basiert auf einem einzelnen Metadatenfeld im JSON-Objekt der Event-Handler-Shell. Die Filterimplementierung verwendete ein JSON-Javascript-Zeigerpaket, um ein Element in einem JSON-Objekt zu erfassen. Dieser Ansatz war viel effizienter als das vollständige Zerlegen des JSON, das Go benötigen würde. Während Funktionen dieses Typs mit Go erreicht werden konnten, war die Verwendung von Javascript für Ingenieure einfacher und lieferte einfachere Lambdas.

Rückblick Go

Wenn ich nach der Implementierung der Kontaktplattform und deren Unterstützung in der Produktion eine Retrospektive von Go und der Inhaltsplattform durchführen würde, wäre mein Feedback wie folgt:

Was ist schon gut

  • Schlüsselelemente des Sprachdesigns für verteilte Systeme.
  • Ein Parallelitätsmodell, das relativ einfach zu implementieren ist.
  • Schöne Codierung und lustige Community.

Was kann verbessert werden?

  • Weitere Weiterentwicklung der Standards für Versionskontrolle und Verkauf.
  • In einigen Bereichen nicht genügend Reife.
  • Details für bestimmte Benutzerfälle.

Im Allgemeinen war es eine positive Erfahrung, und Go ist eines der wichtigsten Elemente, mit denen die Content-Plattform skaliert werden konnte. Go wird nicht immer ein geeignetes Werkzeug sein, und das ist normal. Der Economist hat eine polyglotte Plattform und verwendet verschiedene Sprachen, wo es Sinn macht. Go wird wahrscheinlich nie die beste Wahl sein, wenn Sie mit Textobjekten und dynamischem Inhalt herumspielen müssen, sodass sich Javascript immer noch in der Toolbox befindet. Die Stärken von Go sind jedoch die Grundlage, auf der das System skalieren und wachsen kann.
Berücksichtigen Sie bei der Überlegung, ob Go für Sie geeignet ist, die wichtigsten Aspekte des Systemdesigns:

  • Was sind die Aufgaben Ihres Systems?
  • Welche Garantien bieten Sie Ihren Verbrauchern?
  • Welche Architektur und Muster sind für Ihr System geeignet?
  • Wie soll Ihr System skalieren?

Wenn Sie ein System entwickeln, das die Herausforderungen verteilter Daten, asynchroner Workflows sowie hoher Leistung und Skalierbarkeit bewältigt, empfehlen wir Ihnen, Go und seine Funktionen zur Beschleunigung Ihrer Systemziele in Betracht zu ziehen.

Freunde, wir warten auf Ihre Kommentare und laden alle zu dem offenen Webinar ein , das am 16. vom leitenden Entwickler bei Yandex abgehalten wird. In Kombination ist unser Lehrer Dmitry Smal .

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


All Articles