Mihail Salosin (jäljempänä MS): Hei kaikki! Nimeni on Mikhail. Olen taustajärjestelmäkehittäjä MC2 Softwarella, ja aion kertoa Go:n käytöstä "Smotri+"-mobiilisovelluksen taustajärjestelmässä.

Tykkääkö kukaan täällä jääkiekosta?

Sitten tämä sovellus on sinua varten. Saatavilla Androidille ja iOS:lle, se mahdollistaa erilaisten urheilutapahtumien live- ja tallennettujen lähetysten katsomisen. Sovellus sisältää myös erilaisia tilastoja, live-tekstisyötteitä, konferenssien ja turnausten sijoituksia sekä muita hyödyllisiä tietoja faneille.

Sovelluksessa on myös videokohokohtia, joiden avulla voit katsoa otteluiden tärkeimmät hetket (maalit, tappelut, rangaistuslaukaukset jne.). Jos et halua katsoa koko lähetystä, voit katsoa vain kohokohdat.
Mitä kehitystyössä käytettiin?
Pääosa kirjoitettiin Golla. Mobiiliasiakkaiden käyttämä API oli kirjoitettu Golla. Push-ilmoitusten lähettämiseen mobiililaitteille tarkoitettu palvelu oli myös kirjoitettu Golla. Meidän piti myös kirjoittaa oma ORM, josta saatamme puhua jonain päivänä. Ja joitakin pienempiä palveluita kirjoitettiin Golla: koon muuttaminen ja kuvien lataus editoreille...
Tietokanta-alustanamme käytimme PostgreSQL:ää. Editorin käyttöliittymä oli kirjoitettu Ruby on Railsilla käyttäen ActiveAdmin gemiä. Tilastopalvelun tilastojen tuontityökalu oli myös kirjoitettu Rubylla.
Järjestelmän API-testaukseen käytimme Pythonin unittestiä. Memcachedia käytetään API-maksupyyntöjen rajoittamiseen, Chefiä konfiguroinnin hallintaan, Zabbixia sisäisten järjestelmätilastojen keräämiseen ja valvontaan, Graylog2:ta lokien keräämiseen ja Slate on asiakkaiden API-dokumentaatio.

Protokollan valinta
Ensimmäinen kohtaamamme ongelma oli protokollan valinta mobiiliasiakkaiden kanssa tapahtuvaa taustajärjestelmän vuorovaikutusta varten seuraavien seikkojen perusteella…
- Tärkein vaatimus on, että asiakasdatan on päivityttävä reaaliajassa. Tämä tarkoittaa, että kaikkien lähetystä katsovien on saatava päivitykset lähes välittömästi.
- Yksinkertaisuuden vuoksi olemme olettaneet, että asiakkaiden kanssa synkronoituja tietoja ei poisteta, vaan ne piilotetaan erityisten lippujen avulla.
- Kaikki harvinaiset pyynnöt (kuten tilastot, joukkueiden kokoonpanot, joukkuetilastot) vastaanotetaan tavallisten GET-pyyntöjen kautta.
- Lisäksi järjestelmän piti pystyä helposti käsittelemään 100 000 samanaikaista käyttäjää.
Tämän perusteella meillä oli kaksi protokollavaihtoehtoa:
- Websocketit. Mutta emme tarvinneet kanavia asiakkaalta palvelimelle. Meidän piti vain lähettää päivityksiä palvelimelta asiakkaalle, joten websocket oli liioittelua.
- Server-Sent Events (SSE) oli täydellinen ratkaisu! Se on melko yksinkertainen ja täyttää periaatteessa kaiken tarvitsemamme.
Palvelimen lähettämät tapahtumat
Muutama sana siitä, miten tämä toimii...
Se toimii HTTP-yhteyden yli. Asiakas lähettää pyynnön, palvelin vastaa Content-Type: text/event-stream -määrityksellä eikä sulje yhteyttä asiakkaaseen, vaan jatkaa datan kirjoittamista yhteyteen:

Data voidaan lähettää asiakkaiden kanssa sovitussa muodossa. Meidän tapauksessamme lähetimme sen näin: tapahtumakenttä sisälsi muuttuneen rakenteen (henkilö, pelaaja) nimen ja datakenttä sisälsi JSON-tiedoston pelaajan uusien, muokattujen kenttien kanssa.
Nyt siitä, miten itse vuorovaikutus toimii.
- Ensimmäiseksi asiakasohjelma selvittää, milloin viimeisin synkronointi palvelun kanssa tapahtui: se tarkastelee paikallista tietokantaa ja määrittää sinne viimeksi tallennetun muutoksen päivämäärän.
- Hän lähettää pyynnön tällä päivämäärällä.
- Vastauksena lähetämme hänelle kaikki kyseisen päivämäärän jälkeen tapahtuneet päivitykset.
- Tämän jälkeen se muodostaa yhteyden live-kanavaan eikä sulje sitä ennen kuin se tarvitsee näitä päivityksiä:

Lähetämme sille listan muutoksista: jos joku tekee maalin, tilanne muuttuu tai jos joku loukkaantuu, sekin lähetetään reaaliajassa. Tällä tavoin asiakkaat saavat välittömästi ajantasaista tietoa ottelusyötteessä. Lähetämme sille säännöllisesti 15 sekunnin välein aikaleiman, jotta asiakas tietää, että palvelin ei ole kuollut eikä sille ole tapahtunut mitään – jotta he tietävät kaiken olevan hyvin eivätkä heidän tarvitse muodostaa yhteyttä uudelleen.
Miten reaaliaikaista yhteyttä palvellaan?
- Ensinnäkin luomme kanavan, joka vastaanottaa päivityksiä puskurin avulla.
- Tämän jälkeen tilaamme tämän kanavan saadaksemme päivityksiä.
- Asetimme oikean otsikon, jotta asiakas tietää kaiken olevan kunnossa.
- Lähetämme ensimmäisen ping-komennon. Tallennamme vain nykyisen yhteyden aikaleiman.
- Tämän jälkeen luemme kanavalta silmukassa, kunnes päivityskanava suljetaan. Kanava vastaanottaa säännöllisesti joko nykyisen aikaleiman tai muutokset, jotka sitten kirjoitetaan avoimille yhteyksille.

Ensimmäinen kohtaamamme ongelma oli tämä: jokaista asiakasohjelman kanssa avattua yhteyttä kohden loimme ajastimen, joka tikitti 15 sekunnin välein. Joten jos meillä oli 6 000 yhteyttä auki yhteen koneeseen (yhteen API-palvelimeen), luotiin 6 000 ajastinta. Tämä tarkoitti, että kone ei pystynyt käsittelemään vaadittua kuormitusta. Ongelma ei ollut meille heti ilmeinen, mutta saimme apua ja korjasimme sen.
Tämän seurauksena meillä on nyt ping-kutsu samasta kanavasta, josta päivitys tulee.
Näin ollen on vain yksi ajastin, joka tikittää 15 sekunnin välein.
Tässä on useita apufunktioita – otsikon, pingin ja itse rakenteen lähettäminen. Eli taulukon nimi (henkilö, osuma, kausi) ja tiedot tästä tietueesta välitetään täällä:

Päivitysten lähettämismekanismi
Seuraavaksi hieman muutosten alkuperästä. Meillä on useita toimittajia, jotka katsovat lähetystä reaaliajassa. He luovat kaikki tapahtumat: joku ajetaan ulos, joku loukkaantuu, joku vaihtaa...
CMS tallentaa tiedot tietokantaan. Tietokanta käyttää sitten Listen/Notify-mekanismia ilmoittaakseen API-palvelimille. API-palvelimet lähettävät sitten nämä tiedot asiakkaille. Näin ollen meillä on käytännössä vain muutama palvelin yhdistettynä tietokantaan, eikä tietokantaan kohdistu merkittävää kuormitusta, koska asiakas ei ole suorassa vuorovaikutuksessa tietokannan kanssa.

PostgreSQL: Kuuntele/Ilmoita
PostgreSQL:n Listen/Notify-mekanismin avulla voit ilmoittaa tapahtuman tilaajille, kun tapahtuma on muuttunut – eli kun tietokantatietue on luotu. Tätä varten kirjoitimme yksinkertaisen liipaisimen ja funktion:

Kun lisäämme tai muutamme tietuetta, kutsumme data_updates-kanavan notify-funktiota ja välitämme sille muutetun tai lisätyn tietueen taulukon nimen ja tunnuksen.
Kaikille asiakkaan kanssa synkronoitaville taulukoille määrittelemme liipaisimen, joka kutsuu alla olevassa diassa määritettyä funktiota tietueen muuttamisen/päivityksen jälkeen.
Miten API liittyy näihin muutoksiin?
Luodaan Fanout-mekanismi, joka lähettää viestejä asiakkaille. Se kerää kaikki asiakaskanavat ja lähettää näiden kanavien kautta vastaanotetut päivitykset:

Tässä on standardi pq-kirjasto, joka muodostaa yhteyden tietokantaan ja ilmoittaa haluavansa kuunnella kanavaa (data_updates) ja tarkistaa, että yhteys on auki ja kaikki on kunnossa. Jätän virheentarkistuksen pois säästääkseni tilaa (tarkistamatta jättäminen on vaarallista).
Seuraavaksi määritämme asynkronisesti Tikkerin, joka lähettää ping-signaalin 15 sekunnin välein ja alkaa kuunnella tilaamaamme kanavaa. Jos saamme ping-signaalin, julkaisemme sen. Jos saamme tallenteen, julkaisemme sen kaikille tämän Fanoutin tilaajille.
Miten Fan-out toimii?
Venäjäksi tämä tarkoittaa "jakajaa". Meillä on yksi objekti, joka rekisteröi tilaajat, jotka haluavat vastaanottaa tiettyjä päivityksiä. Ja heti kun päivitys saapuu tälle objektille, se jakaa sen kaikille tilaajilleen. Se on melko yksinkertaista:

Näin se toteutetaan Gossa:

Mutexien avulla synkronoitu rakenne sisältää kentän, joka tallentaa Fanoutin yhteyden tietokantaan – eli tiedon siitä, kuunteleeko se parhaillaan ja vastaanottaako se päivityksiä – sekä luettelon kaikista käytettävissä olevista kanavista – mapin, jonka avain on kanava, ja rakenteen, joka sisältää arvot (joita ei itse asiassa käytetä).
Kahden menetelmän – Yhdistetty ja Katkaistu – avulla voimme kertoa Fanoutille, että meillä on yhteys tietokantaan, että se on muodostettu ja että yhteys tietokantaan on katkennut. Toisessa tapauksessa meidän on katkaistava yhteys kaikkiin asiakkaisiin ja ilmoitettava heille, että he eivät enää voi kuunnella mitään ja että heidän tulisi muodostaa yhteys uudelleen, koska yhteys heihin on katkennut.
Käytössä on myös Subscribe-metodi, joka lisää kanavan "kuuntelijoihin":

Käytettävissä on Unsubscribe-metodi, joka poistaa kanavan kuuntelulistoilta, jos asiakas katkaisee yhteyden, ja Publish-metodi, jonka avulla voit lähettää viestin kaikille tilaajille.
Kysymys: – Mitä tätä kanavaa pitkin välitetään?
MS: – Muuttunut malli tai ping (pohjimmiltaan vain numero, kokonaisluku) lähetetään.
MS: – Voit lähettää tai julkaista mitä tahansa, minkä tahansa rakenteen – se vain muuttuu JSON-muotoon ja siinä kaikki.
MS: Saamme PostgreSQL:ltä ilmoituksen, joka sisältää taulukon nimen ja tunnisteen. Taulukon nimen ja tunnisteen avulla haemme tarvitsemamme tietueen ja lähetämme tämän rakenteen julkaistavaksi.
infrastruktuuri
Miltä tämä näyttää infrastruktuurin näkökulmasta? Meillä on seitsemän laitteistopalvelinta: yksi on kokonaan omistettu tietokannalle ja kuusi muuta ajaa virtuaalikoneita. API-instansseja on kuusi: jokainen API:a ajava virtuaalikone toimii erillisellä laitteistopalvelimella luotettavuuden takaamiseksi.

Meillä on kaksi käyttöliittymää, joissa Keepalived on käytössä esteettömyyden takaamiseksi, joten toinen käyttöliittymä voi korvata toisen tarvittaessa. Meillä on myös kaksi kopiota sisällönhallintajärjestelmästä.
Käytettävissä on myös tilastojen tuontityökalu. Käytettävissä on tietokannan orja, joka varmuuskopioi tiedot säännöllisesti. Käytettävissä on Pigeon Pusher, sovellus, joka lähettää push-ilmoituksia asiakkaille, sekä infrastruktuurikomponentteja: Zabbix, Graylog2 ja Chef.
Todellisuudessa tämä infrastruktuuri on redundantti, koska 100 000 käyttäjää voitaisiin palvella vähemmillä palvelimilla. Mutta meillä oli laitteisto – käytimme sitä (meille kerrottiin, että se oli mahdollista – joten miksi ei).
Go-ohjelman plussat
Työskenneltyäni tämän sovelluksen parissa, Go:n seuraavat ilmeiset edut tulivat ilmeisiksi.
- Loistava HTTP-kirjasto. Sen avulla voit luoda paljon suoraan pakkauksesta.
- Lisäksi kanavien avulla pystyimme helposti toteuttamaan mekanismin ilmoitusten lähettämiseksi asiakkaille.
- Race Detector on loistava ominaisuus, jonka avulla poistimme useita kriittisiä bugeja (testausympäristö). Kaikki staging-ympäristössä käynnissä oleva sisältö käännetään Race-avaimella, joten voimme käyttää staging-infrastruktuuria nähdäksemme, mitä mahdollisia ongelmia meillä saattaa olla.
- Minimalismi ja kielen yksinkertaisuus.

Etsimme kehittäjiä! Jos joku on kiinnostunut, liity joukkoomme.
kysymykset
Yleisökysymys (jäljempänä "Yleisökysymys"): Mielestäni sinulta jäi huomaamatta yksi tärkeä kohta Fan-out-ominaisuuteen liittyen. Ymmärsinkö oikein, että kun lähetät vastauksen asiakkaalle, estät hänet, jos hän ei halua lukea sitä?
MS: "Ei, meitä ei estetä. Ensinnäkin kaikki on Nginxin takana, joten hitaiden asiakasohjelmien kanssa ei ole ongelmia. Toiseksi, asiakasohjelmalla on kanava, jossa on puskuri – voimme tallentaa sinne käytännössä jopa sata päivitystä... Jos emme voi kirjoittaa kanavalle, se poistaa sen. Jos näemme, että kanava on estetty, suljemme kanavan, ja siinä kaikki – asiakasohjelma muodostaa yhteyden uudelleen, jos ongelmia ilmenee. Joten periaatteessa tässä ei ole estoa."
in: – Eikö tietuetta voinut lähettää välittömästi Listen/Notify-palveluun tunnistetaulukon sijaan?
MS: – Listen/Notifylla on 8 000 tavun rajoitus lähettämään esilataukseen. Periaatteessa sen lähettäminen olisi mahdollista, jos käsittelisimme pientä datamäärää, mutta mielestäni se on yksinkertaisesti luotettavampaa tällä tavalla. Rajoitukset ovat itse PostgreSQL:ssä.
in: – Saavatko asiakkaat päivityksiä osumista, joista he eivät ole kiinnostuneita?
MS: – Yleisesti ottaen kyllä. Yleensä käynnissä on kaksi tai kolme ottelua samanaikaisesti, ja silloinkin se on melko harvinaista. Jos asiakas katsoo jotain, hän katsoo yleensä parhaillaan käynnissä olevaa ottelua. Tällöin asiakkaalla on paikallinen tietokanta, johon kaikki nämä päivitykset tallennetaan, ja jopa ilman internetyhteyttä asiakas voi katsoa kaikki aiemmat ottelut, joista hänellä on päivityksiä. Pohjimmiltaan synkronoimme palvelimella olevan tietokantaamme asiakkaan paikallisen tietokannan kanssa, joten se voi toimia offline-tilassa.
in: – Miksi loit oman ORM:n?
Alexey (yksi "Smotri+":n kehittäjistä): – Tuolloin (tämä oli vuosi sitten) ORM-järjestelmiä oli vähemmän kuin nyt, kun niitä on melko paljon. Kaikista olemassa olevista ORM-järjestelmistä en pidä eniten siitä, että useimmat niistä toimivat tyhjillä rajapinnoilla. Toisin sanoen näiden ORM-järjestelmien metodit ovat valmiita hyväksymään mitä tahansa: rakenteen, osoittimen rakenteeseen, numeron, mitä tahansa täysin epäolennaista…
ORM-järjestelmämme luo rakenteita itse datamallin perusteella. Siksi kaikki metodit ovat konkreettisia, eivätkä käytä peittelyä jne. Ne hyväksyvät rakenteita ja odottavat käyttävänsä annettuja rakenteita.
in: – Kuinka monta ihmistä osallistui?
MS: – Alkuvaiheessa oli mukana kaksi ihmistä. Aloitimme kesäkuun tienoilla, ja pääosa (ensimmäinen versio) oli valmis elokuussa. Julkaisu oli syyskuussa.
in: – Kun kuvailet SSE:tä, et käytä aikakatkaisua. Miksi?
MS: "Ollakseni rehellinen, SSE on edelleen HTML5-protokolla: SSE-standardi on suunniteltu selainten kanssa kommunikointiin, sikäli kuin ymmärrän. Siinä on lisäominaisuuksia, joiden avulla selaimet voivat muodostaa yhteyden uudelleen (ja niin edelleen), mutta emme tarvinneet niitä, koska meillä oli asiakkaita, jotka pystyivät toteuttamaan minkä tahansa yhteys- ja tiedonhakulogiikan. Emme luoneet SSE:tä, vaan jotain SSE:n kaltaista. Se ei ole itse protokolla."
Ei ollut tarvetta. Ymmärtääkseni asiakkaat toteuttivat yhteysmekanismin käytännössä tyhjästä. Heitä ei periaatteessa kiinnostanut.
in: – Mitä lisätyökaluja käytit?
MS: – Käytimme laajimmin govet- ja golint-skriptejä yhtenäisen tyylin säilyttämiseksi, samoin kuin gofmt-skriptiä. Emme käyttäneet mitään muuta.
in: – Mitä käytit sen debugaamiseen?
MS: – Virheenkorjaus tehtiin pääosin testien avulla. Emme käyttäneet virheenkorjaajia tai GOP:ia.
in: – Voitko palauttaa dian, jossa Julkaise-funktio on toteutettu? Ovatko yksikirjaimiset muuttujien nimet hämmentäviä?
MS: "Ei. Niiden soveltamisala on melko kapea. Niitä ei käytetä missään muualla paitsi täällä (paitsi tämän luokan sisäisissä osissa), ja se on hyvin kompakti – se vie vain seitsemän riviä."
in: – Jotenkin se ei vieläkään tunnu intuitiiviselta…
MS: "Ei, ei, tämä on oikeaa koodia! Kyse ei ole tyylistä. Se on vain hyvin utilitaristinen, hyvin pieni luokka – vain kolme kenttää luokan sisällä..."

MS: – Suurimmaksi osaksi kaikki asiakkaiden kanssa synkronoidut tiedot (kausiottelut, pelaajat) pysyvät muuttumattomina. Karkeasti sanottuna, jos kehittäisimme toisen urheilulajin, joka vaatisi otteluiden muokkaamista, ottaisimme kaiken huomioon uudessa asiakasversiossa ja vanhemmat versiot kiellettäisiin.
in: – Onko olemassa kolmannen osapuolen paketteja riippuvuuksien hallintaan?
MS: – Käytimme go dep -sovellusta.
in: – Raportin aiheessa mainittiin jotain videosta, mutta raportissa itsessään videota ei mainittu.
MS: "Ei, minulla ei ole mitään videoista ketjussani. Se on nimeltään "Watch+" – se on sovelluksen nimi."
in: - Sanoit, että striimaat asiakkaille?
MS: "Emme olleet mukana videoiden suoratoistossa. Megafon hoiti sen kokonaan. Kyllä, en sanonut, että sovellus oli Megafonin."
MS: – Go – kaikkien tietojen lähettämiseen – tulokset, ottelutapahtumat, tilastot… Go on sovelluksen koko taustajärjestelmä. Asiakkaan on tiedettävä jostain, mitä linkkiä pelaaja käyttää, jotta käyttäjä voi katsoa ottelun. Meillä on valmiiksi valmiita linkkejä videoihin ja striimeihin.
Muutamia mainoksia 🙂
Kiitos, että pysyt kanssamme. Pidätkö artikkeleistamme? Haluatko nähdä mielenkiintoisempaa sisältöä? Tue meitä tekemällä tilauksen tai suosittelemalla ystäville, , ainutlaatuinen lähtötason palvelimien analogi, jonka me keksimme sinulle: (saatavana RAID1:n ja RAID10:n kanssa, jopa 24 ydintä ja jopa 40 Gt DDR4-muistia).
Dell R730xd 2 kertaa halvempi Equinix Tier IV -palvelinkeskuksessa Amsterdamissa? Vain täällä Alankomaissa! Dell R420 - 2x E5-2430 2.2 Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - alkaen 99 dollaria! Lukea
Lähde: will.com
