Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Mikhail Salosin (nedan – MS): - Hej alla! Mitt namn Ă€r Michael. Jag jobbar som backend-utvecklare pĂ„ MC2 Software, och jag kommer att prata om att anvĂ€nda Go i backend av Look+-mobilapplikationen.

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Är det nĂ„gon hĂ€r som gillar hockey?

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

DÄ Àr denna applikation för dig. Den Àr för Android och iOS och anvÀnds för att titta pÄ sÀndningar av olika sportevenemang online och inspelade. Applikationen innehÄller ocksÄ olika statistik, textsÀndningar, tabeller för konferenser, turneringar och annan information som Àr anvÀndbar för fansen.

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

OcksÄ i applikationen finns det nÄgot som videoögonblick, det vill sÀga du kan se de viktigaste ögonblicken i matcher (mÄl, slagsmÄl, skjutningar, etc.). Om du inte vill se hela sÀndningen kan du bara se de mest intressanta.

Vad anvÀnde du i utvecklingen?

Huvuddelen skrevs i Go. API:et som mobila klienter kommunicerade med skrevs i Go. En tjÀnst för att skicka pushnotiser till mobiltelefoner skrevs ocksÄ i Go. Vi var ocksÄ tvungna att skriva vÄr egen ORM, som vi kanske pratar om nÄgon gÄng. NÄvÀl, nÄgra smÄ tjÀnster skrevs i Go: Àndra storlek och ladda bilder för redaktörerna...

Vi anvÀnde PostgreSQL som databas. RedaktörsgrÀnssnittet skrevs i Ruby on Rails med ActiveAdmin-pÀrlan. Att importera statistik frÄn en statistikleverantör skrivs ocksÄ i Ruby.

För system-API-tester anvÀnde vi Python unittest. Memcached anvÀnds för att strypa API-betalningsanrop, "Chef" anvÀnds för att kontrollera konfigurationen, Zabbix anvÀnds för att samla in och övervaka intern systemstatistik. Graylog2 Àr för att samla in loggar, Slate Àr API-dokumentation för klienter.

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Val av protokoll

Det första problemet vi stötte pÄ: vi behövde vÀlja ett protokoll för interaktion mellan backend- och mobilklienter, baserat pÄ följande punkter...

  • Det viktigaste kravet: data om klienter mĂ„ste uppdateras i realtid. Det vill sĂ€ga att alla som just nu tittar pĂ„ sĂ€ndningen ska fĂ„ uppdateringar nĂ€stan direkt.
  • För att förenkla saker och ting antog vi att data som Ă€r synkroniserad med klienter inte raderas, utan döljs med hjĂ€lp av speciella flaggor.
  • Alla typer av sĂ€llsynta förfrĂ„gningar (som statistik, lagsammansĂ€ttningar, lagstatistik) erhĂ„lls av vanliga GET-förfrĂ„gningar.
  • Dessutom mĂ„ste systemet enkelt stödja 100 tusen anvĂ€ndare samtidigt.

Baserat pÄ detta hade vi tvÄ protokollalternativ:

  1. Websockets. Men vi behövde inga kanaler frÄn klienten till servern. Vi behövde bara skicka uppdateringar frÄn servern till klienten, sÄ en websocket Àr ett överflödigt alternativ.
  2. Server-Sent Events (SSE) kom upp helt rÀtt! Det Àr ganska enkelt och tillfredsstÀller i princip allt vi behöver.

ServersÀnda hÀndelser

NÄgra ord om hur det hÀr fungerar...

Den körs ovanpÄ en http-anslutning. Klienten skickar en begÀran, servern svarar med Content-Type: text/event-stream och stÀnger inte anslutningen till klienten, utan fortsÀtter att skriva data till anslutningen:

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Data kan skickas i ett format som överenskommits med kunderna. I vÄrt fall skickade vi det i denna form: namnet pÄ den Àndrade strukturen (person, spelare) skickades till hÀndelsefÀltet och JSON med nya, Àndrade fÀlt för spelaren skickades till datafÀltet.

LÄt oss nu prata om hur sjÀlva interaktionen fungerar.

  • Det första klienten gör Ă€r att bestĂ€mma nĂ€r synkroniseringen med tjĂ€nsten senast utfördes: den tittar pĂ„ sin lokala databas och bestĂ€mmer datumet för den senaste Ă€ndringen som registrerades av den.
  • Den skickar en förfrĂ„gan med detta datum.
  • Som svar skickar vi honom alla uppdateringar som har intrĂ€ffat sedan det datumet.
  • Efter det gör den en anslutning till livekanalen och stĂ€nger inte förrĂ€n den behöver dessa uppdateringar:

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Vi skickar honom en lista med Àndringar: om nÄgon gör ett mÄl Àndrar vi matchresultatet, om han blir skadad skickas detta ocksÄ i realtid. SÄlunda fÄr kunder omedelbart uppdaterad data i matchhÀndelseflödet. Med jÀmna mellanrum, sÄ att klienten förstÄr att servern inte har dött, att ingenting hÀnt med den, skickar vi en tidsstÀmpel var 15:e sekund - sÄ att den vet att allt Àr i sin ordning och att det inte finns nÄgot behov av att Äteransluta.

Hur servas direktanslutningen?

  • Först och frĂ€mst skapar vi en kanal dĂ€r buffrade uppdateringar kommer att tas emot.
  • Efter det prenumererar vi pĂ„ den hĂ€r kanalen för att fĂ„ uppdateringar.
  • Vi stĂ€ller in rĂ€tt rubrik sĂ„ att klienten vet att allt Ă€r ok.
  • Skicka det första pinget. Vi registrerar helt enkelt den aktuella anslutningens tidsstĂ€mpel.
  • DĂ€refter lĂ€ser vi frĂ„n kanalen i en slinga tills uppdateringskanalen Ă€r stĂ€ngd. Kanalen fĂ„r med jĂ€mna mellanrum antingen den aktuella tidsstĂ€mpeln eller Ă€ndringar som vi redan skriver till öppna anslutningar.

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Det första problemet vi stötte pÄ var följande: för varje anslutning som öppnades med klienten skapade vi en timer som tickade en gÄng var 15:e sekund - det visar sig att om vi hade 6 tusen anslutningar öppna med en maskin (med en API-server), 6 tusen timers skapades. Detta ledde till att maskinen inte höll den erforderliga lasten. Problemet var inte sÄ uppenbart för oss, men vi fick lite hjÀlp och fixade det.

Som ett resultat kommer nu vÄr ping frÄn samma kanal som uppdateringen kommer frÄn.

Följaktligen finns det bara en timer som tickar en gÄng var 15:e sekund.

Det finns flera hjÀlpfunktioner hÀr - att skicka rubriken, pinga och sjÀlva strukturen. Det vill sÀga namnet pÄ tabellen (person, match, sÀsong) och informationen om denna post överförs hÀr:

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Mekanism för att skicka uppdateringar

Nu lite om var förÀndringarna kommer ifrÄn. Vi har flera personer, redaktörer, som tittar pÄ sÀndningen i realtid. De skapar alla hÀndelser: nÄgon blev utvisad, nÄgon blev skadad, nÄgon slags ersÀttare...

Med hjÀlp av ett CMS kommer data in i databasen. Efter detta meddelar databasen API-servrarna om detta med hjÀlp av Lyssna/Meddela-mekanismen. API-servrar skickar redan denna information till klienter. SÄledes har vi i princip bara ett fÄtal servrar anslutna till databasen och det finns ingen speciell belastning pÄ databasen, eftersom klienten inte interagerar direkt med databasen pÄ nÄgot sÀtt:

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

PostgreSQL: Lyssna/Meddela

Lyssna/Meddela-mekanismen i Postgres lÄter dig meddela hÀndelseprenumeranter att nÄgon hÀndelse har Àndrats - nÄgon post har skapats i databasen. För att göra detta skrev vi en enkel trigger och funktion:

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

NÀr vi infogar eller Àndrar en post anropar vi notify-funktionen pÄ data_updates-kanalen och skickar dit namnet pÄ tabellen och identifieraren för posten som Àndrades eller infogades.

För alla tabeller som mÄste synkroniseras med klienten definierar vi en trigger, som efter Àndring/uppdatering av en post anropar funktionen som anges pÄ bilden nedan.
Hur prenumererar API:et pÄ dessa Àndringar?

En Fanout-mekanism skapas - den skickar meddelanden till klienten. Den samlar in alla kundkanaler och skickar uppdateringar som den fÄtt genom dessa kanaler:

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

HÀr kontrollerar standard pq-biblioteket, som ansluter till databasen och sÀger att det vill lyssna pÄ kanalen (data_updates), att anslutningen Àr öppen och att allt Àr bra. Jag utelÀmnar felkontroll för att spara utrymme (det Àr farligt att inte kontrollera).

DĂ€refter stĂ€ller vi asynkront in Ticker, som skickar ett ping var 15:e sekund, och börjar lyssna pĂ„ kanalen vi prenumererar pĂ„. Om vi ​​fĂ„r en ping publicerar vi denna ping. Om vi ​​fĂ„r nĂ„gon form av bidrag publicerar vi detta till alla prenumeranter pĂ„ denna Fanout.

Hur fungerar Fan-out?

PÄ ryska översÀtts detta som "splitter". Vi har ett objekt som registrerar prenumeranter som vill fÄ lite uppdateringar. Och sÄ snart en uppdatering kommer till det hÀr objektet distribuerar den denna uppdatering till alla sina prenumeranter. Enkelt nog:

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Hur det implementeras i Go:

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Det finns en struktur, den synkroniseras med Mutexes. Den har ett fÀlt som sparar tillstÄndet för Fanouts anslutning till databasen, det vill sÀga den lyssnar för nÀrvarande och kommer att fÄ uppdateringar, samt en lista över alla tillgÀngliga kanaler - karta, vars nyckel Àr kanalen och strukturen i form av vÀrden (i huvudsak anvÀnds det inte pÄ nÄgot sÀtt).

TvÄ metoder - Ansluten och FrÄnkopplad - lÄter oss berÀtta för Fanout att vi har en anslutning till basen, den har dykt upp och att anslutningen till basen har brutits. I det andra fallet mÄste du koppla bort alla klienter och tala om för dem att de inte lÀngre kan lyssna pÄ nÄgonting och att de Äteransluter eftersom anslutningen till dem har stÀngts.

Det finns ocksÄ en prenumerationsmetod som lÀgger till kanalen till "lyssnarna":

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Det finns en Unsubscribe-metod, som tar bort kanalen frÄn lyssnarna om klienten kopplar bort, samt en Publiceringsmetod, som lÄter dig skicka ett meddelande till alla prenumeranter.

FrĂ„ga: – Vad sĂ€nds genom denna kanal?

FRÖKEN: – Modellen som har Ă€ndrats eller ping sĂ€nds (i huvudsak bara ett tal, heltal).

FRÖKEN: – Du kan skicka vad som helst, skicka vilken struktur som helst, publicera det – det blir bara till JSON och det Ă€r allt.

FRÖKEN: – Vi fĂ„r ett meddelande frĂ„n Postgres – det innehĂ„ller tabellnamn och identifierare. Baserat pĂ„ tabellnamnet och identifieraren fĂ„r vi den post vi behöver, och sedan skickar vi denna struktur för publicering.

Infrastruktur

Hur ser det hÀr ut ur ett infrastrukturperspektiv? Vi har 7 hÄrdvaruservrar: en av dem Àr helt dedikerad till databasen, de andra sex kör virtuella maskiner. Det finns 6 kopior av API:t: varje virtuell maskin med API körs pÄ en separat hÄrdvaruserver - detta för tillförlitligheten.

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Vi har tvĂ„ grĂ€nssnitt med Keepalved installerat för att förbĂ€ttra tillgĂ€ngligheten, sĂ„ att om nĂ„got hĂ€nder kan den ena grĂ€nsen ersĂ€tta den andra. Dessutom – tvĂ„ exemplar av CMS.

Det finns Àven en statistikimportör. Det finns en DB-slav frÄn vilken sÀkerhetskopior görs med jÀmna mellanrum. Det finns Pigeon Pusher, en applikation som skickar push-meddelanden till kunder, sÄvÀl som infrastruktursaker: Zabbix, Graylog2 och Chef.

Faktum Àr att denna infrastruktur Àr överflödig, eftersom 100 tusen kan serveras med fÀrre servrar. Men det fanns jÀrn - vi anvÀnde det (vi fick höra att det var möjligt - varför inte).

Fördelar med Go

Efter att vi arbetat med den hÀr applikationen framkom sÄ uppenbara fördelar med Go.

  • Coolt http-bibliotek. Med den kan du skapa ganska mycket ur lĂ„dan.
  • Dessutom kanaler som gjorde det möjligt för oss att mycket enkelt implementera en mekanism för att skicka meddelanden till kunder.
  • Det underbara Race-detektorn tillĂ€t oss att eliminera flera kritiska buggar (staging-infrastruktur). Allt som fungerar pĂ„ iscensĂ€ttning lanseras, sammanstĂ€llt med Race-nyckeln; och vi kan följaktligen titta pĂ„ iscensĂ€ttningsinfrastrukturen för att se vilka potentiella problem vi har.
  • Minimalism och enkelhet i sprĂ„ket.

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

Vi söker utvecklare! Om nÄgon vill, snÀlla.

frÄgor

FrĂ„ga frĂ„n publiken (nedan – B): – Det verkar som om du missade en viktig punkt angĂ„ende Fan-out. Har jag rĂ€tt i att förstĂ„ att nĂ€r man skickar ett svar till en klient sĂ„ blockerar man om klienten inte vill lĂ€sa?

FRÖKEN: – Nej, vi blockerar inte. För det första har vi allt detta bakom nginx, det vill sĂ€ga det finns inga problem med lĂ„ngsamma klienter. För det andra har klienten en kanal med en buffert - faktiskt kan vi lĂ€gga upp till hundra uppdateringar dĂ€r... Om vi ​​inte kan skriva till kanalen sĂ„ raderar den den. Om vi ​​ser att kanalen Ă€r blockerad stĂ€nger vi helt enkelt kanalen, och det Ă€r det - klienten kommer att Ă„teransluta om nĂ„got problem uppstĂ„r. DĂ€rför finns det i princip ingen blockering hĂ€r.

PÅ: – Kunde det inte vara möjligt att omedelbart skicka en post till Listen/Notify, och inte en identifieringstabell?

FRÖKEN: – Lyssna/Meddela har en grĂ€ns pĂ„ 8 tusen byte pĂ„ förladdningen den skickar. I princip skulle det vara möjligt att skicka om vi hade att göra med en liten mĂ€ngd data, men det verkar för mig att det hĂ€r sĂ€ttet [sĂ€ttet vi gör det] helt enkelt Ă€r mer tillförlitligt. BegrĂ€nsningarna finns i Postgres sjĂ€lv.

PÅ: – FĂ„r kunderna uppdateringar om matcher som de inte Ă€r intresserade av?

FRÖKEN: – Generellt sett, ja. Som regel pĂ„gĂ„r det 2-3 matcher parallellt, och Ă€ven dĂ„ ganska sĂ€llan. Om en klient tittar pĂ„ nĂ„got, sĂ„ brukar han titta pĂ„ matchen som pĂ„gĂ„r. Sedan har klienten en lokal databas i vilken alla dessa uppdateringar lĂ€ggs ihop, och Ă€ven utan internetanslutning kan klienten se alla tidigare matchningar som han har uppdateringar för. I huvudsak synkroniserar vi vĂ„r databas pĂ„ servern med kundens lokala databas sĂ„ att han kan arbeta offline.

PÅ: – Varför gjorde du din egen ORM?

Alexey (en av utvecklarna av Look+): – PĂ„ den tiden (det var ett Ă„r sedan) var det fĂ€rre ORM Ă€n nu, nĂ€r det Ă€r ganska mĂ„nga. Min favoritsak med de flesta ORMs dĂ€r ute Ă€r att de flesta av dem körs pĂ„ tomma grĂ€nssnitt. Det vill sĂ€ga, metoderna i dessa ORM:er Ă€r redo att ta pĂ„ sig vad som helst: en struktur, en strukturpekare, ett nummer, nĂ„got helt irrelevant...

VÄr ORM genererar strukturer baserade pÄ datamodellen. Jag sjÀlv. Och dÀrför Àr alla metoder konkreta, anvÀnder inte reflektion etc. De accepterar strukturer och förvÀntar sig att anvÀnda de strukturerna som kommer.

PÅ: – Hur mĂ„nga deltog?

FRÖKEN: – I det inledande skedet deltog tvĂ„ personer. Vi började nĂ„gonstans i juni, och i augusti var huvuddelen klar (första versionen). Det slĂ€pptes i september.

PÅ: – DĂ€r du beskriver SSE anvĂ€nder du inte timeout. Varför Ă€r det sĂ„?

FRÖKEN: – För att vara Ă€rlig Ă€r SSE fortfarande ett html5-protokoll: SSE-standarden Ă€r utformad för att kommunicera med webblĂ€sare, sĂ„ vitt jag förstĂ„r. Den har ytterligare funktioner sĂ„ att webblĂ€sare kan Ă„teransluta (och sĂ„ vidare), men vi behöver dem inte, eftersom vi hade klienter som kunde implementera vilken logik som helst för att ansluta och ta emot information. Vi gjorde inte SSE, utan snarare nĂ„got liknande SSE. Detta Ă€r inte sjĂ€lva protokollet.
Det fanns inget behov. SÄvitt jag förstÄr implementerade klienter anslutningsmekanismen nÀstan frÄn början. De brydde sig inte riktigt.

PÅ: – Vilka ytterligare verktyg anvĂ€nde du?

FRÖKEN: – Vi anvĂ€nde mest aktivt govet och golint för att göra stilen enhetlig, sĂ„vĂ€l som gofmt. Inget annat anvĂ€ndes.

PÅ: – Vad anvĂ€nde du för att felsöka?

FRÖKEN: – Felsökningen utfördes till stor del med hjĂ€lp av tester. Vi anvĂ€nde ingen debugger eller GOP.

PÅ: – Kan du returnera bilden dĂ€r Publiceringsfunktionen Ă€r implementerad? Förvirrar variabelnamn med en bokstav dig?

FRÖKEN: - Nej. De har en ganska "snĂ€v" synlighet. De anvĂ€nds inte nĂ„gon annanstans förutom hĂ€r (förutom den interna delen av denna klass), och den Ă€r vĂ€ldigt kompakt - den tar bara 7 rader.

PÅ: – PĂ„ nĂ„got sĂ€tt Ă€r det fortfarande inte intuitivt...

FRÖKEN: – Nej, nej, det hĂ€r Ă€r en riktig kod! Det handlar inte om stil. Det Ă€r bara en sĂ„dan utilitaristisk, vĂ€ldigt liten klass - bara 3 fĂ€lt i klassen...

Mikhail Salosin. Golang Meetup. AnvÀnda Go i baksidan av Look+-applikationen

FRÖKEN: – I stort sett förĂ€ndras inte all data som synkroniseras med kunder (sĂ€songsmatcher, spelare). Grovt sett, om vi gör en annan sport dĂ€r vi behöver Ă€ndra matchen, kommer vi helt enkelt att ta hĂ€nsyn till allt i den nya versionen av klienten, och de gamla versionerna av klienten kommer att förbjudas.

PÅ: – Finns det nĂ„gra paket för beroendehantering frĂ„n tredje part?

FRÖKEN: – Vi anvĂ€nde go dep.

PÅ: – Det stod nĂ„got om video i Ă€mnet för rapporten, men det stod ingenting om video i rapporten.

FRÖKEN: – Nej, jag har inget i Ă€mnet om videon. Det heter "Look+" - det Ă€r namnet pĂ„ applikationen.

PÅ: – Du sa att det streamas till kunder?

FRÖKEN: – Vi var inte involverade i strömmande video. Detta gjordes helt av Megafon. Ja, jag sa inte att applikationen var MegaFon.

FRÖKEN: – Go – för att skicka all data – om poĂ€ng, om matchhĂ€ndelser, statistik... Go Ă€r hela backend för applikationen. Klienten mĂ„ste nĂ„gonstans veta vilken lĂ€nk som ska anvĂ€ndas för spelaren sĂ„ att anvĂ€ndaren kan se matchen. Vi har lĂ€nkar till filmer och streams som har förberetts.

NĂ„gra annonser 🙂

Tack för att du stannar hos oss. Gillar du vĂ„ra artiklar? Vill du se mer intressant innehĂ„ll? Stöd oss ​​genom att lĂ€gga en bestĂ€llning eller rekommendera till vĂ€nner, moln VPS för utvecklare frĂ„n $4.99, en unik analog av ingĂ„ngsservrar, som uppfanns av oss för dig: Hela sanningen om VPS (KVM) E5-2697 v3 (6 kĂ€rnor) 10GB DDR4 480GB SSD 1Gbps frĂ„n $19 eller hur delar man en server? (tillgĂ€nglig med RAID1 och RAID10, upp till 24 kĂ€rnor och upp till 40 GB DDR4).

Dell R730xd 2 gÄnger billigare i Equinix Tier IV datacenter i Amsterdam? Bara hÀr 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV frÄn $199 i NederlÀnderna! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - frÄn $99! LÀs om Hur man bygger infrastructure corp. klass med anvÀndning av Dell R730xd E5-2650 v4-servrar vÀrda 9000 XNUMX euro för en slant?

KĂ€lla: will.com

LĂ€gg en kommentar