Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Mikhail Salosin (in seguitu - MS): - Salute à tutti ! Mi chjamu Michael. U travagliu cum'è sviluppatore di backend in MC2 Software, è parleraghju di utilizà Go in u backend di l'applicazione mobile Look+.

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Qualchissia quì piace u hockey?

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Allora sta applicazione hè per voi. Hè per Android è iOS è hè adupratu per fighjà trasmissioni di diversi avvenimenti sportivi in ​​linea è arregistrati. L'applicazione cuntene ancu diverse statistiche, trasmissioni di testu, tavule per cunferenze, tornei è altre informazioni utili per i fan.

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Ancu in l'applicazione ci hè una cosa cum'è i mumenti video, vale à dì chì pudete fighjà i mumenti più impurtanti di partite (ubiettivi, cumbattimenti, shootouts, etc.). Se ùn vulete micca vede tutta a trasmissione, pudete fighjà solu i più interessanti.

Chì avete usatu in u sviluppu?

A parte principale hè stata scritta in Go. L'API chì i clienti mobili anu cumunicatu cù hè stata scritta in Go. Un serviziu per mandà notificazioni push à i telefuni mobili hè statu ancu scrittu in Go. Avemu avutu ancu à scrive u nostru propiu ORM, chì pudemu parlà un ghjornu. Ebbè, alcuni picculi servizii sò stati scritti in Go: ridimensionà è carica l'imaghjini per l'editori...

Avemu usatu PostgreSQL cum'è a basa di dati. L'interfaccia di l'editore hè stata scritta in Ruby on Rails cù a gemma ActiveAdmin. L'importazione di statistiche da un fornitore di statistiche hè ancu scrittu in Ruby.

Per i testi di l'API di u sistema, avemu usatu Python unittest. Memcached hè adupratu per accelerà e chjama di pagamentu API, "Chef" hè utilizatu per cuntrullà a cunfigurazione, Zabbix hè utilizatu per cullà è monitorà e statistiche di u sistema internu. Graylog2 hè per a cullizzioni di logs, Slate hè a documentazione API per i clienti.

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Selezzione di u protocolu

U primu prublema chì avemu scontru: avemu bisognu di sceglie un protokollu per l'interazzione trà u backend è i clienti mobili, basatu annantu à i seguenti punti...

  • U requisitu più impurtante: i dati nantu à i clienti devenu esse aghjurnati in tempu reale. Vale à dì, tutti quelli chì stanu à vede a trasmissione duveranu riceve l'aghjurnamenti quasi istantaneamente.
  • Per simplificà e cose, assumemu chì e dati chì sò sincronizati cù i clienti ùn sò micca sguassati, ma sò oculati cù bandieri speciali.
  • Ogni tipu di richieste rare (cum'è statistiche, cumpusizioni di squadra, statistiche di squadra) sò ottenute da richieste GET ordinarie.
  • In più, u sistema avia da sustene facilmente 100 mila utilizatori à u stessu tempu.

Basatu nantu à questu, avemu avutu duie opzioni di protokollu:

  1. Websockets. Ma ùn avemu micca bisognu di canali da u cliente à u servitore. Avemu bisognu solu di mandà l'aghjurnamenti da u servitore à u cliente, cusì un websocket hè una opzione redundante.
  2. L'avvenimenti mandati da u servitore (SSE) sò venuti ghjustu! Hè abbastanza simplice è basamente satisface tuttu ciò chì avemu bisognu.

Avvenimenti mandati da u servitore

Uni pochi parolle nantu à cumu funziona sta cosa...

Funziona sopra à una cunnessione http. U cliente manda una dumanda, u servitore risponde cù Content-Type: text/event-stream è ùn chjude micca a cunnessione cù u cliente, ma cuntinueghja à scrive dati à a cunnessione:

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

I dati ponu esse mandati in un furmatu accunsentutu cù i clienti. In u nostru casu, l'avemu mandatu in questa forma: u nome di a struttura cambiata (persona, ghjucadore) hè stata mandata à u campu di l'avvenimentu, è JSON cù novi campi cambiati per u ghjucatore hè statu mandatu à u campu di dati.

Avà parlemu di cumu funziona l'interazzione stessu.

  • A prima cosa chì u cliente fa hè determinà l'ultima volta chì a sincronizazione cù u serviziu hè stata realizata: guarda a so basa di dati lucale è determina a data di l'ultimu cambiamentu registratu da ellu.
  • Manda una dumanda cù questa data.
  • In risposta, li mandemu tutte l'aghjurnamenti chì sò accaduti da quella data.
  • Dopu questu, faci una cunnessione à u canali in diretta è ùn si chjude micca finu à chì hà bisognu di sti aghjurnamenti:

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Mandemu una lista di cambiamenti: se qualcunu marca un gol, cambiamu u puntuatu di u partitu, s'ellu si ferite, questu hè ancu mandatu in tempu reale. Cusì, i clienti ricevenu istantaneamente dati aghjurnati in u feed di l'avvenimentu di partita. Periòdicamente, per chì u cliente capisce chì u servitore ùn hè micca mortu, chì nunda ùn hè accadutu, mandemu un timestamp ogni 15 seconde - per sapè chì tuttu hè in ordine è ùn ci hè bisognu di ricunnisce.

Cumu hè servita a cunnessione in diretta?

  • Prima di tuttu, creemu un canale in quale l'aghjurnamenti buffered seranu ricevuti.
  • Dopu quì, sottumettemu stu canale per riceve l'aghjurnamenti.
  • Avemu stabilitu l'intestazione curretta per chì u cliente sà chì tuttu hè bè.
  • Mandate u primu ping. Avemu simpricimenti registrà u timestamp di cunnessione attuale.
  • Dopu quì, leghjemu da u canali in un ciclu finu à chì u canali di l'aghjurnamentu hè chjusu. U canali riceve periodicamente sia u timestamp attuale o cambiamenti chì avemu digià scrittu per apre e cunnessione.

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

U primu prublema chì avemu scontru era u seguitu: per ogni cunnessione aperta cù u cliente, avemu creatu un cronometru chì marcava una volta ogni 15 seconde - risulta chì se avemu avutu 6 mila cunnessione aperte cù una macchina (cù un servitore API), 6. mille timers sò stati creati. Questu hà purtatu à a macchina chì ùn mantene micca a carica necessaria. U prublema ùn era micca cusì evidente per noi, ma avemu un pocu aiutu è l'avemu riparatu.

In u risultatu, avà u nostru ping vene da u listessu canale da quale vene l'aghjurnamentu.

Dunque, ci hè solu un timer chì tick una volta ogni 15 seconde.

Ci sò parechje funzioni ausiliarii quì - mandà l'intestazione, ping è a struttura stessa. Questu hè, u nome di a tavula (persona, partita, stagione) è l'infurmazioni nantu à sta entrata sò trasmessi quì:

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Meccanismu per mandà l'aghjurnamenti

Avà un pocu di induve venenu i cambiamenti. Avemu parechje persone, editori, chì fighjanu a trasmissione in tempu reale. Creanu tutti l'avvenimenti: qualcunu hè statu mandatu, qualcunu hè statu feritu, qualchì tipu di rimpiazzamentu...

Utilizendu un CMS, a dati entra in a basa di dati. Dopu questu, a basa di dati notifica à i servitori API nantu à questu utilizendu u mecanismu Listen / Notify. I servitori API mandanu digià sta informazione à i clienti. Cusì, avemu essenzialmente solu uni pochi servitori cunnessi à a basa di dati è ùn ci hè micca una carica speciale nantu à a basa di dati, perchè u cliente ùn interagisce micca direttamente cù a basa di dati in ogni modu:

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

PostgreSQL: Listen / Notify

U mecanismu Listen / Notify in Postgres permette di avvisà l'abbonati di l'avvenimentu chì qualchì avvenimentu hà cambiatu - qualchì record hè statu creatu in a basa di dati. Per fà questu, avemu scrittu un attivatore simplice è funzione:

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Quandu si inserisce o cambia un registru, chjamemu a funzione di notificazione nantu à u canali data_updates, passendu quì u nome di a tavula è l'identificatore di u registru chì hè statu cambiatu o inseritu.

Per tutti i tavulini chì deve esse sincronizzati cù u cliente, definiscemu un trigger, chì, dopu avè cambiatu / aghjurnatu un registru, chjama a funzione indicata nantu à a slide sottu.
Cumu l'API sottumette à sti cambiamenti?

Un mecanismu Fanout hè creatu - manda missaghji à u cliente. Raccoglie tutti i canali di i clienti è manda l'aghjurnamenti ricevuti attraversu questi canali:

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Quì a libreria pq standard, chì cunnetta à a basa di dati è dice chì vole à sente à u canali (data_updates), verifica chì a cunnessione hè aperta è tuttu hè bè. Omettu a verificazione d'errore per risparmià spaziu (ùn cuntrollà hè periculosu).

Dopu, avemu stabilitu in modu asincronu Ticker, chì mandarà un ping ogni 15 seconde, è cumincianu à sente u canali chì avemu abbonatu. Se ricevemu un ping, publichemu stu ping. Se ricevemu qualchì tipu di entrata, allora publichemu sta entrata à tutti l'abbonati di questu Fanout.

Cumu funziona Fan-out?

In russu questu si traduce cum'è "splitter". Avemu un ughjettu chì registra l'abbonati chì volenu riceve alcune aghjurnamenti. È appena una aghjurnazione ghjunghje à questu ughjettu, distribuisce sta aghjurnazione à tutti i so abbonati. Semplice abbastanza:

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Cumu hè implementatu in Go:

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Ci hè una struttura, hè sincronizata cù Mutexes. Hà un campu chì salva u statu di a cunnessione di Fanout à a basa di dati, vale à dì chì hè attualmente à sente è riceverà l'aghjurnamenti, è ancu una lista di tutti i canali dispunibili - mappa, a chjave di quale hè u canali è struct in forma di valori (essenzialmente ùn hè micca usatu in ogni modu).

Dui metudi - Connected and Disconnected - permettenu di dì à Fanout chì avemu una cunnessione à a basa, hè apparsu è chì a cunnessione à a basa hè stata rotta. In u sicondu casu, avete bisognu di disconnect tutti i clienti è dite à elli chì ùn ponu più sente à nunda è ch'elli ricunniscenu perchè a cunnessione cù elli hè chjusa.

Ci hè ancu un metudu Subscribe chì aghjunghje u canali à i "ascultori":

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Ci hè un metudu Unsubscribe, chì sguassate u canali da l'ascultori se u cliente disconnects, è ancu un metudu Publish, chì permette di mandà un missaghju à tutti l'abbonati.

Quistione: – Chì hè trasmessu attraversu stu canali ?

MS: - U mudellu chì hà cambiatu o ping hè trasmessu (essenzialmente solu un numeru, integer).

MS: - Pudete mandà qualcosa, mandà qualsiasi struttura, pubblicà - si trasforma solu in JSON è questu hè.

MS: - Ricevemu una notificazione da Postgres - cuntene u nome di a tavola è l'identificatore. Basatu nantu à u nome di a tavula è l'identificatore, avemu u registru chì avemu bisognu, è dopu mandemu sta struttura per a publicazione.

Infrastruttura

Cosa hè questu da una perspettiva di l'infrastruttura? Avemu 7 servitori di hardware: unu di elli hè cumpletamente dedicatu à a basa di dati, l'altri sei run machine virtuale. Ci sò 6 copie di l'API: ogni macchina virtuale cù l'API corre nantu à un servitore hardware separatu - questu hè per affidabilità.

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Avemu dui frontends cù Keepalived installatu per migliurà l'accessibilità, perchè se qualcosa succede, un frontend pò rimpiazzà l'altru. Inoltre - duie copie di u CMS.

Ci hè ancu un importatore di statistiche. Ci hè un DB Slave da quale i backups sò fatti periodicamente. Ci hè Pigeon Pusher, una applicazione chì manda notifiche push à i clienti, è ancu cose di infrastruttura: Zabbix, Graylog2 è Chef.

In fatti, sta infrastruttura hè redundante, perchè 100 mila ponu esse servuti cù menu servitori. Ma ci era u ferru - l'avemu usatu (ci anu dettu chì era pussibule - perchè micca).

Pro di Go

Dopu avè travagliatu nantu à sta applicazione, emergenu tali vantaghji evidenti di Go.

  • Cool http biblioteca. Cù ellu pudete creà assai assai fora di a scatula.
  • Inoltre, i canali chì ci permettenu di implementà assai facilmente un mecanismu per mandà notificazioni à i clienti.
  • A cosa maravigliosa Race detector ci hà permessu di eliminà parechji bug critichi (infrastruttura di staging). Tuttu ciò chì travaglia nantu à staging hè lanciatu, cumpilatu cù a chjave Race; è noi, in cunsiquenza, pudemu guardà l'infrastruttura di staging per vede quale prublemi potenziali avemu.
  • Minimalismu è simplicità di lingua.

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

Cerchemu sviluppatori! Sè qualchissia vole, per piacè.

I vostri dumanni

Quistione da l'audienza (in seguitu - B): - Mi pare chì avete mancatu un puntu impurtante in quantu à Fan-out. Sò currettu à capisce chì quandu mandate una risposta à un cliente, bluccà se u cliente ùn vole micca leghje?

MS: - Innò, ùn simu micca bluccatu. Prima, avemu tuttu questu daretu à nginx, vale à dì, ùn ci sò micca prublemi cù i clienti lenti. Siconda, u cliente hà un canale cù un buffer - in fattu, pudemu mette finu à un centu d'aghjurnamenti quì ... Se ùn pudemu micca scrive à u canali, allora l'elimina. Se vedemu chì u canali hè bluccatu, allora solu chjuderemu u canali, è questu hè - u cliente si ricunniscerà s'ellu ci hè un prublema. Dunque, in principiu, ùn ci hè micca bluccatu quì.

IN: - Ùn puderia micca esse pussibule di mandà immediatamente un registru à Listen / Notify, è micca una tabella d'identificatore?

MS: - Listen / Notify hà un limitu di 8 mila byte nantu à a precarica chì manda. In principiu, saria pussìbule di mandà s'è avemu trattatu cù una piccula quantità di dati, ma mi pari chì sta manera [a manera di fà] hè simplicemente più affidabile. E limitazioni sò in Postgres stessu.

IN: - I clienti ricevenu l'aghjurnamenti nantu à i partiti chì ùn anu micca interessatu?

MS: - In generale, sì. In regula, ci sò 2-3 partiti chì passanu in parallelu, è ancu cusì raramenti. Se un cliente fighja qualcosa, di solitu guarda u match chì si passa. Allora, u cliente hà una basa di dati lucale in quale tutte queste aghjurnamenti sò aghjuntu, è ancu senza una cunnessione Internet, u cliente pò vede tutti i partiti passati per quale hà aghjurnamenti. Essenzialmente, sincronizemu a nostra basa di dati nantu à u servitore cù a basa di dati lucale di u cliente per ch'ellu pò travaglià offline.

IN: – Perchè avete fattu u vostru propiu ORM ?

Alexey (unu di i sviluppatori di Look+): – À quellu tempu (era un annu fà) ci era menu ORM chè avà, quandu ci sò assai. A mo cosa preferita di a maiò parte di l'ORM hè chì a maiò parte di elli funzionanu in interfacce vacanti. Vale à dì, i metudi in questi ORM sò pronti per piglià qualcosa: una struttura, un punteru di struttura, un numeru, qualcosa completamente irrilevante ...

U nostru ORM genera strutture basate nantu à u mudellu di dati. Me stessu. È dunque tutti i metudi sò cuncreti, ùn aduprate micca a riflessione, etc. Acceptanu strutture è aspettanu di utilizà quelli strutture chì venenu.

IN: – Quanta ghjente hà participatu ?

MS: – In u stadiu iniziale, duie persone anu participatu. Avemu principiatu in un locu in u ghjugnu, è in l'aostu a parte principale era pronta (a prima versione). Ci hè stata una liberazione in settembre.

IN: - Induve discrive SSE, ùn utilizate micca timeout. Perchè hè questu?

MS: - Per esse onestu, SSE hè sempre un protokollu html5: u standard SSE hè pensatu per cumunicà cù i navigatori, finu à chì aghju capitu. Hà funzioni supplementari per chì i navigatori ponu ricunnisce (è cusì), ma ùn avemu micca bisognu, perchè avemu avutu i clienti chì puderanu implementà ogni logica per cunnette è riceve infurmazioni. Ùn avemu micca fattu SSE, ma piuttostu qualcosa simili à SSE. Questu ùn hè micca u protocolu stessu.
Ùn ci era micca bisognu. In quantu capiscu, i clienti implementanu u mecanismu di cunnessione quasi da zero. Ùn li importava micca veramente.

IN: - Chì utilità supplementari avete usatu?

MS: - Avemu più attivamente utilizatu govet è golint per unificà u stile, è ancu gofmt. Nunda altru hè stata utilizata.

IN: – Chì avete usatu per debug ?

MS: - A debugging hè stata largamente realizata cù e teste. Ùn avemu micca usatu debugger o GOP.

IN: - Pudete rinvià a diapositiva induve a funzione Publish hè implementata? I nomi di variabili di una sola lettera ti confondenu?

MS: - Innò. Hanu un scopu di visibilità abbastanza "strettu". Ùn sò micca usati in altre locu, salvu quì (eccettu per l'internu di sta classa), è hè assai compactu - hè solu 7 linee.

IN: - In qualchì manera ùn hè ancu intuitivu ...

MS: - Innò, nò, hè un veru codice ! Ùn si tratta micca di stile. Hè solu una classe utilitaria, assai chjuca - solu 3 campi in a classe ...

Mikhail Salosin. Golang Meetup. Utilizendu Go in u backend di l'applicazione Look+

MS: - In generale, tutti i dati chì sò sincronizati cù i clienti (partite di stagione, ghjucatori) ùn cambianu micca. À pocu pressu, se facemu un altru sportu in u quale avemu bisognu di cambià a partita, avemu da piglià solu tuttu in contu in a nova versione di u cliente, è i vechji versioni di u cliente seranu pruibiti.

IN: - Ci hè qualchì pacchettu di gestione di a dependenza di terzu?

MS: – Avemu usatu go dep.

IN: - Ci era qualcosa di video in u tema di u rapportu, ma ùn ci era nunda in u rapportu di video.

MS: - Innò, ùn aghju nunda in u tema nantu à u video. Hè chjamatu "Look +" - questu hè u nome di l'applicazione.

IN: - Avete dettu chì hè trasmessu à i clienti ?...

MS: - Ùn eramu micca implicati in streaming video. Questu hè statu interamente fattu da Megafon. Iè, ùn aghju micca dettu chì l'applicazione era MegaFon.

MS: - Vai - per mandà tutte e dati - nantu à u puntuatu, nantu à l'avvenimenti di partita, statistiche ... Go hè u backend tutale per l'applicazione. U cliente deve sapè da un locu quale ligame per aduprà per u ghjucatore per chì l'utilizatore pò fighjà u match. Avemu ligami per i video è i flussi chì sò stati preparati.

Certi annunzii 🙂

Grazie per stà cun noi. Ti piace i nostri articuli ? Vulete vede più cuntenutu interessante? Supportaci facendu un ordine o ricumandendu à l'amichi, cloud VPS per sviluppatori da $ 4.99, un analogu unicu di servitori di livellu d'entrata, chì hè statu inventatu da noi per voi: Tutta a verità nantu à VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps da $ 19 o cumu si sparte un servitore? (dispunibule cù RAID1 è RAID10, finu à 24 core è finu à 40GB DDR4).

Dell R730xd 2 volte più prezzu in u centru di dati Equinix Tier IV in Amsterdam? Solu quì 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV da $ 199 in l'Olanda! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - da $ 99! Leghje circa Cumu custruisce una infrastruttura corp. classa cù l'usu di i servitori Dell R730xd E5-2650 v4 valenu 9000 XNUMX euro per un centesimu?

Source: www.habr.com

Add a comment