Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Mikhail Salosin (im Folgenden: MS): - Hallo zusammen! Ich heiße Michael. Ich arbeite als Backend-Entwickler bei MC2 Software und werde über die Verwendung von Go im Backend der mobilen Look+-Anwendung sprechen.

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Mag hier irgendjemand Hockey?

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Dann ist diese Anwendung genau das Richtige für Sie. Es ist für Android und iOS und dient dazu, Übertragungen verschiedener Sportereignisse online anzusehen und aufzuzeichnen. Die Anwendung enthält außerdem verschiedene Statistiken, Textübertragungen, Tabellen für Konferenzen, Turniere und andere für Fans nützliche Informationen.

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

In der Anwendung gibt es auch so etwas wie Videomomente, d. h. Sie können die wichtigsten Momente von Spielen (Tore, Kämpfe, Schießereien usw.) sehen. Wenn Sie nicht die gesamte Sendung sehen möchten, können Sie sich auch nur die interessantesten Sendungen ansehen.

Was haben Sie bei der Entwicklung verwendet?

Der Hauptteil wurde in Go geschrieben. Die API, mit der mobile Clients kommunizierten, wurde in Go geschrieben. In Go wurde auch ein Dienst zum Versenden von Push-Benachrichtigungen an Mobiltelefone geschrieben. Wir mussten auch unser eigenes ORM schreiben, über das wir vielleicht eines Tages sprechen würden. Nun, einige kleine Dienste wurden in Go geschrieben: Größenänderung und Laden von Bildern für die Redakteure ...

Als Datenbank verwendeten wir PostgreSQL. Die Editor-Schnittstelle wurde in Ruby on Rails mit dem ActiveAdmin-Gem geschrieben. Das Importieren von Statistiken von einem Statistikanbieter ist ebenfalls in Ruby geschrieben.

Für System-API-Tests haben wir Python Unittest verwendet. Memcached wird verwendet, um API-Zahlungsaufrufe zu drosseln, „Chef“ dient der Steuerung der Konfiguration, Zabbix dient der Erfassung und Überwachung interner Systemstatistiken. Graylog2 dient zum Sammeln von Protokollen, Slate ist eine API-Dokumentation für Clients.

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Protokollauswahl

Das erste Problem, auf das wir gestoßen sind: Wir mussten ein Protokoll für die Interaktion zwischen dem Backend und den mobilen Clients auswählen, basierend auf den folgenden Punkten ...

  • Die wichtigste Anforderung: Daten zu Kunden müssen in Echtzeit aktualisiert werden. Das heißt, jeder, der die Sendung gerade sieht, sollte fast sofort Updates erhalten.
  • Der Einfachheit halber haben wir angenommen, dass mit Clients synchronisierte Daten nicht gelöscht, sondern mithilfe spezieller Flags ausgeblendet werden.
  • Alle Arten seltener Anfragen (z. B. Statistiken, Teamzusammensetzungen, Teamstatistiken) werden durch gewöhnliche GET-Anfragen abgerufen.
  • Außerdem musste das System problemlos 100 Benutzer gleichzeitig unterstützen können.

Auf dieser Grundlage hatten wir zwei Protokolloptionen:

  1. Websockets. Aber wir brauchten keine Kanäle vom Client zum Server. Wir mussten nur Updates vom Server an den Client senden, daher ist ein Websocket eine redundante Option.
  2. Server-Sent Events (SSE) kam genau richtig! Es ist ganz einfach und erfüllt im Grunde alles, was wir brauchen.

Vom Server gesendete Ereignisse

Ein paar Worte zur Funktionsweise dieses Dings ...

Es läuft über eine http-Verbindung. Der Client sendet eine Anfrage, der Server antwortet mit Content-Type: text/event-stream und schließt die Verbindung mit dem Client nicht, sondern schreibt weiterhin Daten in die Verbindung:

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Die Daten können in einem mit den Kunden vereinbarten Format versendet werden. In unserem Fall haben wir es in dieser Form gesendet: Der Name der geänderten Struktur (Person, Spieler) wurde an das Ereignisfeld gesendet und JSON mit neuen, geänderten Feldern für den Spieler wurde an das Datenfeld gesendet.

Lassen Sie uns nun darüber sprechen, wie die Interaktion selbst funktioniert.

  • Als erstes ermittelt der Client, wann die letzte Synchronisierung mit dem Dienst durchgeführt wurde: Er schaut sich seine lokale Datenbank an und ermittelt das Datum der letzten von ihm aufgezeichneten Änderung.
  • Es wird eine Anfrage mit diesem Datum gesendet.
  • Als Antwort senden wir ihm alle Aktualisierungen, die seit diesem Datum erfolgt sind.
  • Danach stellt es eine Verbindung zum Live-Kanal her und wird erst geschlossen, wenn die folgenden Updates erforderlich sind:

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Wir senden ihm eine Änderungsliste: Wenn jemand ein Tor schießt, ändern wir den Spielstand, wenn er sich verletzt, wird dies ebenfalls in Echtzeit gesendet. Somit erhalten Kunden sofort aktuelle Daten im Spielereignis-Feed. Damit der Client versteht, dass der Server nicht ausgefallen ist und ihm nichts passiert ist, senden wir regelmäßig alle 15 Sekunden einen Zeitstempel – damit er weiß, dass alles in Ordnung ist und keine erneute Verbindung erforderlich ist.

Wie wird die Live-Verbindung bedient?

  • Zunächst erstellen wir einen Kanal, in dem gepufferte Updates empfangen werden.
  • Danach abonnieren wir diesen Kanal, um Updates zu erhalten.
  • Wir setzen den richtigen Header, damit der Kunde weiß, dass alles in Ordnung ist.
  • Senden Sie den ersten Ping. Wir zeichnen einfach den aktuellen Verbindungszeitstempel auf.
  • Danach lesen wir in einer Schleife aus dem Kanal, bis der Update-Kanal geschlossen wird. Der Kanal erhält regelmäßig entweder den aktuellen Zeitstempel oder Änderungen, die wir bereits in offene Verbindungen schreiben.

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Das erste Problem, auf das wir gestoßen sind, war folgendes: Für jede mit dem Client geöffnete Verbindung haben wir einen Timer erstellt, der alle 15 Sekunden einmal tickte – es stellt sich heraus, dass, wenn wir 6 Verbindungen mit einer Maschine (mit einem API-Server) geöffnet hätten, 6 Es wurden tausend Timer erstellt. Dies führte dazu, dass die Maschine die erforderliche Last nicht halten konnte. Das Problem war für uns nicht so offensichtlich, aber wir haben ein wenig Hilfe bekommen und es behoben.

Daher kommt unser Ping jetzt von demselben Kanal, von dem das Update kommt.

Dementsprechend gibt es nur einen Timer, der alle 15 Sekunden einmal tickt.

Hier gibt es mehrere Hilfsfunktionen – das Senden des Headers, des Pings und der Struktur selbst. Das heißt, hier werden der Name der Tabelle (Person, Spiel, Saison) und die Informationen zu diesem Eintrag übermittelt:

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Mechanismus zum Senden von Updates

Nun ein wenig darüber, woher die Änderungen kommen. Wir haben mehrere Leute, Redakteure, die die Sendung in Echtzeit verfolgen. Sie schaffen alle Ereignisse: Jemand wurde vom Platz gestellt, jemand wurde verletzt, irgendein Ersatz …

Über ein CMS gelangen Daten in die Datenbank. Anschließend benachrichtigt die Datenbank die API-Server über den Listen/Notify-Mechanismus darüber. API-Server senden diese Informationen bereits an Clients. Somit haben wir im Wesentlichen nur wenige Server, die mit der Datenbank verbunden sind, und es gibt keine besondere Belastung für die Datenbank, da der Client in keiner Weise direkt mit der Datenbank interagiert:

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

PostgreSQL: Zuhören/Benachrichtigen

Mit dem Listen/Notify-Mechanismus in Postgres können Sie Abonnenten von Ereignissen benachrichtigen, dass sich ein Ereignis geändert hat – ein Datensatz in der Datenbank erstellt wurde. Dazu haben wir einen einfachen Trigger und eine einfache Funktion geschrieben:

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Beim Einfügen oder Ändern eines Datensatzes rufen wir die Notify-Funktion im Kanal data_updates auf und übergeben dort den Namen der Tabelle und die Kennung des Datensatzes, der geändert oder eingefügt wurde.

Für alle Tabellen, die mit dem Client synchronisiert werden müssen, definieren wir einen Trigger, der nach Änderung/Aktualisierung eines Datensatzes die auf der Folie unten angegebene Funktion aufruft.
Wie abonniert die API diese Änderungen?

Es wird ein Fanout-Mechanismus erstellt – er sendet Nachrichten an den Client. Es sammelt alle Kundenkanäle und sendet Aktualisierungen, die es über diese Kanäle erhalten hat:

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Hier prüft die Standard-PQ-Bibliothek, die sich mit der Datenbank verbindet und sagt, dass sie den Kanal (data_updates) abhören möchte, ob die Verbindung offen ist und alles in Ordnung ist. Ich lasse die Fehlerprüfung weg, um Platz zu sparen (eine Nichtprüfung ist gefährlich).

Als nächstes stellen wir asynchron den Ticker ein, der alle 15 Sekunden einen Ping sendet, und beginnen mit dem Abhören des Kanals, den wir abonniert haben. Wenn wir einen Ping erhalten, veröffentlichen wir diesen Ping. Wenn wir einen Eintrag erhalten, veröffentlichen wir diesen Eintrag an alle Abonnenten dieses Fanouts.

Wie funktioniert Fan-Out?

Auf Russisch bedeutet dies „Splitter“. Wir haben ein Objekt, das Abonnenten registriert, die einige Updates erhalten möchten. Und sobald ein Update für dieses Objekt eintrifft, verteilt es dieses Update an alle seine Abonnenten. Einfach genug:

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

So wird es in Go implementiert:

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Es gibt eine Struktur, die über Mutexe synchronisiert wird. Es verfügt über ein Feld, das den Status der Fanout-Verbindung zur Datenbank speichert, d. h. es lauscht gerade und empfängt Aktualisierungen, sowie eine Liste aller verfügbaren Kanäle – Karte, deren Schlüssel der Kanal und die Struktur in der Form ist Werte (im Wesentlichen wird es in keiner Weise verwendet).

Zwei Methoden – Verbunden und Getrennt – ermöglichen es uns, Fanout mitzuteilen, dass wir eine Verbindung zur Basis haben, diese aufgetaucht ist und dass die Verbindung zur Basis unterbrochen wurde. Im zweiten Fall müssen Sie alle Clients trennen und ihnen mitteilen, dass sie nichts mehr hören können und dass sie die Verbindung wiederherstellen, weil die Verbindung zu ihnen unterbrochen wurde.

Es gibt auch eine Subscribe-Methode, die den Kanal zu den „Listenern“ hinzufügt:

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Es gibt eine Unsubscribe-Methode, die den Kanal von den Zuhörern entfernt, wenn der Client die Verbindung trennt, sowie eine Publish-Methode, mit der Sie eine Nachricht an alle Abonnenten senden können.

Frage: – Was wird über diesen Kanal übertragen?

MS: – Das geänderte Modell oder der Ping wird übertragen (im Wesentlichen nur eine Zahl, Ganzzahl).

MS: – Sie können alles senden, jede Struktur senden, veröffentlichen – es wird einfach in JSON umgewandelt und das war’s.

MS: – Wir erhalten eine Benachrichtigung von Postgres – sie enthält den Tabellennamen und die Kennung. Basierend auf dem Tabellennamen und der Kennung erhalten wir den benötigten Datensatz und senden diese Struktur dann zur Veröffentlichung.

Infrastruktur

Wie sieht das aus infrastruktureller Sicht aus? Wir verfügen über 7 Hardware-Server: Einer davon ist vollständig der Datenbank gewidmet, auf den anderen sechs laufen virtuelle Maschinen. Es gibt 6 Kopien der API: Jede virtuelle Maschine mit der API läuft auf einem separaten Hardwareserver – dies dient der Zuverlässigkeit.

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Wir haben zwei Frontends mit Keepalived installiert, um die Zugänglichkeit zu verbessern, sodass, wenn etwas passiert, ein Frontend das andere ersetzen kann. Außerdem – zwei Kopien des CMS.

Es gibt auch einen Statistikimporter. Es gibt einen DB-Slave, von dem regelmäßig Backups erstellt werden. Es gibt Pigeon Pusher, eine Anwendung, die Push-Benachrichtigungen an Kunden sendet, sowie Infrastruktur-Dinge: Zabbix, Graylog2 und Chef.

Tatsächlich ist diese Infrastruktur redundant, da 100 mit weniger Servern bedient werden können. Aber es gab Eisen – wir haben es benutzt (uns wurde gesagt, dass es möglich sei – warum nicht).

Vorteile von Go

Nachdem wir an dieser Anwendung gearbeitet hatten, kamen die offensichtlichen Vorteile von Go zum Vorschein.

  • Coole http-Bibliothek. Damit kann man ziemlich viel „out of the box“ erstellen.
  • Außerdem Kanäle, die es uns ermöglichten, ganz einfach einen Mechanismus zum Versenden von Benachrichtigungen an Kunden zu implementieren.
  • Der wunderbare Race-Detektor ermöglichte es uns, mehrere kritische Fehler (Staging-Infrastruktur) zu beseitigen. Alles, was am Staging funktioniert, wird gestartet und mit dem Race-Schlüssel kompiliert. und wir können uns dementsprechend die Staging-Infrastruktur ansehen, um zu sehen, welche potenziellen Probleme wir haben.
  • Minimalismus und Einfachheit der Sprache.

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

Wir suchen Entwickler! Wenn jemand möchte, bitte.

Fragen

Frage aus dem Publikum (im Folgenden – B): – Mir scheint, dass Sie einen wichtigen Punkt bezüglich Fan-out übersehen haben. Verstehe ich richtig, dass Sie beim Senden einer Antwort an einen Client blockieren, wenn der Client nicht lesen möchte?

MS: - Nein, wir blockieren nicht. Erstens haben wir das alles hinter Nginx, das heißt, es gibt keine Probleme mit langsamen Clients. Zweitens verfügt der Client über einen Kanal mit Puffer – tatsächlich können wir dort bis zu hundert Updates platzieren... Wenn wir nicht in den Kanal schreiben können, wird er gelöscht. Wenn wir feststellen, dass der Kanal blockiert ist, schließen wir ihn einfach und fertig – der Client stellt die Verbindung wieder her, wenn ein Problem auftritt. Daher liegt hier grundsätzlich keine Sperrung vor.

in: – Könnte es nicht möglich sein, sofort einen Datensatz an Listen/Notify zu senden und nicht eine Identifier-Tabelle?

MS: – Für „Listen/Notify“ gilt ein Limit von 8 Bytes für die gesendete Vorladung. Im Prinzip wäre es möglich, zu versenden, wenn wir es mit einer kleinen Datenmenge zu tun hätten, aber mir scheint, dass dieser Weg einfach zuverlässiger ist. Die Einschränkungen liegen in Postgres selbst.

in: – Erhalten Kunden Updates zu Spielen, an denen sie nicht interessiert sind?

MS: - Im Allgemeinen ja. In der Regel finden 2-3 Spiele parallel statt, und selbst dann recht selten. Wenn ein Kunde sich etwas ansieht, dann schaut er sich normalerweise das laufende Spiel an. Dann verfügt der Kunde über eine lokale Datenbank, in der alle diese Aktualisierungen zusammengefasst werden, und auch ohne Internetverbindung kann der Kunde alle vergangenen Spiele einsehen, für die er Aktualisierungen hat. Im Wesentlichen synchronisieren wir unsere Datenbank auf dem Server mit der lokalen Datenbank des Kunden, damit dieser offline arbeiten kann.

in: – Warum haben Sie Ihr eigenes ORM erstellt?

Alexey (einer der Entwickler von Look+): – Damals (es war vor einem Jahr) gab es weniger ORMs als heute, wo es ziemlich viele davon gibt. Was mir an den meisten ORMs gefällt, ist, dass die meisten auf leeren Schnittstellen laufen. Das heißt, die Methoden in diesen ORMs sind bereit, alles anzunehmen: eine Struktur, einen Strukturzeiger, eine Zahl, etwas völlig Irrelevantes ...

Unser ORM generiert Strukturen auf Basis des Datenmodells. Ich selbst. Und deshalb sind alle Methoden konkret, verwenden keine Reflexion usw. Sie akzeptieren Strukturen und erwarten, die Strukturen zu nutzen, die kommen.

in: – Wie viele Personen haben teilgenommen?

MS: – In der Anfangsphase nahmen zwei Personen teil. Wir haben irgendwo im Juni angefangen und im August war der Hauptteil fertig (die erste Version). Im September gab es eine Veröffentlichung.

in: – Wo Sie SSE beschreiben, verwenden Sie kein Timeout. Warum so?

MS: – Um ehrlich zu sein, ist SSE immer noch ein HTML5-Protokoll: Soweit ich weiß, ist der SSE-Standard für die Kommunikation mit Browsern konzipiert. Es verfügt über zusätzliche Funktionen, damit Browser die Verbindung wiederherstellen können (usw.), aber wir brauchen sie nicht, weil wir Clients hatten, die jede beliebige Logik zum Verbinden und Empfangen von Informationen implementieren konnten. Wir haben nicht SSE gemacht, sondern etwas Ähnliches wie SSE. Dies ist nicht das Protokoll selbst.
Es bestand keine Notwendigkeit. Soweit ich weiß, haben Kunden den Verbindungsmechanismus fast von Grund auf implementiert. Es war ihnen eigentlich egal.

in: – Welche zusätzlichen Dienstprogramme haben Sie verwendet?

MS: – Wir haben am aktivsten Govet und Golint verwendet, um den Stil zu vereinheitlichen, sowie Gofmt. Es wurde nichts anderes verwendet.

in: – Was haben Sie zum Debuggen verwendet?

MS: – Die Fehlersuche erfolgte größtenteils durch Tests. Wir haben keinen Debugger oder GOP verwendet.

in: – Können Sie die Folie zurückgeben, auf der die Veröffentlichungsfunktion implementiert ist? Verwirren Sie Variablennamen mit nur einem Buchstaben?

MS: - Nein. Sie haben einen ziemlich „engen“ Sichtbereich. Sie werden nirgendwo sonst außer hier verwendet (mit Ausnahme der Interna dieser Klasse) und sie sind sehr kompakt – sie benötigen nur 7 Zeilen.

in: – Irgendwie ist es immer noch nicht intuitiv...

MS: - Nein, nein, das ist ein echter Code! Es geht nicht um Stil. Es ist einfach eine so nützliche, sehr kleine Klasse – nur 3 Felder innerhalb der Klasse ...

Michail Salosin. Golang-Treffen. Verwendung von Go im Backend der Look+-Anwendung

MS: – Im Großen und Ganzen ändern sich alle Daten, die mit Clients synchronisiert werden (Saisonspiele, Spieler), nicht. Grob gesagt: Wenn wir eine andere Sportart erstellen, bei der wir das Spiel ändern müssen, werden wir einfach alles in der neuen Version des Clients berücksichtigen und die alten Versionen des Clients werden gesperrt.

in: – Gibt es Abhängigkeitsverwaltungspakete von Drittanbietern?

MS: – Wir haben go dep verwendet.

in: – Im Thema des Berichts stand etwas über Video, aber im Bericht stand nichts über Video.

MS: – Nein, ich habe im Thema nichts über das Video. Es heißt „Look+“ – so heißt die Anwendung.

in: – Sie sagten, dass es an Kunden gestreamt wird?

MS: – Wir waren nicht am Streaming von Videos beteiligt. Dies wurde vollständig von Megafon durchgeführt. Ja, ich habe nicht gesagt, dass es sich bei der Anwendung um MegaFon handelt.

MS: – Go – zum Senden aller Daten – zum Spielstand, zu Spielereignissen, Statistiken... Go ist das gesamte Backend für die Anwendung. Der Client muss von irgendwoher wissen, welchen Link er für den Spieler verwenden muss, damit der Benutzer das Spiel sehen kann. Wir haben Links zu vorbereiteten Videos und Streams.

Einige Anzeigen 🙂

Vielen Dank, dass Sie bei uns geblieben sind. Gefallen Ihnen unsere Artikel? Möchten Sie weitere interessante Inhalte sehen? Unterstützen Sie uns, indem Sie eine Bestellung aufgeben oder an Freunde weiterempfehlen. Cloud-VPS für Entwickler ab 4.99 $, ein einzigartiges Analogon von Einstiegsservern, das von uns für Sie erfunden wurde: Die ganze Wahrheit über VPS (KVM) E5-2697 v3 (6 Kerne) 10 GB DDR4 480 GB SSD 1 Gbit/s ab 19 $ oder wie teilt man sich einen Server? (verfügbar mit RAID1 und RAID10, bis zu 24 Kerne und bis zu 40 GB DDR4).

Dell R730xd 2-mal günstiger im Equinix Tier IV-Rechenzentrum in Amsterdam? Nur hier 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6 GHz 14C 64 GB DDR4 4 x 960 GB SSD 1 Gbit/s 100 TV ab 199 $ in den Niederlanden! Dell R420 – 2x E5-2430 2.2 GHz 6C 128 GB DDR3 2 x 960 GB SSD 1 Gbit/s 100 TB – ab 99 $! Lesen über Wie baut man ein Infrastrukturunternehmen auf? Klasse mit dem Einsatz von Dell R730xd E5-2650 v4 Servern im Wert von 9000 Euro für einen Cent?

Source: habr.com

Kommentar hinzufügen