Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Mikhail Salosin (hierna – MS): - Dag Allemaal! Mijn naam is Michael. Ik werk als backend-ontwikkelaar bij MC2 Software en ik zal het hebben over het gebruik van Go in de backend van de Look+ mobiele applicatie.

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Houdt iemand hier van hockey?

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Dan is deze applicatie iets voor jou. Het is voor Android en iOS en wordt gebruikt om uitzendingen van verschillende sportevenementen online te bekijken en op te nemen. De applicatie bevat ook verschillende statistieken, tekstuitzendingen, tabellen voor conferenties, toernooien en andere informatie die nuttig is voor fans.

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Ook in de applicatie bestaat er zoiets als videomomenten, d.w.z. je kunt de belangrijkste momenten van wedstrijden bekijken (goals, gevechten, shootouts, etc.). Als je niet de hele uitzending wilt bekijken, kun je alleen de meest interessante bekijken.

Wat heb je gebruikt bij de ontwikkeling?

Het grootste deel is geschreven in Go. De API waarmee mobiele clients communiceerden, is geschreven in Go. Ook is er in Go een dienst geschreven voor het versturen van pushmeldingen naar mobiele telefoons. We moesten ook ons ​​eigen ORM schrijven, waar we het misschien ooit over zouden hebben. Welnu, er zijn een aantal kleine services in Go geschreven: het formaat wijzigen en afbeeldingen laden voor de redactie...

We gebruikten PostgreSQL als database. De editorinterface is geschreven in Ruby on Rails met behulp van de ActiveAdmin-edelsteen. Ook het importeren van statistieken van een statistiekprovider wordt in Ruby geschreven.

Voor systeem-API-tests hebben we Python unittest gebruikt. Memcached wordt gebruikt om API-betalingsoproepen af ​​te remmen, “Chef” wordt gebruikt om de configuratie te controleren, Zabbix wordt gebruikt om interne systeemstatistieken te verzamelen en te monitoren. Graylog2 is voor het verzamelen van logbestanden, Slate is API-documentatie voor klanten.

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Protocolselectie

Het eerste probleem dat we tegenkwamen: we moesten een protocol kiezen voor interactie tussen de backend en mobiele clients, op basis van de volgende punten...

  • De belangrijkste eis: gegevens over klanten moeten realtime worden bijgewerkt. Dat wil zeggen dat iedereen die momenteel naar de uitzending kijkt, vrijwel onmiddellijk updates zou moeten ontvangen.
  • Om de zaken eenvoudiger te maken, zijn we ervan uitgegaan dat gegevens die met clients worden gesynchroniseerd, niet worden verwijderd, maar worden verborgen met behulp van speciale vlaggen.
  • Allerlei zeldzame verzoeken (zoals statistieken, teamsamenstellingen, teamstatistieken) worden verkregen door gewone GET-verzoeken.
  • Bovendien moest het systeem gemakkelijk 100 gebruikers tegelijk kunnen ondersteunen.

Op basis hiervan hadden we twee protocolopties:

  1. Webaansluitingen. Maar we hadden geen kanalen nodig van de client naar de server. We hoefden alleen updates van de server naar de client te sturen, dus een websocket is een redundante optie.
  2. Server-Sent Events (SSE) kwamen precies goed naar voren! Het is vrij eenvoudig en voldoet in principe aan alles wat we nodig hebben.

Door server verzonden gebeurtenissen

Een paar woorden over hoe dit ding werkt...

Het draait bovenop een http-verbinding. De client stuurt een verzoek, de server reageert met Content-Type: text/event-stream en verbreekt de verbinding met de client niet, maar blijft gegevens naar de verbinding schrijven:

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Gegevens kunnen worden verzonden in een met de klant overeengekomen formaat. In ons geval hebben we het in deze vorm verzonden: de naam van de gewijzigde structuur (persoon, speler) werd naar het gebeurtenisveld gestuurd en JSON met nieuwe, gewijzigde velden voor de speler werd naar het gegevensveld gestuurd.

Laten we het nu hebben over hoe de interactie zelf werkt.

  • Het eerste dat de klant doet, is vaststellen wanneer de laatste synchronisatie met de dienst is uitgevoerd: hij kijkt naar zijn lokale database en bepaalt de datum van de laatste daarin geregistreerde wijziging.
  • Er wordt een verzoek verzonden met deze datum.
  • Als reactie sturen we hem alle updates die sinds die datum hebben plaatsgevonden.
  • Daarna maakt het verbinding met het livekanaal en sluit het pas als het deze updates nodig heeft:

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

We sturen hem een ​​lijst met wijzigingen: als iemand een doelpunt scoort, veranderen we de uitslag van de wedstrijd, als hij geblesseerd raakt, wordt dit ook realtime verzonden. Zo ontvangen klanten direct actuele gegevens in de wedstrijdgebeurtenisfeed. Om de klant te laten begrijpen dat de server niet is overleden en dat er niets mee is gebeurd, sturen we periodiek elke 15 seconden een tijdstempel, zodat hij weet dat alles in orde is en dat er geen nieuwe verbinding nodig is.

Hoe wordt de liveverbinding onderhouden?

  • Allereerst creëren we een kanaal waarin gebufferde updates worden ontvangen.
  • Daarna abonneren we ons op dit kanaal om updates te ontvangen.
  • We stellen de juiste header in, zodat de klant weet dat alles in orde is.
  • Verzend de eerste ping. We registreren eenvoudigweg de huidige verbindingstijdstempel.
  • Daarna lezen we in een lus van het kanaal totdat het updatekanaal wordt gesloten. Het kanaal ontvangt periodiek de huidige tijdstempel of de wijzigingen die we al aan het schrijven zijn naar open verbindingen.

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Het eerste probleem dat we tegenkwamen was het volgende: voor elke verbinding die met de client werd geopend, hebben we een timer gemaakt die elke 15 seconden tikte - het blijkt dat als we 6 verbindingen open hadden met één machine (met één API-server), 6 duizend timers zijn gemaakt. Dit leidde ertoe dat de machine niet de vereiste lading kon dragen. Het probleem was voor ons niet zo duidelijk, maar we kregen een beetje hulp en hebben het opgelost.

Als gevolg hiervan komt onze ping nu van hetzelfde kanaal waarvan de update afkomstig is.

Er is dus maar één timer die elke 15 seconden tikt.

Er zijn hier verschillende hulpfuncties: het verzenden van de header, ping en de structuur zelf. Dat wil zeggen, de naam van de tafel (persoon, wedstrijd, seizoen) en de informatie over deze invoer worden hier verzonden:

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Mechanisme voor het verzenden van updates

Nu een beetje over waar de veranderingen vandaan komen. We hebben verschillende mensen, redacteuren, die de uitzending realtime bekijken. Ze creëren alle gebeurtenissen: iemand werd weggestuurd, iemand raakte gewond, een soort vervanger...

Via een CMS komen gegevens de database binnen. Hierna informeert de database de API-servers hierover via het Listen/Notify-mechanisme. API-servers sturen deze informatie al naar clients. We hebben dus in wezen maar een paar servers die op de database zijn aangesloten en er is geen speciale belasting voor de database, omdat de client op geen enkele manier rechtstreeks met de database communiceert:

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

PostgreSQL: Luister/Meld

Met het Listen/Notify-mechanisme in Postgres kunt u abonnees van gebeurtenissen op de hoogte stellen dat een gebeurtenis is gewijzigd - er is een record in de database aangemaakt. Om dit te doen, hebben we een eenvoudige trigger en functie geschreven:

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Bij het invoegen of wijzigen van een record roepen we de notificatiefunctie op het data_updates-kanaal aan, waarbij we de naam van de tabel en de identificatie van het record doorgeven dat is gewijzigd of ingevoegd.

Voor alle tabellen die met de client gesynchroniseerd moeten worden, definiëren we een trigger, die, na het wijzigen/bijwerken van een record, de functie oproept die op de onderstaande dia staat aangegeven.
Hoe abonneert de API zich op deze wijzigingen?

Er wordt een Fanout-mechanisme gemaakt: het stuurt berichten naar de client. Het verzamelt alle klantkanalen en verzendt updates die het via deze kanalen heeft ontvangen:

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Hier controleert de standaard pq-bibliotheek, die verbinding maakt met de database en zegt dat hij naar het kanaal wil luisteren (data_updates), of de verbinding open is en dat alles in orde is. Ik laat de foutcontrole achterwege om ruimte te besparen (niet controleren is gevaarlijk).

Vervolgens stellen we Ticker asynchroon in, die elke 15 seconden een ping verzendt, en beginnen we te luisteren naar het kanaal waarop we zijn geabonneerd. Als we een ping ontvangen, publiceren we deze ping. Als we een inzending ontvangen, publiceren we deze inzending voor alle abonnees van deze Fanout.

Hoe werkt Fan-out?

In het Russisch vertaalt dit zich als “splitter”. We hebben één object dat abonnees registreert die updates willen ontvangen. En zodra er een update voor dit object binnenkomt, distribueert het deze update naar al zijn abonnees. Simpel genoeg:

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Hoe het is geïmplementeerd in Go:

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Er is een structuur, deze wordt gesynchroniseerd met behulp van Mutexes. Het heeft een veld dat de status van Fanout's verbinding met de database opslaat, d.w.z. het luistert momenteel en zal updates ontvangen, evenals een lijst met alle beschikbare kanalen - kaart, waarvan de sleutel het kanaal en de structuur is in de vorm van waarden (in wezen wordt het op geen enkele manier gebruikt).

Twee methoden - Verbonden en Verbinding verbroken - stellen ons in staat om Fanout te vertellen dat we een verbinding met de basis hebben, het is verschenen en dat de verbinding met de basis is verbroken. In het tweede geval moet je alle clients loskoppelen en hen vertellen dat ze nergens meer naar kunnen luisteren en dat ze opnieuw verbinding maken omdat de verbinding met hen is verbroken.

Er is ook een abonneermethode die het kanaal toevoegt aan de “luisteraars”:

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Er is een Unsubscribe-methode, die het kanaal van luisteraars verwijdert als de client de verbinding verbreekt, evenals een Publish-methode, waarmee je een bericht naar alle abonnees kunt sturen.

Vraag: – Wat wordt via dit kanaal uitgezonden?

MEVR: – Het model dat is gewijzigd of ping wordt verzonden (in wezen slechts een getal, geheel getal).

MEVR: – Je kunt alles verzenden, elke structuur verzenden, publiceren – het verandert gewoon in JSON en dat is alles.

MEVR: – We ontvangen een melding van Postgres – deze bevat de tabelnaam en ID. Op basis van de tabelnaam en ID krijgen we de record die we nodig hebben, en vervolgens sturen we deze structuur ter publicatie.

Infrastructuur

Hoe ziet dit eruit vanuit een infrastructuurperspectief? We hebben 7 hardwareservers: één ervan is volledig toegewijd aan de database, de andere zes draaien virtuele machines. Er zijn 6 exemplaren van de API: elke virtuele machine met de API draait op een aparte hardwareserver - dit is voor de betrouwbaarheid.

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

We hebben twee frontends waarop Keepalived is geïnstalleerd om de toegankelijkheid te verbeteren, zodat als er iets gebeurt, de ene frontend de andere kan vervangen. Ook – twee exemplaren van het CMS.

Er is ook een importeur van statistieken. Er is een DB Slave waarvan periodiek backups worden gemaakt. Er is Pigeon Pusher, een applicatie die pushmeldingen naar klanten stuurt, maar ook infrastructuurzaken: Zabbix, Graylog2 en Chef.

In feite is deze infrastructuur redundant, omdat er met minder servers 100 bediend kunnen worden. Maar er was ijzer - we gebruikten het (ons werd verteld dat het mogelijk was - waarom niet).

Voordelen van Go

Nadat we aan deze applicatie hadden gewerkt, kwamen zulke duidelijke voordelen van Go naar voren.

  • Coole http-bibliotheek. Hiermee kun je behoorlijk wat out-of-the-box creëren.
  • Plus kanalen waarmee we heel eenvoudig een mechanisme konden implementeren voor het verzenden van meldingen naar klanten.
  • Het mooie is dat Race Detector ons in staat stelde een aantal kritieke bugs te elimineren (staging-infrastructuur). Alles wat aan enscenering werkt wordt gelanceerd, samengesteld met de Race-toets; en we kunnen dienovereenkomstig naar de staging-infrastructuur kijken om te zien welke potentiële problemen we hebben.
  • Minimalisme en eenvoud van taal.

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

Wij zijn op zoek naar ontwikkelaars! Als iemand wil, alsjeblieft.

vragen

Vraag uit het publiek (hierna – B): – Het lijkt mij dat je een belangrijk punt met betrekking tot Fan-out hebt gemist. Begrijp ik het goed dat wanneer u een antwoord naar een klant verzendt, u blokkeert als de klant niet wil lezen?

MEVR: - Nee, we blokkeren niet. Ten eerste hebben we dit allemaal achter nginx, dat wil zeggen dat er geen problemen zijn met langzame clients. Ten tweede heeft de client een kanaal met een buffer - sterker nog, we kunnen daar maximaal honderd updates plaatsen... Als we niet naar het kanaal kunnen schrijven, wordt het verwijderd. Als we zien dat het kanaal is geblokkeerd, sluiten we het kanaal eenvoudigweg, en dat is alles: de klant zal opnieuw verbinding maken als er zich een probleem voordoet. Daarom is hier in principe geen sprake van blokkering.

В: – Zou het niet mogelijk zijn om onmiddellijk een record naar Listen/Notify te sturen, en niet een identificatietabel?

MEVR: – Listen/Notify heeft een limiet van 8 bytes op de preload die het verzendt. In principe zou het mogelijk zijn om te verzenden als we met een kleine hoeveelheid gegevens te maken hadden, maar het lijkt mij dat deze manier [de manier waarop we het doen] gewoon betrouwbaarder is. De beperkingen liggen in Postgres zelf.

В: – Ontvangen klanten updates over matches waarin ze niet geïnteresseerd zijn?

MEVR: - Over het algemeen wel. In de regel vinden er 2-3 wedstrijden parallel plaats, en zelfs dan vrij zelden. Als een klant ergens naar kijkt, dan kijkt hij meestal naar de wedstrijd die gaande is. Vervolgens heeft de klant een lokale database waarin al deze updates worden opgeteld, en zelfs zonder internetverbinding kan de klant alle eerdere wedstrijden bekijken waarvoor hij updates heeft. In wezen synchroniseren we onze database op de server met de lokale database van de klant, zodat hij offline kan werken.

В: – Waarom heb je je eigen ORM gemaakt?

Alexey (een van de ontwikkelaars van Look+): – Destijds (het was een jaar geleden) waren er minder ORM’s dan nu, terwijl het er behoorlijk veel zijn. Het leukste aan de meeste ORM's die er zijn, is dat de meeste op lege interfaces draaien. Dat wil zeggen dat de methoden in deze ORM's klaar zijn om alles aan te pakken: een structuur, een structuuraanwijzer, een getal, iets dat volkomen irrelevant is...

Ons ORM genereert structuren op basis van het datamodel. Mezelf. En daarom zijn alle methoden concreet, maken geen gebruik van reflectie etc. Ze accepteren structuren en verwachten ook gebruik te maken van de structuren die komen.

В: – Hoeveel mensen namen deel?

MEVR: – In de beginfase namen twee mensen deel. We zijn ergens in juni begonnen en in augustus was het grootste deel klaar (de eerste versie). In september was er een release.

В: – Waar u SSE beschrijft, gebruikt u geen time-out. Waarom is dat?

MEVR: – Eerlijk gezegd is SSE nog steeds een html5-protocol: de SSE-standaard is ontworpen om met browsers te communiceren, voor zover ik het begrijp. Het heeft extra functies zodat browsers opnieuw verbinding kunnen maken (enzovoorts), maar we hebben ze niet nodig omdat we klanten hadden die elke logica konden implementeren voor het verbinden en ontvangen van informatie. We hebben geen SSE gemaakt, maar iets dat lijkt op SSE. Dit is niet het protocol zelf.
Er was geen noodzaak. Voor zover ik het begrijp, hebben klanten het verbindingsmechanisme bijna helemaal opnieuw geïmplementeerd. Het maakte hen niet zoveel uit.

В: – Welke extra hulpprogramma's heb je gebruikt?

MEVR: – We hebben het meest actief govet en golint gebruikt om de stijl één te maken, evenals gofmt. Er werd niets anders gebruikt.

В: – Wat heb je gebruikt om te debuggen?

MEVR: – Het debuggen gebeurde grotendeels met behulp van tests. We hebben geen debugger of GOP gebruikt.

В: – Kunt u de dia retourneren waarop de publicatiefunctie is geïmplementeerd? Brengen variabelennamen van één letter u in verwarring?

MEVR: - Nee. Ze hebben een vrij “beperkte” zichtbaarheid. Ze worden nergens anders gebruikt behalve hier (behalve de interne onderdelen van deze klasse), en het is erg compact: er zijn slechts 7 regels voor nodig.

В: – Op de een of andere manier is het nog steeds niet intuïtief...

MEVR: - Nee, nee, dit is een echte code! Het gaat niet om stijl. Het is gewoon zo'n utilitaire, heel kleine klasse - slechts 3 velden binnen de klas...

Michail Salosin. Golang-bijeenkomst. Gebruik Go in de backend van de Look+ applicatie

MEVR: – Over het algemeen veranderen alle gegevens die worden gesynchroniseerd met klanten (seizoenswedstrijden, spelers) niet. Grofweg gezegd, als we een andere sport maken waarin we de wedstrijd moeten veranderen, zullen we gewoon alles in de nieuwe versie van de client meenemen en zullen de oude versies van de client worden verbannen.

В: – Zijn er pakketten voor afhankelijkheidsbeheer van derden?

MEVR: – We gebruikten go-dep.

В: – Er stond iets over video in het onderwerp van het rapport, maar er stond niets in het rapport over video.

MEVR: – Nee, ik heb niets in het onderwerp over de video. Het heet "Look+" - dat is de naam van de applicatie.

В: – Je zei dat het naar klanten wordt gestreamd?

MEVR: – We waren niet betrokken bij het streamen van video. Dit is geheel door Megafon gedaan. Ja, ik heb niet gezegd dat de applicatie MegaFon was.

MEVR: – Go – voor het verzenden van alle gegevens – over de score, over wedstrijdevenementen, statistieken... Go is de volledige backend voor de applicatie. De klant moet ergens vandaan weten welke link hij voor de speler moet gebruiken, zodat de gebruiker de wedstrijd kan bekijken. We hebben links naar video's en streams die zijn voorbereid.

Sommige advertenties 🙂

Bedankt dat je bij ons bent gebleven. Vind je onze artikelen leuk? Wil je meer interessante inhoud zien? Steun ons door een bestelling te plaatsen of door vrienden aan te bevelen, cloud VPS voor ontwikkelaars vanaf $ 4.99, een unieke analoog van servers op instapniveau, die door ons voor u is uitgevonden: De hele waarheid over VPS (KVM) E5-2697 v3 (6 kernen) 10 GB DDR4 480 GB SSD 1 Gbps vanaf $ 19 of hoe een server te delen? (beschikbaar met RAID1 en RAID10, tot 24 cores en tot 40GB DDR4).

Dell R730xd 2x goedkoper in Equinix Tier IV datacenter in Amsterdam? Alleen hier 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV vanaf $199 in Nederland! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - vanaf $99! Lees over Hoe infrastructuur corp te bouwen. klasse met het gebruik van Dell R730xd E5-2650 v4-servers ter waarde van 9000 euro voor een cent?

Bron: www.habr.com

Voeg een reactie