Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Mikhail Salosin (hĂ€refter – MS): – Hej alla! Jag heter Mikhail. Jag arbetar som backend-utvecklare pĂ„ företaget MC2 Software, och jag kommer att berĂ€tta om anvĂ€ndningen av Go i backend för mobilapplikationen "Smotri+".

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

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

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

DÄ Àr den hÀr appen för dig. Den finns för Android och iOS och anvÀnds för att titta pÄ sÀndningar av olika sportevenemang online och i efterhand. I appen finns Àven olika statistik, textbaserade sÀndningar, tabeller för konferenser, turneringar och annan information som Àr anvÀndbar för fans.

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Appen har ocksÄ en funktion som kallas videomoments, vilket innebÀr att man kan se höjdpunkter frÄn matcherna (mÄl, slagsmÄl, straffar osv.). Om du inte vill se hela sÀndningen kan du bara titta pÄ det mest intressanta.

Vad anvÀnde vi i utvecklingen?

Största delen var skriven i Go. Den API som mobilklienterna kommunicerade med var skriven i Go. Även tjĂ€nsten för att skicka push-notifikationer till mobiler var skriven i Go. Dessutom var vi tvungna att skriva vĂ„r egen ORM, som vi kanske berĂ€ttar mer om nĂ„gon gĂ„ng. Och vissa mindre tjĂ€nster har skrivits i Go: resizing och uppladdning av bilder för redaktörernas sida


För databasen anvÀnde vi PostgreSQL. GrÀnssnittet för redaktörerna var skrivet i Ruby on Rails med hjÀlp av gemet (gem) ActiveAdmin. Statistikimporten frÄn statistikleverantören var ocksÄ skriven i Ruby.

För systemtester av API:n anvÀnde vi Python's unittest. Memcached anvÀnds för att throttla API-betalningar, Chef för konfigurationshantering, Zabbix för insamling och övervakning av interna statistiska data i systemet. Graylog2 anvÀnds för att samla loggar, och Slate Àr API-dokumentationen för kunder.

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Val av protokoll

Det första problemet vi stötte pĂ„: vi behövde vĂ€lja ett protokoll för interaktionen mellan backend och mobilklienterna, baserat pĂ„ följande punkter


  • Det viktigaste kravet: data pĂ„ klienterna mĂ„ste uppdateras i realtid. Det vill sĂ€ga alla som just nu tittar pĂ„ sĂ€ndningen ska fĂ„ uppdateringar nĂ€stan omedelbart.
  • För enkelhetens skull antog vi att de data som synkroniseras med klienterna inte tas bort utan döljs med hjĂ€lp av speciella flaggor.
  • SĂ€llsynta förfrĂ„gningar (som statistik, laguppstĂ€llningar, lagstatistik) görs genom vanliga GET-förfrĂ„gningar.
  • Dessutom skulle systemet kunna hantera 100 000 anvĂ€ndare samtidigt utan problem.

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Ä websockets var överflödiga.
  2. Server-Sent Events (SSE) passade perfekt! Det Àr tillrÀckligt enkelt och uppfyller i princip allt vi behöver.

Server-Sent Events

NÄgra ord om hur denna sak fungerar...

Den fungerar över http-anslutningen. Klienten skickar en förfrÄgan, servern svarar med Content-Type: text/event-stream och stÀnger inte anslutningen till klienten, utan fortsÀtter att skriva data i anslutningen:

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Data kan skickas i det format som överenskommits med klienterna. I vĂ„rt fall skickade vi i följande form: i fĂ€ltet event angavs namnet pĂ„ den Ă€ndrade strukturen (person, spelare), och i data-fĂ€ltet – JSON med de nya, Ă€ndrade fĂ€lten för spelaren.

Nu om hur sjÀlva interaktionen fungerar.

  • Först och frĂ€mst faststĂ€ller klienten nĂ€r den senast synkroniserade med tjĂ€nsten: den tittar i sin lokala databas och bestĂ€mmer datumet för den senaste Ă€ndringen som registrerades hos den.
  • Den skickar en förfrĂ„gan med detta datum.
  • Som svar skickar vi all uppdatering som har skett efter detta datum.
  • DĂ€refter upprĂ€ttar den en anslutning till live-kanalen och stĂ€nger den inte förrĂ€n den inte lĂ€ngre behöver dessa uppdateringar:

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Vi skickar en lista över förĂ€ndringar: om nĂ„gon gör mĂ„l förĂ€ndrar vi matchresultatet, om nĂ„gon skadar sig – det skickas ocksĂ„ i realtid. PĂ„ sĂ„ sĂ€tt fĂ„r klienterna omedelbart aktuella data i matchens hĂ€ndelseflöde. Periodiskt, för att klienten ska förstĂ„ att servern inte har kraschat och att ingenting har hĂ€nt med den, skickar vi varje 15:e sekund en tidsstĂ€mpel – sĂ„ att den vet att allt Ă€r okej och att det inte behövs nĂ„gon Ă„teranslutning.

Hur underhÄlls live-anslutningen?

  • Först skapar vi en kanal som kommer att ta emot uppdateringar med en buffert.
  • Efter det registrerar vi denna kanal för att ta emot uppdateringar.
  • Vi sĂ€tter rĂ€tt rubrik sĂ„ att klienten vet att allt Ă€r ok.
  • Vi skickar den första ping. Vi skriver helt enkelt ned den aktuella tidsstĂ€mpeln för anslutningen.
  • Efter detta lĂ€ser vi i en loop frĂ„n kanalen sĂ„ lĂ€nge uppdateringskanalen inte Ă€r stĂ€ngd. Det kommer periodiskt antingen en aktuell tidsstĂ€mpel eller förĂ€ndringar som vi redan skriver till öppna anslutningar.

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Det första problemet vi stötte pĂ„ var följande: för varje öppen anslutning med en klient skapade vi en timer som tickade var 15:e sekund – vilket innebĂ€r att om vi hade 6000 anslutningar till en maskin (med en API-server) skapades 6000 timers. Detta ledde till att maskinen inte höll den nödvĂ€ndiga belastningen. Problemet var inte sĂ„ uppenbart för oss, men vi fick lite hjĂ€lp och löste det.

I slutÀndan kommer nu pinget frÄn samma kanal som uppdateringen kommer ifrÄn.

DĂ€rmed finns det bara en timer som tickar var 15:e sekund.

HĂ€r finns nĂ„gra hjĂ€lpfunktioner – att skicka header, ping och sjĂ€lva strukturen. Det vill sĂ€ga, hĂ€r skickas namnet pĂ„ tabellen (person, match, sĂ€song) och sjĂ€lva informationen om denna post:

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Mekanismen för att skicka uppdateringar

Nu lite om var förĂ€ndringarna kommer ifrĂ„n. Vi har flera personer, redaktörer, som i realtid ser pĂ„ sĂ€ndningen. De skapar alla hĂ€ndelser: nĂ„gon har blivit utvisad, nĂ„gon har skadat sig, nĂ„got byte


Genom CMS gÄr datan in i databasen. Efter det meddelar databasen via mekanismen Listen/Notify API-servrarna om detta. API-servrarna skickar sedan denna information till klienterna. PÄ sÄ sÀtt Àr det i praktiken bara nÄgra servrar som Àr anslutna till databasen och det finns ingen sÀrskild belastning pÄ databasen eftersom klienten pÄ inget sÀtt interagerar direkt med databasen:

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

PostgreSQL: Listen/Notify

Mekanismen Listen/Notify i "Postgres" gör det möjligt att informera prenumeranter om hĂ€ndelser om att nĂ„gon hĂ€ndelse har förĂ€ndrats – det har skapats en post i databasen. För detta skrev vi en enkel trigger och funktion:

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Vid insert eller Àndring av en post anropar vi funktionen notify pÄ kanalen data_updates, dit vi skickar namnet pÄ tabellen och identifieraren för den post som har Àndrats eller lagts till.

För alla tabeller som ska synkroniseras med klienten definierar vi en trigger som efter förÀndring/uppdatering av posten anropar funktionen som anges pÄ bilden nedan.
Hur prenumererar API:t pÄ dessa förÀndringar?

En fanout-mekanism skapas – den distribuerar meddelanden till kunder. Den samlar alla kundkanaler och skickar de uppdateringar som den fĂ„tt genom dessa kanaler:

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

HÀr Àr standardbiblioteket pq, som ansluter till databasen och sÀger att den vill lyssna pÄ kanalen (data_updates), kontrollerar att anslutningen Àr öppen och att allt fungerar. Jag hoppar över felkontrollen för att spara utrymme (att inte kontrollera Àr riskabelt).

Sedan stÀller vi in en Ticker asynkront, som skickar ping var 15:e sekund, och börjar lyssna pÄ kanalen som vi prenumererat pÄ. Om vi fÄr en ping, publicerar vi den pinget. Om vi fÄr nÄgon post, publicerar vi denna post till alla prenumeranter av denna Fanout.

Hur fungerar Fan-out?

PÄ svenska översÀtts det som "förgrenare". Vi har ett objekt som registrerar prenumeranter som vill fÄ nÄgra uppdateringar. SÄ snart en uppdatering till detta objekt kommer, distribuerar det denna uppdatering till alla sina prenumeranter. Ganska enkelt:

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Hur detta implementeras i Go:

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Det finns en struktur som synkroniseras med hjĂ€lp av Mutexar. Den har ett fĂ€lt som sparar anslutningens tillstĂ„nd mellan Fanout och databasen, dvs. just nu lyssnar den och kommer att ta emot uppdateringar, samt en lista över alla befintliga kanaler – en map, dĂ€r nyckeln Ă€r kanalen och struct i form av vĂ€rden (egentligen anvĂ€nds den inte pĂ„ nĂ„got sĂ€tt).

TvĂ„ metoder – Connected och Disconnected – gör att Fanout vet att vi har en anslutning till databasen, den har skapats och att anslutningen till databasen har brutits. I det andra fallet behöver alla klienter kopplas bort och meddelas att de inte lĂ€ngre kan lyssna och att de bör Ă„teransluta, eftersom anslutningen till dem har stĂ€ngts.

Det finns ocksÄ en metod Subscribe som lÀgger till en kanal i "lyssnare":

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Det finns en metod Unsubscribe som tar bort kanalen frÄn de lyssnande, om klienten har kopplat bort sig, samt en metod Publish som gör att meddelanden kan skickas till alla prenumeranter.

FrĂ„ga: – Vad överförs via denna kanal?

MS: – En modell som har Ă€ndrats eller en ping (egentligen bara ett nummer, heltal).

MS: – Man kan skicka vad som helst, vilken struktur som helst, publicera – den omvandlas helt enkelt till JSON och det Ă€r allt.

MS: – Vi fĂ„r en notifiering frĂ„n "Postgres" – den innehĂ„ller namnet pĂ„ tabellen och identifieraren. Genom tabellens namn fĂ„r vi den registrering vi behöver via identifieraren, och denna struktur skickar vi för publicering.

Infrastruktur

Hur ser det ut ur ett infrastruktursynpunkt? Vi har 7 hĂ„rdvaruserverar: en av dem Ă€r helt dedikerad för databasen, pĂ„ de andra sex körs virtuella maskiner. Det finns 6 kopior av API:et: varje virtuell maskin med API körs pĂ„ en separat hĂ„rdvaruserver – detta för pĂ„litlighet.

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Vi har tvÄ frontend, pÄ vilka Keepalived Àr installerat för att förbÀttra tillgÀngligheten, sÄ att i hÀndelse av nÄgot kan en frontend ersÀtta den andra. Dessutom finns det tvÄ kopior av CMS.

Det finns ocksĂ„ en statistikimportör. Det finns en DB Slave, frĂ„n vilken sĂ€kerhetskopior görs periodiskt. Det finns Pigeon Pusher – den applikationen som skickar push-notiser till kunderna, samt infrastrukturella saker: Zabbix, Graylog2 och Chef.

Denna infrastruktur Ă€r faktiskt överflödig, eftersom man kan hantera 100 000 med fĂ€rre servrar. Men vi hade hĂ„rdvaran – vi anvĂ€nde den (vi fick höra att det var okej – varför inte).

Fördelar med Go

Efter att vi har arbetat med denna applikation har dessa uppenbara fördelar med Go framtrÀtt.

  • En fantastisk http-bibliotek. Med hjĂ€lp av det kan man skapa ganska mycket direkt "ur lĂ„dan".
  • Dessutom kanaler som gjorde det mycket enkelt för oss att implementera mekanismen för att skicka notifieringar till kunder.
  • Det underbara verktyget Race detector gjorde att vi kunde undanröja flera kritiska buggar (staging-infrastrukturen). Allt som fungerar pĂ„ staging körs och kompileras med Race-nyckeln; och vi kan dĂ€rmed se pĂ„ staging-infrastrukturen vilka potentiella problem vi har.
  • Minimalism och enkelhet i sprĂ„ket.

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

Vi söker utvecklare! Om nĂ„gon Ă€r intresserad – vĂ€nligen hör av er.

FrÄgor

FrĂ„ga frĂ„n publiken (vidare – F): – Jag verkar ha missat en viktig punkt angĂ„ende Fan-out. FörstĂ„r jag rĂ€tt att nĂ€r ni skickar svar till kunden, blockeras ni om kunden inte vill lĂ€sa?

MS: – Nej, vi blockerar oss inte. För det första Ă€r allt detta bakom nginx, vilket innebĂ€r att vi inte har nĂ„gra problem med lĂ„ngsamma kunder. För det andra har kunden en buffertkanal – i princip kan vi lĂ€gga upp till hundra uppdateringar dĂ€r... Om vi inte kan skriva till kanalen, sĂ„ tar den bort den. Om vi ser att kanalen har blockerat, stĂ€nger vi helt enkelt kanalen, och allt – kunden kommer att Ă„teransluta om det uppstĂ„r nĂ„got problem. DĂ€rför uppstĂ„r det i princip inga blockeringar hĂ€r.

L: – Kunde vi inte skicka en Listen/Notify-post direkt, istĂ€llet för identifikatorn för tabellen?

MS: – Listen/Notify har en begrĂ€nsning pĂ„ 8 000 byte pĂ„ preload som den skickar. I princip skulle vi kunna skicka det om vi hade att göra med en liten mĂ€ngd data, men jag tycker att det Ă€r helt enkelt mer pĂ„litligt att göra som vi gör. BegrĂ€nsningarna ligger i sjĂ€lva "PostgreSQL".

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

MS: – I allmĂ€nhet, ja. Som regel sker det 2-3 matcher parallellt, och det Ă€r ganska sĂ€llsynt. Om kunden tittar pĂ„ nĂ„got, Ă€r det vanligtvis den matchen som pĂ„gĂ„r just nu. Dessutom har kunden en lokal databas dĂ€r alla dessa uppdateringar lagras, och Ă€ven utan internetanslutning kan kunden se alla passerade matcher som han har uppdateringar för. I princip synkroniserar vi vĂ„r databas pĂ„ servern med kundens lokala databas sĂ„ att han kan arbeta offline.

L: – Varför gjorde ni er egen ORM?

Alexey (en av utvecklarna av "Smotri+"): – Vid den tiden (det var för ett Ă„r sedan) fanns det fĂ€rre ORM:er Ă€n det finns nu, nĂ€r det finns ganska mĂ„nga. Av de flesta existerande ORM:er gillar jag mest att de flesta av dem arbetar med tomma grĂ€nssnitt. Det betyder att metoderna i dessa ORM:er Ă€r redo att ta emot vad som helst: en struktur, en strukturpekare, ett nummer, nĂ„got helt orelaterat...

VÄr ORM genererar strukturer baserat pÄ datamodellen. SjÀlv. SÄ alla metoder Àr specifika och anvÀnder inte reflektion osv. De tar emot strukturer och förvÀntar sig att anvÀnda de strukturer som tas emot.

L: – Hur mĂ„nga personer deltog?

MS: – I det inledande skedet deltog tvĂ„ personer. NĂ„gon gĂ„ng i juni började vi, i augusti var den största delen klar (den första versionen). I september var det release.

L: – DĂ€r du beskriver SSE, anvĂ€nder du ingen timeout. Varför sĂ„?

MS: – Om vi talar klart och tydligt, sĂ„ Ă€r SSE Ă€ndĂ„ ett html5-protokoll: standarden för SSE Ă€r avsedd för kommunikation med webblĂ€sare, sĂ„vitt jag förstĂ„r. Den har ytterligare funktioner för att webblĂ€sare ska kunna Ă„teransluta (och sĂ„ vidare), men vi behöver dem inte, eftersom vi hade kunder som kunde implementera vilken anslutningslogik som helst och hĂ€mta information. Vi gjorde snarare nĂ„got som liknar SSE Ă€n sjĂ€lva protokollet.
Det fanns ingen anledning. SÄvitt jag förstÄr implementerade kunderna anslutningsmekanismen praktiskt taget frÄn början. Det spelade i princip ingen roll för dem.

L: – Vilka ytterligare verktyg anvĂ€nde ni?

MS: – Vi anvĂ€nde mest aktivt govet och golint för att stilen skulle vara enhetlig, samt gofmt. Inga andra verktyg anvĂ€ndes.

L: – Hur genomförde ni felsökning?

MS: – Felsökningen skedde i stort sett med hjĂ€lp av tester. Vi anvĂ€nde ingen debugger och inget GOP.

L: – Kan du Ă„tervĂ€nda till den bild dĂ€r funktionen Publish Ă€r implementerad? Stör dig inte pĂ„ variabler med enstaviga namn?

MS: – Nej. De har en ganska "snĂ€v" synlighetsomrĂ„de. De anvĂ€nds ingen annanstans Ă€n hĂ€r (förutom inuti denna klass), och den Ă€r mycket kompakt – bara 7 rader lĂ„ng.

L: – Det kĂ€nns Ă€ndĂ„ inte intuitivt...

MS: – Nej, nej, det hĂ€r Ă€r verklig kod! Det handlar inte om stil. Det Ă€r helt enkelt en sĂ„n utilitaristisk, mycket liten klass – bara 3 fĂ€lt inuti klassen...

Mikhail Salosin. Golang Meetup. AnvÀndning av Go i backend för appen "Sveti+"

MS: – I stort sett Ă€ndras alla de data som synkroniseras med kunderna (sĂ€songsmatcher, spelare) inte. Grovt sagt, om vi ska göra nĂ„gon annan sport dĂ€r vi behöver Ă€ndra matchen, sĂ„ tar vi bara hĂ€nsyn till det i den nya versionen av klienten, och de gamla versionerna av klienten kommer att blockeras.

L: – Finns det nĂ„gra externa paket för att hantera beroenden?

MS: – Vi anvĂ€nde go dep.

L: – I föredragets Ă€mne fanns det nĂ„got om video, men det finns inget om video i föredraget.

MS: – Nej, jag har inget om video i mitt Ă€mne. Det heter "Svet +" – sĂ„ heter appen.

L: – Du sa att det strömmas till kunderna?..

MS: – Vi sysslade inte med strömmande video. Det gjorde helt och hĂ„llet "MegaFon". Ja, jag nĂ€mnde inte att appen Ă€r frĂ„n MegaFon.

MS: – Go – för att skicka all data – om kontot, om matchhĂ€ndelser, statistik
 Go – det Ă€r helt backend för applikationen. Klienten mĂ„ste nĂ„gonstans fĂ„ veta vilken lĂ€nk som ska anvĂ€ndas för spelaren sĂ„ att anvĂ€ndaren kan se matchen. Vi har lĂ€nkar till videor och strömmar som Ă€r förberedda.

Spela video

Lite reklam 🙂

Tack för att ni stannar kvar hos oss. Gillar ni vÄra artiklar? Vill ni se fler intressanta material? Stöd oss genom att göra en bestÀllning eller rekommendera oss till era vÀnner, moln-VPS för utvecklare frÄn $4,99, en unik motsvarighet till entry-level servrar som vi har skapat för er: Allt om VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps frÄn $19 eller hur man korrekt delar en server? (alternativ med RAID1 och RAID10 finns tillgÀngliga, upp till 24 kÀrnor och upp till 40GB DDR4).

Dell R730xd Ă€r tvĂ„ gĂ„nger billigare i datacentret Equinix Tier IV i Amsterdam? Bara hos oss 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100TB 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 en företagsinfrastruktur med Dell R730xd E5-2650 v4-servrar som kostar 9000 euro för en brĂ„kdel?

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster