Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Michail Salosin (dále jen MS): - Ahoj všichni! Mé jméno je Michael. Pracuji jako backendový vývojář ve společnosti MC2 Software a budu mluvit o používání Go v backendu mobilní aplikace Look+.

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Má tady někdo rád hokej?

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Pak je tato aplikace určena právě vám. Je pro Android a iOS a slouží ke sledování přenosů různých sportovních událostí online i ze záznamu. Aplikace dále obsahuje různé statistiky, textová vysílání, tabulky pro konference, turnaje a další informace užitečné pro fanoušky.

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

V aplikaci je také něco jako videomomenty, tedy můžete sledovat nejdůležitější momenty zápasů (góly, souboje, přestřelky atd.). Pokud nechcete sledovat celý přenos, můžete se podívat jen na ty nejzajímavější.

Co jste při vývoji použili?

Hlavní část byla napsána v Go. API, se kterým mobilní klienti komunikovali, bylo napsáno v Go. V Go byla napsána i služba pro zasílání push notifikací na mobilní telefony. Také jsme si museli napsat vlastní ORM, o kterém se možná někdy budeme bavit. Některé malé služby byly napsány v Go: změna velikosti a načítání obrázků pro editory...

Jako databázi jsme použili PostgreSQL. Rozhraní editoru bylo napsáno v Ruby on Rails pomocí drahokamu ActiveAdmin. Import statistik od poskytovatele statistik je také napsán v Ruby.

Pro systémové API testy jsme použili Python unittest. Memcached se používá k omezení volání API plateb, „Chef“ se používá ke kontrole konfigurace, Zabbix se používá ke shromažďování a sledování interních systémových statistik. Graylog2 je pro sběr logů, Slate je API dokumentace pro klienty.

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Výběr protokolu

První problém, na který jsme narazili: potřebovali jsme zvolit protokol pro interakci mezi backendem a mobilními klienty na základě následujících bodů...

  • Nejdůležitější požadavek: data o klientech musí být aktualizována v reálném čase. To znamená, že každý, kdo právě sleduje vysílání, by měl dostávat aktualizace téměř okamžitě.
  • Pro zjednodušení jsme předpokládali, že data, která jsou synchronizována s klienty, nejsou smazána, ale jsou skryta pomocí speciálních příznaků.
  • Všechny druhy vzácných požadavků (jako jsou statistiky, složení týmu, statistiky týmu) jsou získávány běžnými požadavky GET.
  • Systém navíc musel bez problémů podporovat 100 tisíc uživatelů současně.

Na základě toho jsme měli dvě možnosti protokolu:

  1. Websockets. Ale nepotřebovali jsme kanály od klienta k serveru. Potřebovali jsme pouze odesílat aktualizace ze serveru klientovi, takže websocket je redundantní možností.
  2. Server-Sent Events (SSE) vyšly přesně! Je celkem jednoduchý a v podstatě splní vše, co potřebujeme.

Události odeslané serverem

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

Běží nad připojením http. Klient odešle požadavek, server odpoví Content-Type: text/event-stream a neuzavře spojení s klientem, ale pokračuje v zápisu dat do spojení:

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Data lze zasílat ve formátu dohodnutém s klienty. V našem případě jsme jej odeslali v tomto tvaru: do pole události bylo odesláno jméno změněné struktury (osoba, hráč) a do datového pole JSON s novými, změněnými poli pro hráče.

Nyní si povíme, jak samotná interakce funguje.

  • První věc, kterou klient udělá, je zjistit, kdy byla provedena poslední synchronizace se službou: podívá se do své lokální databáze a určí datum poslední změny, kterou zaznamenal.
  • Odešle žádost s tímto datem.
  • Jako odpověď mu zašleme všechny aktualizace, které se od tohoto data udály.
  • Poté se připojí k živému kanálu a nezavře se, dokud nebude potřebovat tyto aktualizace:

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Posíláme mu seznam změn: pokud někdo vstřelí gól, změníme skóre zápasu, pokud se zraní, odešle se také v reálném čase. Klienti tak okamžitě obdrží aktuální data ve feedu událostí zápasu. Periodicky, aby klient pochopil, že server nezemřel, že se mu nic nestalo, zasíláme každých 15 sekund časové razítko - aby věděl, že je vše v pořádku a není třeba se znovu připojovat.

Jak probíhá servis živého připojení?

  • Nejprve vytvoříme kanál, do kterého budou přijímány vyrovnávací paměti.
  • Poté se přihlásíme k odběru tohoto kanálu, abychom mohli dostávat aktualizace.
  • Nastavíme správnou hlavičku, aby klient věděl, že je vše ok.
  • Odešlete první ping. Jednoduše zaznamenáme aktuální časové razítko připojení.
  • Poté čteme z kanálu ve smyčce, dokud není aktualizační kanál uzavřen. Kanál pravidelně přijímá buď aktuální časové razítko nebo změny, které již zapisujeme do otevřených připojení.

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

První problém, na který jsme narazili, byl následující: pro každé připojení otevřené s klientem jsme vytvořili časovač, který tikal jednou za 15 sekund - ukázalo se, že pokud jsme měli otevřených 6 tisíc připojení na jednom počítači (s jedním API serverem), 6 bylo vytvořeno tisíc časovačů. To vedlo k tomu, že stroj neudržel požadovaný náklad. Problém pro nás nebyl tak zřejmý, ale dostali jsme malou pomoc a opravili to.

V důsledku toho nyní náš ping pochází ze stejného kanálu, ze kterého přichází aktualizace.

V souladu s tím existuje pouze jeden časovač, který tiká jednou za 15 sekund.

Pomocných funkcí je zde několik - odeslání hlavičky, ping a samotná struktura. To znamená, že se zde přenáší název stolu (osoba, zápas, sezóna) a informace o tomto záznamu:

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Mechanismus pro zasílání aktualizací

Nyní něco málo o tom, odkud změny pocházejí. Máme několik lidí, redaktorů, kteří sledují vysílání v reálném čase. Vytvářejí všechny události: někdo byl vyloučen, někdo byl zraněn, nějaká náhrada...

Pomocí CMS vstupují data do databáze. Poté o tom databáze informuje servery API pomocí mechanismu Listen/Notify. Servery API již tyto informace klientům odesílají. K databázi tak máme v podstatě připojeno jen několik serverů a databáze není nijak zvlášť zatěžována, protože klient s databází nijak přímo neinteraguje:

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

PostgreSQL: Poslouchejte/upozorňujte

Mechanismus Listen/Notify v Postgresu umožňuje upozornit předplatitele události, že se nějaká událost změnila – v databázi byl vytvořen nějaký záznam. K tomu jsme napsali jednoduchý spouštěč a funkci:

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Při vkládání nebo změně záznamu zavoláme funkci notify na kanálu data_updates, předáme tam jméno tabulky a identifikátor záznamu, který byl změněn nebo vložen.

Pro všechny tabulky, které je nutné synchronizovat s klientem, definujeme trigger, který po změně / aktualizaci záznamu vyvolá funkci uvedenou na snímku níže.
Jak se API přihlásí k těmto změnám?

Je vytvořen mechanismus Fanout - odesílá zprávy klientovi. Shromažďuje všechny zákaznické kanály a odesílá aktualizace, které obdržel prostřednictvím těchto kanálů:

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Zde standardní knihovna pq, která se připojí k databázi a řekne, že chce poslouchat kanál (data_updates), zkontroluje, zda je připojení otevřené a vše je v pořádku. Kontrolu chyb vynechávám z důvodu úspory místa (nekontrola je nebezpečná).

Dále asynchronně nastavíme Ticker, který bude každých 15 sekund odesílat ping, a začneme poslouchat kanál, který odebíráme. Pokud obdržíme ping, zveřejníme tento ping. Pokud obdržíme nějaký druh příspěvku, pak tento příspěvek zveřejníme všem odběratelům tohoto Fanoutu.

Jak Fan-out funguje?

V ruštině se to překládá jako „rozdělovač“. Máme jeden objekt, který registruje předplatitele, kteří chtějí dostávat nějaké aktualizace. A jakmile k tomuto objektu dorazí aktualizace, rozešle tuto aktualizaci všem svým předplatitelům. Dost jednoduché:

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Jak je to implementováno v Go:

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Existuje struktura, je synchronizována pomocí Mutexů. Má pole, které ukládá stav připojení Fanoutu k databázi, tedy aktuálně poslouchá a bude dostávat aktualizace, a také seznam všech dostupných kanálů - mapa, jejímž klíčem je kanál a struktura ve tvaru hodnoty (v podstatě se to nijak nepoužívá).

Dvě metody – Connected a Disconnected – nám umožňují sdělit Fanoutu, že máme spojení se základnou, objevilo se a že spojení se základnou bylo přerušeno. Ve druhém případě je potřeba odpojit všechny klienty a říct jim, že už nemohou nic poslouchat a že se znovu připojují, protože spojení s nimi bylo ukončeno.

Existuje také metoda Subscribe, která přidá kanál mezi „posluchače“:

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Existuje metoda Unsubscribe, která odebere kanál z posluchačů, pokud se klient odpojí, a také metoda Publish, která umožňuje odeslat zprávu všem odběratelům.

Otázka: – Co se tímto kanálem přenáší?

SLEČNA: – Model, který se změnil, nebo je odeslán ping (v podstatě jen číslo, celé číslo).

SLEČNA: – Můžete poslat cokoli, poslat libovolnou strukturu, publikovat ji – prostě se to změní na JSON a je to.

SLEČNA: – Obdržíme upozornění od Postgresu – obsahuje název tabulky a identifikátor. Na základě názvu tabulky a identifikátoru získáme záznam, který potřebujeme, a následně tuto strukturu odešleme k publikaci.

infrastruktura

Jak to vypadá z hlediska infrastruktury? Máme 7 hardwarových serverů: jeden z nich je zcela vyhrazený pro databázi, na dalších šesti běží virtuální stroje. K dispozici je 6 kopií API: každý virtuální stroj s API běží na samostatném hardwarovém serveru – to je kvůli spolehlivosti.

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Máme dva frontendy s nainstalovaným Keepalived pro zlepšení dostupnosti, takže pokud se něco stane, jeden frontend může nahradit druhý. Také – dvě kopie CMS.

K dispozici je také importér statistik. Existuje DB Slave, ze kterého se pravidelně zálohují. Existuje Pigeon Pusher, aplikace, která klientům posílá push notifikace a také infrastrukturní věci: Zabbix, Graylog2 a Chef.

Ve skutečnosti je tato infrastruktura nadbytečná, protože 100 tisíc lze obsloužit méně servery. Ale bylo tam železo - to jsme používali (bylo nám řečeno, že je to možné - proč ne).

Výhody Go

Poté, co jsme pracovali na této aplikaci, se objevily takové zjevné výhody Go.

  • Skvělá knihovna http. S ním můžete vytvořit docela hodně z krabice.
  • Navíc kanály, které nám umožnily velmi snadno implementovat mechanismus pro zasílání upozornění klientům.
  • Nádherná věc Race detector nám umožnil odstranit několik kritických chyb (infrastruktura stagingu). Vše, co funguje na stagingu, je spuštěno, kompilováno pomocí klíče Race; a podle toho se můžeme podívat na infrastrukturu předvádění, abychom viděli, jaké potenciální problémy máme.
  • Minimalismus a jednoduchost jazyka.

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

Hledáme vývojáře! Pokud někdo chce, prosím.

otázky

Otázka z publika (dále – B): – Zdá se mi, že jste přehlédl jeden důležitý bod ohledně Fan-out. Chápu to správně, že když odešlete odpověď klientovi, zablokujete, pokud klient nechce číst?

SLEČNA: - Ne, neblokujeme. Za prvé, to vše máme za nginx, to znamená, že s pomalými klienty nejsou žádné problémy. Za druhé, klient má kanál s vyrovnávací pamětí - ve skutečnosti tam můžeme dát až sto aktualizací... Pokud nemůžeme do kanálu zapisovat, tak ho smaže. Pokud uvidíme, že je kanál zablokován, jednoduše kanál zavřeme a je to – klient se znovu připojí, pokud nastane nějaký problém. Proto zde v zásadě k žádnému blokování nedochází.

In: – Nebylo by možné okamžitě odeslat záznam do Listen/Notify a ne tabulku identifikátorů?

SLEČNA: – Listen/Notify má limit 8 tisíc bajtů na předběžné načtení, které odesílá. V zásadě by šlo posílat, kdybychom měli co do činění s malým množstvím dat, ale zdá se mi, že tento způsob [způsob, jakým to děláme] je prostě spolehlivější. Omezení jsou v samotném Postgresu.

In: – Dostávají klienti aktualizace zápasů, o které nemají zájem?

SLEČNA: - Obecně ano. Zpravidla se paralelně odehrávají 2-3 zápasy a i to dost zřídka. Pokud klient něco sleduje, pak obvykle sleduje právě probíhající zápas. Klient pak má lokální databázi, do které se všechny tyto aktualizace sčítají a i bez připojení k internetu si klient může prohlížet všechny minulé zápasy, pro které má aktualizace. V podstatě synchronizujeme naši databázi na serveru s lokální databází klienta, aby mohl pracovat offline.

In: – Proč jste si vytvořili vlastní ORM?

Alexey (jeden z vývojářů Look+): – V té době (bylo to před rokem) bylo ORM méně než nyní, kdy jich je poměrně hodně. Moje oblíbená věc na většině ORM je, že většina z nich běží na prázdných rozhraních. To znamená, že metody v těchto ORM jsou připraveny přijmout cokoli: strukturu, ukazatel struktury, číslo, něco zcela nepodstatného...

Náš ORM generuje struktury založené na datovém modelu. Moje maličkost. A proto jsou všechny metody konkrétní, nepoužívají reflexi atd. Přijímají struktury a očekávají, že použijí ty struktury, které přicházejí.

In: – Kolik lidí se zúčastnilo?

SLEČNA: – V počáteční fázi se účastnili dva lidé. Začali jsme někde v červnu a v srpnu byla hotová hlavní část (první verze). V září došlo k vydání.

In: – Tam, kde popisujete SSE, nepoužíváte časový limit. proč tomu tak je?

SLEČNA: – Abych byl upřímný, SSE je stále protokol html5: standard SSE je navržen pro komunikaci s prohlížeči, pokud tomu rozumím. Má další funkce, aby se prohlížeče mohly znovu připojit (a tak dále), ale my je nepotřebujeme, protože jsme měli klienty, kteří mohli implementovat jakoukoli logiku pro připojení a příjem informací. Neudělali jsme SSE, ale spíše něco podobného SSE. Toto není samotný protokol.
Nebylo potřeba. Pokud jsem pochopil, klienti implementovali mechanismus připojení téměř od nuly. Bylo jim to vlastně jedno.

In: – Jaké další nástroje jste použili?

SLEČNA: – Nejaktivněji jsme používali govet a golint ke sjednocení stylu, stejně jako gofmt. Nic jiného nebylo použito.

In: – Co jste použili k ladění?

SLEČNA: – Ladění bylo z velké části prováděno pomocí testů. Nepoužili jsme žádný debugger ani GOP.

In: – Můžete vrátit snímek, kde je implementována funkce Publikovat? Matou vás jednopísmenné názvy proměnných?

SLEČNA: - Ne. Mají poměrně „úzký“ rozsah viditelnosti. Nikde jinde než zde se nepoužívají (kromě vnitřností této třídy) a je velmi kompaktní – zabere pouze 7 řádků.

In: - Nějak to stále není intuitivní...

SLEČNA: - Ne, ne, tohle je skutečný kód! Nejde o styl. Je to prostě taková utilitární, velmi malá třída - pouze 3 pole uvnitř třídy...

Michail Salosin. Golangské setkání. Pomocí Go v backendu aplikace Look+

SLEČNA: – Celkově se všechna data synchronizovaná s klienty (sezónní zápasy, hráči) nemění. Zhruba řečeno, pokud uděláme jiný sport, ve kterém potřebujeme změnit zápas, tak prostě v nové verzi klienta vše zohledníme a staré verze klienta budou zakázány.

In: – Existují nějaké balíčky správy závislostí třetích stran?

SLEČNA: – Použili jsme go dep.

In: – V tématu zprávy bylo něco o videu, ale ve zprávě o videu nebylo nic.

SLEČNA: – Ne, v tématu o videu nic nemám. Jmenuje se „Look+“ – to je název aplikace.

In: – Řekl jste, že se to streamuje klientům?...

SLEČNA: – Nepodíleli jsme se na streamování videa. Celé to provedl Megafon. Ano, neřekl jsem, že aplikace je MegaFon.

SLEČNA: – Go – pro zasílání všech dat – o skóre, o utkáních, statistikách... Go je celý backend aplikace. Klient musí odněkud vědět, který odkaz má pro hráče použít, aby uživatel mohl zápas sledovat. Máme připravené odkazy na videa a streamy.

Nějaké inzeráty 🙂

Děkujeme, že s námi zůstáváte. Líbí se vám naše články? Chcete vidět více zajímavého obsahu? Podpořte nás objednávkou nebo doporučením přátelům, cloud VPS pro vývojáře od 4.99 $, jedinečný analog serverů základní úrovně, který jsme pro vás vymysleli: Celá pravda o VPS (KVM) E5-2697 v3 (6 jader) 10GB DDR4 480GB SSD 1Gbps od 19 $ nebo jak sdílet server? (k dispozici s RAID1 a RAID10, až 24 jader a až 40 GB DDR4).

Dell R730xd 2krát levnější v datovém centru Equinix Tier IV v Amsterdamu? Pouze zde 2 x Intel TetraDeca-Core Xeon 2 x E5-2697v3 2.6 GHz 14C 64 GB DDR4 4 x 960 GB SSD 1 Gbps 100 TV od 199 USD V Nizozemsku! Dell R420 – 2x E5-2430 2.2 GHz 6C 128 GB DDR3 2 x 960 GB SSD 1 Gb/s 100 TB – od 99 $! Číst o Jak budovat infrastrukturu corp. třídy s využitím serverů Dell R730xd E5-2650 v4 v hodnotě 9000 XNUMX eur za cent?

Zdroj: www.habr.com

Přidat komentář