Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Mihály Salosin (később: MS): – Üdvözlöm mindenkinek! A nevem Mihály. Backend fejlesztő vagyok az MC2 Software cégnél, és a "Smatri+" mobilalkalmazás backend használatáról fogok beszélni.

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Van itt valaki, aki szereti a hokit?

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Akkor ez az alkalmazás önnek való. Az "Android"-ra és iOS-re készült, lehetővé teszi különböző sportesemények közvetítésének megtekintését élőben és felvételről. Az alkalmazásban statisztikák, szöveges közvetítések, konferenciák és tornák táblázatai, valamint egyéb, a szurkolók számára hasznos információk is találhatók.

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Az alkalmazásban van egy funkció, a videomoments, amely lehetővé teszi a meccsek izgalmas pillanatainak (gólok, bunyók, büntetők stb.) megtekintését. Ha nem szeretne az egész közvetítést végignézni, csak a legérdekesebbeket nézheti meg.

Mit használtunk a fejlesztés során?

A fő része Go nyelven készült. Az API, amellyel a mobil kliens kommunikált, Go nyelven íródott. Push-értesítések küldésére is írtunk egy szolgáltatást Go nyelven. Illetve írtunk egy saját ORM-et, amelyről talán egyszer mesélni fogunk. Továbbá Go nyelven írtunk néhány kisebb szolgáltatást: képek átméretezése és feltöltése a szerkesztők részére...

Adatbázisként „Postgres” (PostgreSQL) használatával dolgoztunk. A szerkesztők felülete Ruby on Rails keretrendszerben készült az ActiveAdmin gem segítségével. A statisztika betöltése is Ruby nyelven készült a statisztikai szolgáltatótól.

API rendszerteszteléséhez Python unit test-et használtunk. A Memcached az API-fizetések kérelmeinek korlátozására szolgál, a Chef a konfigurációk kezelésére, a Zabbix pedig a rendszer belső statisztikai adatait gyűjti és monitorozza. A Graylog2 a naplók gyűjtésére szolgál, a Slate pedig az API dokumentáció ügyfelek számára.

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

A protokoll megválasztása

Az első probléma, amellyel szembesültünk: ki kellett választanunk a backend és a mobil kliensek közötti kommunikációs protokollt az alábbi szempontok figyelembevételével...

  • A legfontosabb követelmény: az ügyfelek adatait valós időben kell frissíteni. Tehát mindenki, aki éppen a közvetítést nézi, szinte azonnal meg kell, hogy kapja az aktualizálásokat.
  • A leegyszerűsítés érdekében elfogadtuk, hogy az ügyfelekkel szinkronizált adatok nem törlődnek, hanem speciális zászlók segítségével rejtve maradnak.
  • Mindenféle ritka lekérdezés (például statisztikák, csapatösszeállítások, csapatstatisztikák) normál GET lekérdezésekkel történik.
  • Ezen kívül a rendszernek képesnek kellett lennie arra, hogy egyszerre 100 ezer felhasználót elbírjon.

Ez alapján két protokollválasztási lehetőségünk volt:

  1. Websocketek. De nem volt szükségünk kliens-szerver csatornákra. Csak a szerver felől a kliensnek történő frissítések küldése volt a cél, így a websockets felesleges lehetőség volt.
  2. A Server-Sent Events (SSE) tökéletesen megfelelt! Ez elég egyszerű és alapvetően mindent kielégít, amire szükségünk van.

Server-Sent Events

Pár szó arról, hogyan működik ez a dolog...

HTTP-kapcsolaton működik. A kliens küld egy lekérdezést, amire a szerver a Content-Type: text/event-stream válasz érkezésével reagál, és nem zárja le a kapcsolatot a klienssel, hanem folyamatosan adatokat ír a kapcsolatra:

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Az adatokat a kliensekkel összehangolt formátumban lehet küldeni. A mi esetünkben úgy küldtük, hogy az esemény mezőbe a megváltozott struktúra neve (személy, játékos) került, míg az adatok mezőbe – JSON a játékos új, megváltozott mezőivel.

Most beszéljünk arról, hogyan működik az interakció.

  • Először a kliens meghatározza, mikor történt utoljára szinkronizálás a szolgáltatással: megnézi a helyi adatbázisát, és megállapítja az utolsó módosítás dátumát, amit rögzített.
  • Ezután elküldi a lekérdezését ezzel a dátummal.
  • Válaszul elküldjük neki az összes frissítést, ami ezen a dátumon történt.
  • Ezután létrehozza a kapcsolatot az élő csatornával, és azt nem zárja le, amíg szüksége van ezekre a frissítésekre:

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Küldjük neki a változások listáját: ha valaki gólt lőtt, frissítjük a meccs állását, ha sérülést szenvedett – azt is valós időben küldjük. Így a meccs eseményeiben a kliensek azonnal megkapják a legfrissebb adatokat. Időről időre, hogy a kliens tudja, hogy a szerver él, és nem történt vele semmi, 15 másodpercenként küldünk egy timestampet – hogy tudja, minden rendben van, és nem szükséges újrakapcsolódnia.

Hogyan történik az élő kapcsolat kiszolgálása?

  • Elsősorban létrehozunk egy csatornát, amelybe érkeznek az frissítések pufferrel.
  • Ezután feliratkozunk erre a csatornára, hogy új frissítéseket kapjunk.
  • Megfelelő fejlécet állítunk be, hogy a kliens tudja, minden rendben van.
  • Elküldjük az első pinget. Csak rögzítjük a kapcsolati timestampet.
  • Ezután a ciklusban olvassuk az adatfolyamból mindaddig, amíg az frissítési csatorna le nem zárul. Az adatfolyamba időnként érkezik az aktuális timestamp vagy a már megírt változások nyitott kapcsolatokba.

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Az első probléma, amellyel szembesültünk, a következő volt: minden egyes ügyfélkapcsolathoz létrehoztunk egy időzítőt, amely 15 másodpercenként ütött – tehát, ha 6000 kapcsolatunk volt egy gépen (egy API szerveren), 6000 időzítő jött létre. Ez arra vezetett, hogy a gép nem tartotta a szükséges terhelést. A probléma nem volt annyira nyilvánvaló számunkra, de kaptunk némi segítséget, és megszüntettük.

A végeredmény, hogy a ping most ugyanabból a csatornából érkezik, ahonnan a frissítések jönnek.

Ennek megfelelően csak egy időzítő van, amely 15 másodpercenként üt.

Itt van néhány segédfunkció – a fejléc, a ping és a struktúra küldése. Tehát itt a táblázat neve (person, match, season) és az adott bejegyzés információja kerül átadásra:

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

A frissítések küldési mechanizmusa

Most néhány szót arról, honnan származnak a változások. Van néhány ember, szerkesztők, akik valós időben nézik az adást. Ők hozzák létre az összes eseményt: valakit eltávolítottak, valakinek sérülése lett, valamiféle csere történt...

A CMS segítségével az adatok belekerülnek az adatbázisba. Ezt követően az adatbázis a Listen/Notify mechanizmus segítségével értesíti az API szervereket. Az API szerverek már is továbbítják ezt az információt az ügyfeleknek. Így gyakorlatilag csak néhány szerver csatlakozik az adatbázishoz, és különösebb terhelés nincs az adatbázison, mivel az ügyfelek semmilyen módon nem lépnek közvetlen kapcsolatba az adatbázissal:

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

PostgreSQL: Listen/Notify

A Listen/Notify mechanizmus a PostgreSQL-ben lehetővé teszi, hogy a megfelelő események iránt érdeklődő felhasználókat értesítse arról, hogy egy bejegyzés létrejött az adatbázisban. Ehhez írtunk egy egyszerű trigger-t és függvényt:

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

A bejegyzés beszúrásakor vagy módosításakor a notify függvényt hívjuk meg a data_updates csatornán, átadva a táblázat nevét és a módosított vagy beszúrt bejegyzés azonosítóját.

Az összes táblázatra, amelynek szinkronizálva kell lennie az ügyféllel, definiálunk egy trigger-t, amely a bejegyzés módosítása/aktualizálása után hívja meg az alább látható függvényt.
Hogyan iratkozik fel az API ezekre a változásokra?

Kialakul egy Fanout mechanizmus – amely üzeneteket küld a klienseknek. Összegyűjti az összes klienscsatornát, és elküldi az ezeken a csatornákon megkapott frissítéseket:

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Itt van a standard pq könyvtár, amely csatlakozik az adatbázishoz és jelzi, hogy szeretné hallgatni a csatornát (data_updates), ellenőrzi, hogy a kapcsolat nyitva van és minden rendben. A hibák ellenőrzését kihagyom, hogy helyet spóroljak (a hiba ellenőrzése kockázatos).

Ezután aszimmetrikusan beállítunk egy Ticker-t, amely 15 másodpercenként pinget küld, és elkezdjük hallgatni a csatornát, amelyre feliratkoztunk. Ha pingen érkezik, ezt a pinget publikáljuk. Ha valamilyen bejegyzés érkezik, akkor ezt a bejegyzést elküldjük ennek a Fanout-nak az összes feliratkozójának.

Hogyan működik a Fan-out?

Magyarul ezt "elágazó eszköznek" hívják. Van egy objektumunk, amely regisztrálja a feliratkozókat, akik szeretnének kapni frissítéseket. Amint frissítés érkezik ehhez az objektumhoz, szétszórja ezt a frissítést minden meglévő feliratkozónak. Egyszerűen hangzik:

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Hogyan valósul ez meg Go-ban:

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Van egy struktúra, amelyet Mutexok segítségével szinkronizálnak. Van egy mezője, amely a Fanout kapcsolat állapotát tárolja az adatbázishoz, azaz jelenleg hallgat és frissítéseket fog kapni, valamint az összes meglévő csatorna listája – map, amelynek kulcsa a csatorna és struct értékek formájában (valójában ezt nem használják).

Két módszer – Connected és Disconnected – amely lehetővé teszi a Fanout számára, hogy jelezze, van kapcsolat az adatbázissal, ez létrejött, és hogy a kapcsolat az adatbázissal megszakadt. A második esetben az összes klienst le kell választani és értesíteni őket, hogy már nem hallgathatnak semmit és újra kell csatlakozniuk, mivel a kapcsolatuk megszakadt.

Van egy Subscribe módszer is, amely hozzáad egy csatornát a "hallgatókhoz":

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Van egy Unsubscribe módszer is, amely eltávolítja a csatornát a hallgatók közül, ha a kliens lekapcsolódik, valamint egy Publish módszer, amely lehetővé teszi üzenetek küldését az összes feliratkozónak.

Kérdés: – Mi kerül át ezen a csatornán?

MS: – Egy modell kerül át, amely megváltozott vagy egy ping (valójában csak egy szám, integer).

MS: – Bármilyen struktúrát lehet küldeni, publikálni – ez egyszerűen JSON-ra alakul át, és ennyi.

MS: – Értesítést kapunk a „Postgres”-től – ez tartalmazza a táblázat nevét és az azonosítót. A táblázat neve alapján lekérjük az azonosítót, így megkapjuk a szükséges bejegyzést, majd ezt a struktúrát küldjük ki a közzétételhez.

Infrastruktúra

Hogyan néz ki ez az infrastruktúra szempontjából? Van 7 fizikai szerverünk: az egyik teljesen dedikálva van az adatbázis számára, míg a másik haton virtuális gépek futnak. Van 6 másolata az API-nak: minden virtuális gép külön fizikai szerveren fut az API-val – ez a megbízhatóság érdekében történik.

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Két frontendunk van, amelyeken a Keepalived van telepítve a rendelkezésre állás javítása érdekében, hogy ha szükséges, az egyik frontend helyettesíthesse a másikat. Ezen kívül – két példányban van CMS.

Van továbbá egy statisztikai importáló is. Van DB Slave, amelyről időszakosan készítünk mentéseket. Van Pigeon Pusher – az az alkalmazás, amely push értesítéseket küld a klienseknek, valamint néhány infrastruktúra elem: Zabbix, Graylog2 és Chef.

Valójában ez az infrastruktúra redundáns, mert 100 ezer felhasználót kevesebb szerverrel is el lehet látni. De volt hardverünk – felhasználtuk (azt mondták, hogy lehet – miért ne).

A Go előnyei

Azt követően, hogy dolgoztunk ezen az alkalmazáson, egyértelmű előnyök derültek ki a Go használatával kapcsolatban.

  • Nagyszerű HTTP könyvtár. Ezzel viszonylag sok mindent létre lehet hozni már „do it yourself” alapvetően.
  • Továbbá, a csatornák lehetővé tették számunkra, hogy nagyon könnyen megvalósítsuk az értesítések küldésének mechanizmusát a kliensek felé.
  • A csodálatos Race detector segített több kritikus hibát kijavítani (staging infrastruktúra). Minden, ami a staging-en fut – lefuttatták, és a Race kulccsal van lefordítva; így a staging infrastruktúrában láthatjuk, hogy milyen potenciális problémáink vannak.
  • A nyelv minimalizmusa és egyszerűsége.

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

Fejlesztőket keresünk! Ha valaki szeretné – kérjük, jelentkezzen.

Kérdések

Kérdés a közönségtől (továbbiakban – K): – Úgy tűnik, hogy kihagytál egy fontos pontot a Fan-out-tal kapcsolatban. Jól értem, hogy amikor válaszol a kliensnek, akkor blokkolva van, ha a kliens nem szeretné elolvasni?

MS: – Nem, nem blokkolunk. Először is, mindez nginx mögött van, így lassú kliensekkel nincs probléma. Másodszor, a kliens csatornája puffertartalommal rendelkezik - lényegében akár száz frissítést is betethetünk... Ha nem tudunk írni a csatornába, akkor az törli azt. Ha látjuk, hogy a csatorna blokkolva van, egyszerűen bezárjuk, és kész - a kliens újracsatlakozik, ha probléma merül fel. Így itt elvileg nem merül fel blokkálás.

K: – Nem lehetett volna azonnal a Listen/Notify rekordot küldeni, nem pedig a táblázat-azonosítót?

MS: – A Listen/Notify-nak 8 ezer byte-ra van korlátozása a preload-ra, amit küld. Alapvetően elküldhetnénk, ha kis mennyiségű adatokkal dolgoznánk, de úgy érzem, hogy így [ahogyan csináljuk] egyszerűen megbízhatóbb. A korlátozások a “Postgres”-ben vannak.

K: – Kapnak a kliensek frissítéseket olyan mérkőzésekről, amelyek nem érdeklik őket?

MS: – Alapvetően igen. Általában párhuzamosan 2-3 mérkőzés zajlik, és az is elég ritka. Ha a kliens néz valamit, akkor általában azt a mérkőzést figyeli, ami zajlik. Ezenkívül a kliensnek van egy helyi adatbázisa, ahová az összes frissítést elmenti, és még internetkapcsolat nélkül is megnézheti azokat a mérkőzéseket, amelyekről van frissítése. Létesítünk egy szinkronizációt a szerverünk és a kliens helyi adatbázisa között, hogy offline is tudjon dolgozni.

K: – Miért készítettetek saját ORM-et?

Alekszej (a ‘Nézd+’ fejlesztői közül): – Akkoriban (ez egy éve volt) kevesebb ORM volt, mint most, amikor már viszonylag sok van. A legtöbb létező ORM-ből a legjobban az zavar, hogy a legtöbb üres interfészeken alapul. Vagyis az ezen ORM-ekben található metódusok készen állnak bármire: struktúrára, struktúrára mutató paraméterre, számra, vagy akár teljesen irreleváns dologra...

A mi ORM-ünk a modellek alapján generálja a struktúrákat. Automatikusan. Ezért minden metódus konkrét, nem használ reflektálást stb. Elfogadják a struktúrákat, és várják, hogy az ezeket a struktúrákat használják.

K: – Hány ember dolgozott ezen?

MS: – A kezdeti szakaszban két ember vett részt. Június környékén kezdtük, augusztusra a fő rész kész volt (az első verzió). Szeptemberben volt a megjelenés.

K: – Ott, ahol leírjátok az SSE-t, nem használtok timeout-ot. Miért van ez így?

MS: – Ha őszintén beszélünk, akkor az SSE mégiscsak egy HTML5 protokoll: az SSE szabvány a böngészőkkel való kommunikációra készült, amennyire én tudom. Vannak további funkciói, hogy a böngészők újra tudjanak csatlakozni (és hasonlók), de ezekre nekünk nincs szükségünk, mert voltak ügyfeleink, akik bármilyen csatlakozási logikát és információszerzést meg tudtak valósítani. Inkább nem SSE-t csináltunk, hanem valami SSE-hez hasonlót. Ez nem maga a protokoll.
Nem volt szükség. Amennyire tudom, az ügyfelek gyakorlatilag a nulláról valósították meg a csatlakozási mechanizmust. Tulajdonképpen mindegy volt nekik.

K: – Milyen kiegészítő eszközöket használtak?

MS: – Legintenzívebben a govet és a golint eszközöket használtuk, hogy a stílus egységes legyen, valamint a gofmt. Többet nem használtunk.

K: – Mivel végezték a hibakeresést?

MS: – A hibakeresés alapvetően teszteken keresztül zajlott. Nincs debuggereink, GOP-ot nem használtunk.

K: – Vissza tudja hozni azt a diát, ahol a Publish funkció valósult meg? A betűrövidítések nem zavarják?

MS: – Nem. Különösen "keskeny" a hatókörük. Sehol sem használják őket, csak itt (kivéve ennek a klassznak a belső részeit), és nagyon kompakt – mindössze 7 sorból áll.

K: – Valahogy mégiscsak nem intuitív...

MS: – Nem-nem, ez valódi kód! Nem a stílusról van szó. Csak egy ilyen utilitáris, nagyon kis osztály – mindössze 3 mező van az osztályban...

Mihail Salosin. Golang Meetup. A Go alkalmazása a "Smtori+" backendjében

MS: – Alapvetően az összes adat, ami az ügyfelekkel szinkronizálódik (szezonális mérkőzések, játékosok), nem változik. Durván szólva, ha csinálunk egy másik sportágat, ahol szükséges lesz a mérkőzés módosítása, akkor azt egyszerűen figyelembe vesszük az új kliensem verziójában, míg a régi kliensek tiltva lesznek.

K: – Vannak külső csomagok a függőségek kezelésére?

MS: – Mi a go dep-t használtuk.

K: – A beszámoló témájában volt valami a videóról, de a beszámolóban nincs videó.

MS: – Nem, nekem a videóról a témámban semmi nincs. Az a címe, hogy 'Szemlézik+' – ez az alkalmazás neve.

K: – Ön azt mondta, hogy az ügyfeleknek streamel?...

MS: – A streamelt videóval nem foglalkoztunk. Ezt teljesen a 'Megafon' végezte. Igen, nem mondtam, hogy ez megafonos alkalmazás.

MS: – Go – az összes adat tömeges küldésére – számlákról, mérkőzésekről, statisztikákról… A Go teljes backend az alkalmazás számára. Az ügyfélnek valahonnan tudnia kell, melyik linket használja a lejátszóhoz, hogy a felhasználó megnézhessen egy mérkőzést. Nálunk vannak linkek a videókhoz és az élő közvetítésekhez, amelyek előkészítve állnak rendelkezésre.

Videó lejátszása

Egy kis reklám 🙂

Köszönjük, hogy velünk maradtál. Tetszenek a cikkeink? Szeretnél több érdekes anyagot látni? Támogass minket, és rendelés leadásával vagy ismerősöknek ajánlással segíts nekünk. Felhőalapú VPS fejlesztőknek 4,99 dollártól, Egyedülálló alternatívája az entry-level szervereknek, amelyet Önnek találtunk ki: Minden igazság a VPS (KVM) E5-2697 v3 (6 mag) 10GB DDR4 480GB SSD 1Gbps áron 19 dollártól, vagy hogyan oszd fel helyesen a szervert? (elérhetők RAID1 és RAID10 opciók, akár 24 magig és 40GB DDR4-ig).

Dell R730xd kétszer olcsóbban a Tier IV Equinix adatközpontban Amszterdamban? Csak nálunk 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TB 199 dollártól Hollandiában! Dell R420 — 2x E5-2430 2.2GHz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — 99 dollártól! Olvasd el ezt Hogyan építsünk vállalati szintű infrastruktúrát Dell R730xd E5-2650 v4 szerverekkel, amelyek költsége 9000 euróban van megszabva?

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster