Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Michail Salosin (ďalej len MS): - Ahojte všetci! Volám sa michael. Pracujem ako backend developer v MC2 Software a budem hovoriť o používaní Go v backende mobilnej aplikácie Look+.

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Má tu niekto rád hokej?

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Potom je táto aplikácia pre vás. Je pre Android a iOS a slúži na sledovanie prenosov rôznych športových podujatí online a zo záznamu. Aplikácia obsahuje aj rôzne štatistiky, textové prenosy, tabuľky pre konferencie, turnaje a ďalšie informácie užitočné pre fanúšikov.

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

V aplikácii je tiež niečo ako videomomenty, t.j. môžete sledovať najdôležitejšie momenty zápasov (góly, súboje, rozstrely atď.). Ak sa vám nechce pozerať celé vysielanie, môžete si pozrieť len tie najzaujímavejšie.

Čo ste použili pri vývoji?

Hlavná časť bola napísaná v Go. API, s ktorým komunikovali mobilní klienti, bolo napísané v Go. V Go bola napísaná aj služba na posielanie push notifikácií na mobilné telefóny. Museli sme si napísať aj vlastný ORM, o ktorom sa možno niekedy porozprávame. Niektoré malé služby boli napísané v Go: zmena veľkosti a načítanie obrázkov pre editorov...

Ako databázu sme použili PostgreSQL. Rozhranie editora bolo napísané v Ruby on Rails pomocou drahokamu ActiveAdmin. Import štatistík od poskytovateľa štatistík je tiež napísaný v Ruby.

Pre systémové API testy sme použili Python unittest. Memcached sa používa na obmedzenie platobných volaní API, „Chef“ sa používa na kontrolu konfigurácie, Zabbix sa používa na zhromažďovanie a monitorovanie interných systémových štatistík. Graylog2 je na zbieranie logov, Slate je API dokumentácia pre klientov.

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Výber protokolu

Prvý problém, na ktorý sme narazili: potrebovali sme zvoliť protokol na interakciu medzi backendom a mobilnými klientmi na základe nasledujúcich bodov...

  • Najdôležitejšia požiadavka: údaje o klientoch musia byť aktualizované v reálnom čase. To znamená, že každý, kto práve sleduje vysielanie, by mal dostávať aktualizácie takmer okamžite.
  • Pre zjednodušenie sme predpokladali, že údaje, ktoré sú synchronizované s klientmi, sa nevymažú, ale skryjú sa pomocou špeciálnych príznakov.
  • Všetky druhy zriedkavých požiadaviek (ako sú štatistiky, zostavy tímov, tímové štatistiky) sa získavajú bežnými požiadavkami GET.
  • Navyše, systém musel bez problémov podporovať 100 tisíc používateľov súčasne.

Na základe toho sme mali dve možnosti protokolu:

  1. Websockety. Nepotrebovali sme však kanály z klienta na server. Potrebovali sme iba odosielať aktualizácie zo servera klientovi, takže websocket je redundantná možnosť.
  2. Server-Sent Events (SSE) vyšiel presne! Je celkom jednoduchý a v podstate spĺňa všetko, čo potrebujeme.

Udalosti odoslané serverom

Pár slov o tom, ako to funguje...

Beží nad pripojením http. Klient odošle požiadavku, server odpovie Content-Type: text/event-stream a neuzavrie spojenie s klientom, ale pokračuje v zapisovaní údajov do spojenia:

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Údaje je možné zasielať vo formáte dohodnutom s klientmi. V našom prípade sme ho odoslali v tomto tvare: do poľa udalosti bol odoslaný názov zmenenej štruktúry (osoba, hráč) a do poľa s údajmi JSON s novými, zmenenými poľami pre hráča.

Teraz si povedzme, ako funguje samotná interakcia.

  • Prvá vec, ktorú klient urobí, je určiť, kedy bola vykonaná posledná synchronizácia so službou: pozrie sa do svojej lokálnej databázy a určí dátum poslednej zmeny, ktorú zaznamenal.
  • S týmto dátumom odošle žiadosť.
  • Ako odpoveď mu posielame všetky aktualizácie, ktoré sa udiali od tohto dátumu.
  • Potom sa pripojí k živému kanálu a nezatvorí sa, kým nebude potrebovať tieto aktualizácie:

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Pošleme mu zoznam zmien: ak niekto strelí gól, zmeníme skóre zápasu, ak sa zraní, aj tento sa odošle v reálnom čase. Klienti tak okamžite dostávajú aktuálne údaje v informačnom kanáli zápasov. Pravidelne, aby klient pochopil, že server nezomrel, že sa mu nič nestalo, posielame časovú pečiatku každých 15 sekúnd - aby vedel, že je všetko v poriadku a nie je potrebné sa znova pripájať.

Ako prebieha servis živého pripojenia?

  • Najprv vytvoríme kanál, do ktorého budú prijímané aktualizácie vo vyrovnávacej pamäti.
  • Potom sa prihlásime na odber tohto kanála, aby sme dostávali aktualizácie.
  • Nastavíme správnu hlavičku, aby klient vedel, že je všetko ok.
  • Pošlite prvý ping. Jednoducho zaznamenáme aktuálnu časovú pečiatku pripojenia.
  • Potom čítame z kanála v slučke, kým sa aktualizačný kanál nezatvorí. Kanál pravidelne prijíma buď aktuálnu časovú pečiatku, alebo zmeny, ktoré už zapisujeme, aby sme otvorili pripojenia.

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Prvý problém, na ktorý sme narazili, bol nasledujúci: pre každé pripojenie otvorené s klientom sme vytvorili časovač, ktorý tikal raz za 15 sekúnd - ukázalo sa, že ak sme mali otvorených 6 6 pripojení na jednom počítači (s jedným API serverom), XNUMX bolo vytvorených tisíc časovačov. To viedlo k tomu, že stroj neudržal požadované zaťaženie. Problém pre nás nebol taký zrejmý, ale dostali sme malú pomoc a opravili sme ho.

Výsledkom je, že teraz náš ping pochádza z rovnakého kanála, z ktorého prichádza aktualizácia.

V súlade s tým existuje iba jeden časovač, ktorý tiká raz za 15 sekúnd.

Pomocných funkcií je tu viacero – odoslanie hlavičky, ping a samotná štruktúra. To znamená, že sa tu prenáša názov tabuľky (osoba, zápas, sezóna) a informácie o tomto zázname:

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Mechanizmus odosielania aktualizácií

Teraz trochu o tom, odkiaľ prichádzajú zmeny. Máme viacero ľudí, redaktorov, ktorí vysielanie sledujú v reálnom čase. Vytvárajú všetky udalosti: niekto bol vylúčený, niekto bol zranený, nejaký druh náhrady...

Pomocou CMS sa údaje dostávajú do databázy. Potom databáza o tom informuje servery API pomocou mechanizmu Listen/Notify. Servery API už odosielajú tieto informácie klientom. K databáze máme teda v podstate pripojených len niekoľko serverov a databáza nie je nijako špeciálne zaťažená, pretože klient s databázou žiadnym spôsobom priamo neinteraguje:

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

PostgreSQL: Počúvajte/upozorňujte

Mechanizmus Listen/Notify v Postgres vám umožňuje upozorniť predplatiteľov udalosti, že sa nejaká udalosť zmenila – v databáze bol vytvorený nejaký záznam. Aby sme to dosiahli, napísali sme jednoduchý spúšťač a funkciu:

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Pri vkladaní alebo zmene záznamu zavoláme funkciu notify na kanáli data_updates, kde prenesieme názov tabuľky a identifikátor záznamu, ktorý bol zmenený alebo vložený.

Pre všetky tabuľky, ktoré musia byť synchronizované s klientom, definujeme spúšťač, ktorý po zmene/aktualizácii záznamu vyvolá funkciu uvedenú na snímke nižšie.
Ako sa API prihlási k týmto zmenám?

Vytvorí sa mechanizmus Fanout – posiela správy klientovi. Zhromažďuje všetky zákaznícke kanály a odosiela aktualizácie, ktoré prijal prostredníctvom týchto kanálov:

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Tu štandardná knižnica pq, ktorá sa pripojí k databáze a povie, že chce počúvať kanál (data_updates), skontroluje, či je pripojenie otvorené a všetko je v poriadku. Kontrolu chýb vynechávam, aby som ušetril miesto (nekontrolovanie je nebezpečné).

Ďalej asynchrónne nastavíme Ticker, ktorý každých 15 sekúnd odošle ping, a začneme počúvať kanál, ktorý odoberáme. Ak dostaneme ping, tento ping zverejníme. Ak dostaneme nejaký druh príspevku, potom ho zverejníme všetkým predplatiteľom tohto Fanoutu.

Ako funguje Fan-out?

V ruštine sa to prekladá ako „rozdeľovač“. Máme jeden objekt, ktorý registruje predplatiteľov, ktorí chcú dostávať nejaké aktualizácie. A akonáhle príde aktualizácia k tomuto objektu, distribuuje túto aktualizáciu všetkým svojim predplatiteľom. Dosť jednoduché:

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Ako sa to implementuje v Go:

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Existuje štruktúra, je synchronizovaná pomocou Mutexov. Má pole, ktoré ukladá stav pripojenia Fanoutu k databáze, t.j. práve počúva a bude dostávať aktualizácie, ako aj zoznam všetkých dostupných kanálov - mapa, ktorej kľúčom je kanál a štruktúra v tvare hodnoty (v podstate sa to nijakým spôsobom nepoužíva).

Dve metódy – Connected a Disconnected – nám umožňujú povedať Fanoutovi, že máme spojenie so základňou, objavilo sa a že spojenie so základňou bolo prerušené. V druhom prípade musíte odpojiť všetkých klientov a povedať im, že už nemôžu nič počúvať a že sa znova pripájajú, pretože spojenie s nimi sa uzavrelo.

Existuje aj metóda odberu, ktorá pridá kanál medzi „poslucháčov“:

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Existuje metóda Unsubscribe, ktorá odstráni kanál z poslucháčov, ak sa klient odpojí, ako aj metóda Publish, ktorá vám umožní poslať správu všetkým odberateľom.

Otázka: – Čo sa prenáša týmto kanálom?

PANI: – Model, ktorý sa zmenil, alebo sa odošle ping (v podstate len číslo, celé číslo).

PANI: – Môžete poslať čokoľvek, poslať akúkoľvek štruktúru, zverejniť ju – jednoducho sa zmení na JSON a je to.

PANI: – Dostávame upozornenie od Postgresu – obsahuje názov tabuľky a identifikátor. Na základe názvu tabuľky a identifikátora získame záznam, ktorý potrebujeme, a následne túto štruktúru odošleme na zverejnenie.

Infraštruktúra

Ako to vyzerá z pohľadu infraštruktúry? Máme 7 hardvérových serverov: jeden z nich je úplne vyhradený pre databázu, na ďalších šiestich bežia virtuálne stroje. Existuje 6 kópií API: každý virtuálny stroj s API beží na samostatnom hardvérovom serveri - je to kvôli spoľahlivosti.

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Máme nainštalované dva frontendy s Keepalived na zlepšenie dostupnosti, takže ak sa niečo stane, jeden frontend môže nahradiť druhý. Tiež – dve kópie CMS.

Nechýba ani importér štatistík. Existuje DB Slave, z ktorého sa pravidelne robia zálohy. Existuje Pigeon Pusher, aplikácia, ktorá klientom posiela push notifikácie, ako aj infraštruktúrne veci: Zabbix, Graylog2 a Chef.

V skutočnosti je táto infraštruktúra nadbytočná, pretože 100 tisíc môže byť obsluhovaných menším počtom serverov. Ale bolo tam železo - to sme používali (bolo nám povedané, že sa to dá - prečo nie).

Výhody Go

Keď sme pracovali na tejto aplikácii, objavili sa také zjavné výhody Go.

  • Skvelá knižnica http. S ním môžete vytvoriť pomerne veľa z krabice.
  • Navyše kanály, ktoré nám umožnili veľmi jednoducho implementovať mechanizmus na odosielanie upozornení klientom.
  • Úžasná vec Race detector nám umožnil eliminovať niekoľko kritických chýb (infraštruktúra stagingu). Všetko, čo funguje na stagingu, je spustené, skompilované pomocou Race kľúča; a podľa toho sa môžeme pozrieť na fázovú infraštruktúru, aby sme zistili, aké potenciálne problémy máme.
  • Minimalizmus a jednoduchosť jazyka.

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

Hľadáme vývojárov! Ak niekto chce, prosím.

otázky

Otázka od publika (ďalej – B): – Zdá sa mi, že vám unikol jeden dôležitý bod týkajúci sa Fan-out. Chápem správne, že keď odošlete odpoveď klientovi, zablokujete ho, ak klient nechce čítať?

PANI: - Nie, neblokujeme. Po prvé, toto všetko máme za nginx, to znamená, že s pomalými klientmi nie sú žiadne problémy. Po druhé, klient má kanál s vyrovnávacou pamäťou - v skutočnosti tam môžeme dať až sto aktualizácií... Ak nemôžeme na kanál zapisovať, tak ho vymaže. Ak vidíme, že kanál je zablokovaný, jednoducho kanál zatvoríme a je to - klient sa znova pripojí, ak sa vyskytne nejaký problém. Preto tu v zásade neexistuje žiadne blokovanie.

in: – Nebolo by možné okamžite odoslať záznam do Listen/Notify a nie tabuľku identifikátorov?

PANI: – Listen/Notify má limit 8 XNUMX bajtov na predbežné načítanie, ktoré odosiela. V zásade by bolo možné posielať, ak by sme mali do činenia s malým množstvom údajov, ale zdá sa mi, že tento spôsob [spôsob, akým to robíme] je jednoducho spoľahlivejší. Obmedzenia sú v samotnom Postgrese.

in: – Dostávajú zákazníci aktuálne informácie o zápasoch, o ktoré nemajú záujem?

PANI: - Vo všeobecnosti áno. Spravidla prebiehajú paralelne 2-3 zápasy a aj to dosť zriedkavo. Ak klient niečo sleduje, tak väčšinou sleduje práve prebiehajúci zápas. Klient má potom k dispozícii lokálnu databázu, do ktorej sa všetky tieto aktualizácie sčítavajú a aj bez pripojenia na internet si klient môže prezerať všetky minulé zápasy, pre ktoré má aktualizácie. V podstate synchronizujeme našu databázu na serveri s lokálnou databázou klienta, aby mohol pracovať offline.

in: – Prečo ste si vytvorili vlastný ORM?

Alexey (jeden z vývojárov Look+): – V tom čase (bolo to pred rokom) bolo ORM menej ako teraz, keď ich je pomerne veľa. Moja obľúbená vec na väčšine ORM je, že väčšina z nich beží na prázdnych rozhraniach. To znamená, že metódy v týchto ORM sú pripravené prijať čokoľvek: štruktúru, ukazovateľ štruktúry, číslo, niečo úplne irelevantné...

Náš ORM generuje štruktúry na základe dátového modelu. Ja sám. A preto sú všetky metódy konkrétne, nepoužívajú reflexiu atď. Prijímajú štruktúry a očakávajú, že použijú tie štruktúry, ktoré prídu.

in: – Koľko ľudí sa zúčastnilo?

PANI: – V počiatočnej fáze sa zúčastnili dvaja ľudia. Začali sme niekde v júni a v auguste bola hotová hlavná časť (prvá verzia). V septembri došlo k prepusteniu.

in: – Tam, kde popisujete SSE, nepoužívate časový limit. prečo je to tak?

PANI: – Aby som bol úprimný, SSE je stále protokol html5: štandard SSE je navrhnutý na komunikáciu s prehliadačmi, pokiaľ som pochopil. Má ďalšie funkcie, aby sa prehliadače mohli znova pripojiť (a tak ďalej), ale nepotrebujeme ich, pretože sme mali klientov, ktorí mohli implementovať akúkoľvek logiku na pripojenie a prijímanie informácií. My sme nespravili SSE, ale skôr niečo podobné ako SSE. Toto nie je samotný protokol.
Nebolo treba. Pokiaľ som pochopil, klienti implementovali mechanizmus pripojenia takmer od nuly. Bolo im to úplne jedno.

in: – Aké ďalšie nástroje ste použili?

PANI: – Najaktívnejšie sme na zjednotenie štýlu používali govet a golint, ako aj gofmt. Nič iné nebolo použité.

in: – Čo ste použili na ladenie?

PANI: – Ladenie sa vo veľkej miere vykonávalo pomocou testov. Nepoužili sme žiadny debugger ani GOP.

in: – Môžete vrátiť snímku, na ktorej je implementovaná funkcia Publikovať? Mýtia vás jednopísmenové názvy premenných?

PANI: - Nie. Majú pomerne „úzky“ rozsah viditeľnosti. Nikde inde okrem tu sa nepoužívajú (okrem vnútorných častí tejto triedy) a je veľmi kompaktný – zaberie len 7 riadkov.

in: - Akosi to stále nie je intuitívne...

PANI: - Nie, nie, toto je skutočný kód! Nie je to o štýle. Je to len taká utilitárna, veľmi malá trieda - iba 3 polia v triede...

Michail Salošin. Stretnutie Golang. Pomocou Go v backende aplikácie Look+

PANI: – Celkovo sa všetky údaje, ktoré sú synchronizované s klientmi (sezónne zápasy, hráči), nemenia. Zhruba povedané, ak urobíme iný šport, v ktorom potrebujeme zmeniť zápas, jednoducho všetko zohľadníme v novej verzii klienta a staré verzie klienta budú zakázané.

in: – Existujú nejaké balíčky správy závislostí tretích strán?

PANI: – Použili sme go dep.

in: – V téme správy bolo niečo o videu, ale v správe o videu nebolo nič.

PANI: – Nie, v téme o videu nič nemám. Volá sa „Look+“ – to je názov aplikácie.

in: – Povedali ste, že sa to streamuje klientom?...

PANI: – Nezapojili sme sa do streamovania videa. Celé to urobil Megafon. Áno, nepovedal som, že aplikácia je MegaFon.

PANI: – Go – na odosielanie všetkých údajov – o skóre, o udalostiach zápasov, štatistikách... Go je celý backend pre aplikáciu. Klient musí odniekiaľ vedieť, ktorý odkaz má pre hráča použiť, aby si používateľ mohol pozrieť zápas. Máme pripravené odkazy na videá a streamy.

Nejaké inzeráty 🙂

Ďakujeme, že ste zostali s nami. Páčia sa vám naše články? Chcete vidieť viac zaujímavého obsahu? Podporte nás zadaním objednávky alebo odporučením priateľom, cloud VPS pre vývojárov od 4.99 USD, jedinečný analóg serverov základnej úrovne, ktorý sme pre vás vymysleli: Celá pravda o VPS (KVM) E5-2697 v3 (6 jadier) 10GB DDR4 480GB SSD 1Gbps od 19 USD alebo ako zdieľať server? (k dispozícii s RAID1 a RAID10, až 24 jadier a až 40 GB DDR4).

Dell R730xd 2 krát lacnejší v dátovom centre Equinix Tier IV v Amsterdame? Len tu 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6 GHz 14C 64 GB DDR4 4 x 960 GB SSD 1 Gbps 100 TV od 199 USD v Holandsku! Dell R420 – 2x E5-2430 2.2 GHz 6C 128 GB DDR3 2 x 960 GB SSD 1 Gb/s 100 TB – od 99 USD! Čítať o Ako vybudovať infraštruktúru spol. triedy s využitím serverov Dell R730xd E5-2650 v4 v hodnote 9000 XNUMX eur za cent?

Zdroj: hab.com

Pridať komentár