Michaël Salosin (ďalej – MS): – Ahojte všetci! Volám sa Michaël. Pracujem ako backend vývojár v spoločnosti MC2 Software a porozprávam vám o využití Go v backend vývoji mobilnej aplikácie „Sívaj+”.

Kto z prítomných má rád hokej?

Potom je táto aplikácia pre vás. Je dostupná na platformách Android a iOS a slúži na sledovanie prenosov rôznych športových udalostí online aj zo záznamu. V aplikácii sú tiež rôzne štatistiky, textové prenosy, tabuľky konferencií, turnajov a iné informácie užitočné pre fanúšikov.

V aplikácii je tiež funkcia, ako sú videomomenty, t. j. môžete si pozrieť najzaujímavejšie momenty zápasov (góly, bitky, nájazdy a iné). Ak sa vám nechce sledovať celý prenos, môžete si pozrieť len to najzaujímavejšie.
Čo sme používali pri vývoji?
Hlavná časť bola napísaná v Go. Ten API, s ktorým komunikovali mobilné klienti, bol napísaný v Go. Taktiež sme na Go vytvorili službu na odosielanie push notifikácií na mobily. Ešte sme museli napísať naše vlastné ORM, o ktorom sa možno niekedy podelíme. A na Go boli napísané aj niektoré menšie služby: zmenšovanie a nahrávanie obrázkov pre redaktorskú stranu...
Ako databázu sme použili PostgreSQL. Rozhranie pre redaktorov bolo napísané v Ruby on Rails s pomocou gemu ActiveAdmin. Na Ruby boli napísané aj importy štatistík od poskytovateľa štatistík.
Na systémové testy API sme použili unittest v Pythone. Memcached sa používa na obmedzovanie počtu požiadaviek na API platby, Chef sa používa na správu konfigurácie, Zabbix na zber a monitoring interných štatistických údajov systému. Graylog2 sa používa na zber logov, Slate je dokumentácia API pre klientov.

Výber protokolu
Prvý problém, s ktorým sme sa stretli: museli sme si vybrať protokol interakcie backendu s mobilnými klientmi, vychádzajúc z nasledujúcich bodov...
- Najdôležitejšia požiadavka: údaje na klientoch sa musia aktualizovať v reálnom čase. To znamená, že všetci, ktorí práve sledujú prenos, by mali dostávať aktualizácie takmer okamžite.
- Pre zjednodušenie sme prijali, že údaje, ktoré sa synchronizujú s klientmi, sa nemajú mazať, ale skrývať pomocou špeciálnych flagov.
- Rôzne zriedkavé požiadavky (ako napríklad štatistiky, zostavy tímov, štatistiky tímov) sa získavajú bežnými GET požiadavkami.
- Navyše, systém mal bez problémov zvládnuť 100 tisíc používateľov súčasne.
Na základe toho sme mali dve možnosti protokolu:
- Websockety. Ale nepotrebovali sme kanály od klienta k serveru. Potrebovali sme len posielať aktualizácie zo servera na klienta, takže web-socket je nadbytočný variant.
- Server-Sent Events (SSE) bolo presne to, čo sme potrebovali! Je dosť jednoduché a prakticky spĺňa všetko, čo potrebujeme.
Server-Sent Events
Pár slov o tom, ako táto vec funguje…
Funguje to nad http pripojením. Klient posiela požiadavku, na ňu server odpovedá Content-Type: text/event-stream a nezatvára spojenie s klientom, ale pokračuje v posielaní údajov do spojenia:

Údaje je možné posielať v formáte dohodnutom s klientmi. V našom prípade sme posielali takto: do poľa event sme posielali názov zmenenej štruktúry (človek, hráč), a do poľa data JSON s novými, zmenenými poľami pre hráča.
Teraz k tomu, ako funguje samotná interakcia.
- Najprv klient určí, kedy naposledy došlo k synchronizácii so službou: pozrie sa do svojej lokálnej DB a určí dátum poslednej zmeny, zaregistrovanej u neho.
- Posiela požiadavku s týmto dátumom.
- Na oplátku mu posielame všetky aktualizácie, ktoré sa stali od tohto dátumu.
- Potom sa pripojí k live kanálu a nezatvára ho, pokiaľ potrebuje tieto aktualizácie:

Posielame mu zoznam zmien: ak niekto dal gól – meníme skóre zápasu, ak dostal zranenie – to sa tiež posiela v reálnom čase. Takto klienti okamžite dostávajú aktuálne údaje v správe o zápase. Občas, aby klient vedel, že server nezlyhal, že sa nič nestalo, posielame každých 15 sekúnd timestamp – aby vedel, že všetko je v poriadku a nemusel sa znovu pripájať.
Ako sa obsluhuje live spojenie?
- Najprv vytvoríme kanál, do ktorého prichádzajú aktualizácie s bufferom.
- Potom sa prihlásime na tento kanál na prijímanie aktualizácií.
- Nastavíme správny hlavičku, aby klient vedel, že všetko je v poriadku.
- Posielame prvý ping. Jednoducho zapíšeme aktuálny timestamp spojenia.
- Potom v cykle čítame z kanála, kým nie je kanál aktualizácií uzavretý. Do kanála pravidelne prichádza buď aktuálny timestamp, alebo zmeny, ktoré už zapisujeme do otvorených pripojení.

Prvý problém, s ktorým sme sa stretli, spočíval v tom, že na každé otvorené pripojenie s klientom sme vytvárali časovač, ktorý tikal raz za 15 sekúnd – takže ak sme mali otvorených 6 tisíc pripojení s jedným strojom (s jedným API serverom), vytvorilo sa 6 tisíc časovačov. To viedlo k tomu, že stroj neudržal požadovanú záťaž. Problém nebol pre nás taký zrejmý, ale trošku nám pomohli a vyriešili sme ho.
Na konci teraz ping prichádza z rovnakého kanála, z ktorého prichádza update.
Tým pádom existuje iba jeden časovač, ktorý tiká raz za 15 sekúnd.
Tu je niekoľko pomocných funkcií – odoslanie hlavičky, pingu a samotnej štruktúry. Teda tu sa prenáša názov tabuľky (person, match, season) a samotné informácie o tejto záznamu:

Mechanizmus odosielania aktualizácií
Teraz trochu o tom, odkiaľ zmeny prichádzajú. Máme niekoľko editorov, ktorí v reálnom čase sledujú prenos. Vytvárajú všetky udalosti: niekoho vyhodili, niekto sa zranil, nejaká výmena…
Pomocou CMS sa dáta dostávajú do databázy. Potom databáza pomocou mechanizmu Listen/Notify informuje o tom API servery. API servery už rozosielajú tieto informácie klientom. Takto máme v podstate k databáze pripojených len niekoľko serverov a žiadna zvláštna záťaž na databázu nie je, pretože klient nijako priamo s databázou neinteraguje:

PostgreSQL: Listen/Notify
Mechanizmus Listen/Notify v 'Postgres' umožňuje informovať odberateľov o udalostiach, že došlo k zmene v nejakej udalosti – bola vytvorená nejaká záznam v databáze. Na tento účel sme napísali jednoduchý spúšťač a funkciu:

Pri insert alebo zmene záznamu voláme funkciu notify na kanáli data_updates, predávame tam názov tabuľky a identifikátor záznamu, ktorý bol zmenený alebo vložený.
Na všetky tabuľky, ktoré by mali byť synchronizované s klientom, definujeme spúšťač, ktorý po zmene / aktualizácii záznamu volá funkciu, uvedenú na slajde nižšie.
Ako sa API prihlásilo na tieto zmeny?
Vytvára sa mechanizmus Fanout – ten rozosiela správy klientom. Zhromažďuje všetky kanály klientov a rozosiela aktualizácie, ktoré dostal cez tieto kanály:

Tu je štandardná knižnica pq, ktorá sa pripája k databáze a hovorí, že chce počúvať kanál (data_updates), kontroluje, či je spojenie otvorené a všetko je v poriadku. Kontrola chýb je vynechaná, aby sa ušetrilo miesto (neoverenie môže byť riskantné).
Potom asynchrónne nastavíme Ticker, ktorý bude posielať ping každých 15 sekúnd, a začneme počúvať kanál, na ktorý sme sa prihlásili. Ak dorazí ping, publikujeme tento ping. Ak prichádza nejaký záznam, publikujeme tento záznam všetkým odberateľom tohto Fanoutu.
Ako funguje Fan-out?
Po slovensky sa to prekladá ako „rozdvojovač“. Máme jeden objekt, ktorý registruje odberateľov, ktorí chcú dostávať nejaké aktualizácie. A keď len dorazí aktualizácia k tomuto objektu, rozdelí túto aktualizáciu všetkým existujúcim odberateľom. Je to dosť jednoduché:

Ako je to implementované v Go:

Existuje štruktúra, ktorá sa synchronizuje pomocou Mutexov. Má pole, ktoré ukladá stav pripojenia Fanout k databáze, t. j. momentálne počúva a bude dostávať aktualizácie, ako aj zoznam všetkých existujúcich kanálov – mapu, kde kľúčom je kanál a štruktúra vo forme hodnôt (v podstate sa nijako nepoužíva).
Dva metódy – Connected a Disconnected – umožňujú Fanoutovi vedieť, že máme spojenie s databázou, ktoré sa vytvorilo, a že spojenie s databázou bolo prerušené. V druhom prípade je potrebné odpojiť všetkých klientov a oznámiť im, že už nemôžu nič počúvať a že sa musia znovu pripojiť, keďže spojenie s nimi bolo uzavreté.
Existuje aj metóda Subscribe, ktorá pridáva kanál do „poslucháčov“:

Existuje metóda Unsubscribe, ktorá odstraňuje kanál z poslucháčov, ak sa klient odpojil, a tiež metóda Publish, ktorá umožňuje rozposlať správu všetkým odberateľom.
Otázka: – Čo sa prenáša cez tento kanál?
MSC: – Prenáša sa model, ktorý sa zmenil, alebo ping (v podstate len číslo, integer).
MSC: – Môže sa prenášať čokoľvek, akákoľvek štruktúra – jednoducho sa premení na JSON a je to.
MSC: – Dostávame notifikáciu z „Postgresu“ – obsahuje názov tabuľky a identifikátor. Podľa názvu tabuľky a identifikátora získame požadovaný záznam a túto štruktúru posielame na publikáciu.
Infrastruktúra
Ako to vyzerá z pohľadu infraštruktúry? Máme 7 fyzických serverov: jeden z nich je úplne vyhradený pre databázu, na ostatných šiestich bežia virtuálne stroje. Existuje 6 kópií API: každá virtuálka s API beží na samostatnom fyzickom serveri – to je pre spoľahlivosť.

Máme dva frontendové servery, na ktorých je nainštalovaný Keepalived na zlepšenie dostupnosti, aby v prípade potreby jeden frontend mohol nahradiť druhý. Ešte – dve kópie CMS.
Taktiež máme importér štatistík. Existuje DB Slave, z ktorého sa pravidelne robia zálohy. Existuje Pigeon Pusher – tá aplikácia, ktorá rozosiela push notifikácie klientom, a tiež 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ôžeme zvládnuť aj s menším počtom serverov. Ale hardvér bol – použili sme ho (povedali nám, že môžeme – prečo nie).
Výhody Go
Po tom, čo sme pracovali na tejto aplikácii, sa objavili také zjavné výhody Go.
- Skvelá http knižnica. S ňou sa dá vytvoriť už dosť veľa „z krabice“.
- Plus, kanály, ktoré nám umožnili veľmi ľahko implementovať mechanizmus posielania notifikácií klientom.
- Úžasná vec Race detector nám umožnila odstrániť niekoľko kritických chýb (stagingová infraštruktúra). Všetko, čo beží na stagingu – je spustené, skompilované s kľúčom Race; a my môžeme na stagingovej infraštruktúre vidieť, aké máme potenciálne problémy.
- Minimalizmus a jednoduchость jazyka.

Hľadáme vývojárov! Ak má niekto záujem – prosím.
Otázky
Otázka z publika (ďalej – O): – Zdá sa mi, že ste vynechali jeden dôležitý moment ohľadom Fan-out. Rozumiem správne, že keď posielate klientovi odpoveď, zablokujete sa, ak klient nechce čítať?
MSC: – Nie, neblokujeme sa. Po prvé, všetko je za nginx, teda nemáme žiadne problémy so pomalými klientmi. Po druhé, klient má kanál s vyrovnávacou pamäťou – v podstate môžeme zabudovať až sto aktualizácií... Ak nemôžeme zapísať do kanála, tak ho odstráni. Ak vidíme, že sa kanál zablokoval, jednoducho ho zatvoríme a hotovo – klient sa znovu pripojí, ak nastane nejaký problém. Preto tu v zásade nezaznamenávame žiadne blokácie.
A: – Nemohli ste posielať zápis priamo do Listen/Notify, a nie identifikátor tabuľky?
MSC: – Listen/Notify má obmedzenie na 8000 bajtov pri preload, ktorý odosiela. V zásade by sme to mohli posielať, ak by sme mali na starosti malé množstvo údajov, ale myslím, že to, čo robíme, je jednoducho spoľahlivejšie. Obmedzenia sú v samotnom „PostgreSQL“.
A: – Dostávajú klienti aktualizácie o zápasoch, ktoré ich nezaujímajú?
MSC: – V podstate áno. Zvyčajne je tam 2-3 zápasy súbežne, a to sa deje aj dosť zriedka. Ak klient sleduje niečo, zvyčajne sleduje ten zápas, ktorý práve beží. Potom má klient lokálnu databázu, do ktorej sa ukladajú všetky tieto aktualizácie, a dokonca aj bez pripojenia na internet môže klient vidieť všetky predošlé zápasy, ku ktorým má aktualizácie. V podstate synchronizujeme našu databázu na serveri s lokálnou databázou klienta, aby mohol pracovať aj offline.
A: – Prečo ste vytvorili svoju ORM?
Alexej (jeden z vývojárov „Svet+“): – V tom čase (to bolo pred rokom) bolo ORM menej ako teraz, keď ich je pomerne dosť. Z väčšiny existujúcich ORM sa mi najviac nepáči, že väčšina z nich pracuje na prázdnych rozhraniach. To znamená, že metódy, ktoré sú v týchto ORM, sú pripravené prijať čokoľvek: štruktúru, ukazovateľ štruktúry, číslo, niečo, čo vôbec nesúvisí...
Naša ORM generuje štruktúry na základe modelu údajov. Automaticky. A preto sú všetky metódy konkrétne, nepoužívajú reflexiu atď. Akceptujú štruktúry a očakávajú, že budú používať tie štruktúry, ktoré prídu.
A: – Koľko ľudí sa zúčastnilo?
MSC: – V počiatočnej fáze boli dvaja ľudia. Niekedy v júni sme začali, v auguste bola pripravená väčšina (prvá verzia). V septembri bol vydaný.
A: – Tam, kde opisujete SSE, nepoužívate timeout. Prečo?
MSC: Ak hovoríme otvorene, SSE je predsa len HTML5 protokol: štandard SSE je určený na komunikáciu s prehliadačmi, ak som to správne pochopil. Má ďalšie funkcie, aby sa prehliadače mohli znovu pripojiť (a ďalšie), ale tie nepotrebujeme, pretože sme mali klientov, ktorí mohli implementovať akúkoľvek logiku pripojenia a získavania informácií. Spravili sme skôr niečo, čo je podobné SSE. Nie je to samotný protokol.
Nebolo to potrebné. Ak sa nemýlim, klienti realizovali pripojovací mechanizmus prakticky od nuly. Im to v zásade bolo jedno.
A: Aké ďalšie nástroje ste používali?
MSC: Najaktívnejšie sme používali govet a golint, aby bol štýl jednotný, a tiež gofmt. Nič iné sme nepoužívali.
A: S pomocou čoho ste vykonávali ladenie?
MSC: Ladenie sa v podstate vykonávalo pomocou testov. Žiaden debugger, GOP sme nepoužívali.
A: Môžete vrátiť snímku, kde je implementovaná funkcia Publish? Nezáleží vám na jednopísmenných názvoch premenných?
MSC: Nie. Ich oblasť viditeľnosti je dosť "úzka". Nikde, okrem tohto, už viac nie sú používané (okrem vnútorností tejto triedy), a je veľmi kompaktná – zaberá len 7 riadkov.
A: Pripadne mi to ešte dosť intuitívne...
MSC: Nie-nie, to je pravý kód! Nie je to o štýle. Je to proste taký utilitárny, úplne malý trieda – len 3 polia vnútri triedy...

MSC: Vo všeobecnosti sa všetky údaje, ktoré sa synchronizujú s klientmi (sezónne zápasy, hráči), nemennia. Hrubo povedané, ak by sme robili nejaký iný šport, v ktorom by bolo potrebné meniť zápas, jednoducho to zvážime v novej verzii klienta, a staré verzie klienta budú zablokované.
A: Existujú nejaké externé balíčky na správu závislostí?
MSC: Používali sme go dep.
A: V téme prezentácie bolo niečo o videu, ale v prezentácii o videu nie je.
MSC: Nie, nemám v téme o videu nič. Nazýva sa to "Svetlo+" – takto sa to volá aplikáciu.
A: Hovorili ste, že sa streamuje klientom?..
MSC: Streamingovým videom sme sa nezaoberali. To úplne robil "Megafon". Áno, nespomenul som, že aplikácia je od megafonu.
MSC: – Go – na prenos všetkých údajov – o faktúre, o udalostiach zápasu, štatistikách… Go – to je celý backend pre aplikáciu. Klient sa musí niekde dozvedieť, aký odkaz použiť pre prehrávač, aby mohol používateľ sledovať zápas. Máme odkazy na videá a streamy, ktoré sú pripravené.

Trochu reklamy 🙂
Ďakujeme, že ostávate s nami. Páčia sa vám naše články? Chcete vidieť viac zaujímavých materiálov? Podporte nás, vytvorením objednávky alebo odporúčaním známym, , unikátny ekvivalent entry-level serverov, ktorý sme pre vás vytvorili: (k dispozícii varianty s RAID1 a RAID10, až 24 jadier a až 40GB DDR4).
Dell R730xd je dvakrát lacnejší v dátovom centre Equinix Tier IV v Amsterdame? Len u nás v Holandsku! Dell R420 — 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — od 99 dolárov! Prečítajte si o tom
Zdroj: habr.com
