Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Miĥail Salosin (ĉi-poste - MS): - Saluton al ĉiuj! Mia nomo estas Mikaelo. Mi laboras kiel backend-programisto ĉe MC2 Software, kaj mi parolos pri uzado de Go en la backend de la poŝtelefona aplikaĵo Look+.

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Ĉu iu ĉi tie ŝatas hokeon?

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Tiam ĉi tiu aplikaĵo estas por vi. Ĝi estas por Android kaj iOS kaj estas uzata por spekti elsendojn de diversaj sportaj eventoj interrete kaj registritaj. La aplikaĵo ankaŭ enhavas diversajn statistikojn, tekstajn elsendojn, tabelojn por konferencoj, turniroj kaj aliajn informojn utilajn por ŝatantoj.

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Ankaŭ en la aplikaĵo ekzistas io kiel videomomentoj, t.e. vi povas spekti la plej gravajn momentojn de matĉoj (goloj, bataloj, interpafadoj ktp.). Se vi ne volas spekti la tutan elsendon, vi povas spekti nur la plej interesajn.

Kion vi uzis en evoluo?

La ĉefparto estis skribita en Go. La API, kun kiu poŝtelefonaj klientoj komunikis, estis skribita en Go. Servo por sendi puŝajn sciigojn al poŝtelefonoj ankaŭ estis skribita en Go. Ni ankaŭ devis skribi nian propran ORM, pri kiu ni eble parolos iam. Nu, kelkaj malgrandaj servoj estis skribitaj en Go: regrandigi kaj ŝargi bildojn por la redaktantoj...

Ni uzis PostgreSQL kiel la datumbazon. La redaktila interfaco estis skribita en Ruby on Rails uzante la ActiveAdmin-gemon. Importi statistikojn de statistika provizanto ankaŭ estas skribita en Ruby.

Por sistemaj API-testoj, ni uzis Python-unuteston. Memcached estas uzata por akceli API-pagvokojn, "Chef" estas uzata por kontroli agordon, Zabbix estas uzata por kolekti kaj monitori internajn sistemajn statistikojn. Graylog2 estas por kolekti ŝtipojn, Slate estas API-dokumentado por klientoj.

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Selektado de protokolo

La unua problemo, kiun ni renkontis: ni devis elekti protokolon por interago inter la backend kaj moveblaj klientoj, surbaze de la sekvaj punktoj...

  • La plej grava postulo: datumoj pri klientoj devas esti ĝisdatigitaj en reala tempo. Tio estas, ĉiuj, kiuj nuntempe spektas la elsendon, devas ricevi ĝisdatigojn preskaŭ tuj.
  • Por simpligi aferojn, ni supozis, ke datumoj sinkronigitaj kun klientoj ne estas forigitaj, sed kaŝitaj per specialaj flagoj.
  • Ĉiaj maloftaj petoj (kiel ekzemple statistikoj, teamkonsistoj, teamstatistikoj) estas akiritaj per ordinaraj GET-petoj.
  • Krome, la sistemo devis facile subteni 100 mil uzantojn samtempe.

Surbaze de ĉi tio, ni havis du protokoleblojn:

  1. Websockets. Sed ni ne bezonis kanalojn de la kliento al la servilo. Ni nur bezonis sendi ĝisdatigojn de la servilo al la kliento, do websocket estas redunda opcio.
  2. Serviloj-Senditaj Eventoj (SSE) aperis ĝuste! Ĝi estas sufiĉe simpla kaj esence kontentigas ĉion, kion ni bezonas.

Servilo-Senditaj Eventoj

Kelkajn vortojn pri kiel ĉi tiu afero funkcias...

Ĝi funkcias supre de http-konekto. La kliento sendas peton, la servilo respondas per Content-Type: text/event-stream kaj ne fermas la ligon kun la kliento, sed daŭre skribas datumojn al la konekto:

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Datumoj povas esti senditaj en formato interkonsentita kun klientoj. En nia kazo, ni sendis ĝin en ĉi tiu formo: la nomo de la ŝanĝita strukturo (persono, ludanto) estis sendita al la eventokampo, kaj JSON kun novaj, ŝanĝitaj kampoj por la ludanto estis sendita al la datumkampo.

Nun ni parolu pri kiel funkcias la interago mem.

  • La unua afero, kiun la kliento faras, estas determini la lastan fojon kiam sinkronigo kun la servo estis farita: ĝi rigardas sian lokan datumbazon kaj determinas la daton de la lasta ŝanĝo registrita de ĝi.
  • Ĝi sendas peton kun ĉi tiu dato.
  • Responde, ni sendas al li ĉiujn ĝisdatigojn, kiuj okazis ekde tiu dato.
  • Post tio, ĝi faras konekton al la viva kanalo kaj ne fermiĝas ĝis ĝi bezonas ĉi tiujn ĝisdatigojn:

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Ni sendas al li liston de ŝanĝoj: se iu trafas golon, ni ŝanĝas la poentaron de la matĉo, se li vundiĝas, tio ankaŭ estas sendita en reala tempo. Tiel, klientoj tuj ricevas ĝisdatajn datumojn en la matĉa evento-fluo. Periode, por ke la kliento komprenu, ke la servilo ne mortis, ke nenio okazis al ĝi, ni sendas tempomarkon ĉiujn 15 sekundojn - por ke ĝi sciu, ke ĉio estas en ordo kaj ne necesas rekonekti.

Kiel la viva konekto estas servata?

  • Antaŭ ĉio, ni kreas kanalon en kiu bufraj ĝisdatigoj estos ricevitaj.
  • Post tio, ni abonas ĉi tiun kanalon por ricevi ĝisdatigojn.
  • Ni starigas la ĝustan kaplinion por ke la kliento sciu, ke ĉio estas en ordo.
  • Sendu la unuan pingon. Ni simple registras la nunan konektan tempomarkon.
  • Post tio, ni legas de la kanalo en buklo ĝis la ĝisdatiga kanalo estas fermita. La kanalo periode ricevas aŭ la nunan tempomarkon aŭ ŝanĝojn, kiujn ni jam skribas por malfermi konektojn.

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

La unua problemo, kiun ni renkontis, estis la sekva: por ĉiu konekto malfermita kun la kliento, ni kreis tempigilon, kiu tiktakis unufoje ĉiujn 15 sekundojn - rezultas, ke se ni havus 6 mil konektojn malfermitaj per unu maŝino (kun unu API-servilo), 6 mil tempigiloj estis kreitaj. Ĉi tio kondukis al la maŝino ne tenante la postulatan ŝarĝon. La problemo ne estis tiel evidenta al ni, sed ni ricevis iom da helpo kaj riparis ĝin.

Kiel rezulto, nun nia ping venas de la sama kanalo de kiu venas ĝisdatigo.

Sekve, ekzistas nur unu tempigilo, kiu batas unufoje ĉiujn 15 sekundojn.

Estas pluraj helpaj funkcioj ĉi tie - sendado de la kaplinio, ping kaj la strukturo mem. Tio estas, la nomo de la tabelo (persono, matĉo, sezono) kaj la informoj pri ĉi tiu enskribo estas transdonitaj ĉi tie:

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Mekanismo por sendi ĝisdatigojn

Nun iom pri kie venas la ŝanĝoj. Ni havas plurajn homojn, redaktorojn, kiuj spektas la elsendon en reala tempo. Ili kreas ĉiujn eventojn: iu estis forsendita, iu vundita, ia anstataŭaĵo...

Uzante CMS, datumoj eniras la datumbazon. Post ĉi tio, la datumbazo sciigas la API-servilojn pri tio uzante la Aŭskultu/Sentigi mekanismon. API-serviloj jam sendas ĉi tiujn informojn al klientoj. Tiel, ni esence havas nur kelkajn servilojn konektitajn al la datumbazo kaj ne estas speciala ŝarĝo sur la datumbazo, ĉar la kliento neniel interagas rekte kun la datumbazo:

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

PostgreSQL: Aŭskultu/Sciigi

La mekanismo Aŭskultu/Sciigi en Postgres ebligas al vi sciigi al evento-abonantoj, ke iu evento ŝanĝiĝis - iu rekordo estis kreita en la datumbazo. Por fari tion, ni skribis simplan ellasilon kaj funkcion:

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Kiam oni enmetas aŭ ŝanĝas rekordon, ni nomas la notigan funkcion sur la kanalo data_updates, pasigante tie la nomon de la tabelo kaj la identigilon de la rekordo, kiu estis ŝanĝita aŭ enigita.

Por ĉiuj tabeloj, kiuj devas esti sinkronigitaj kun la kliento, ni difinas ellasilon, kiu, post ŝanĝi/ĝisdatigi rekordon, nomas la funkcion indikitan sur la suba glito.
Kiel la API abonas ĉi tiujn ŝanĝojn?

Fanout-mekanismo estas kreita - ĝi sendas mesaĝojn al la kliento. Ĝi kolektas ĉiujn klientajn kanalojn kaj sendas ĝisdatigojn kiujn ĝi ricevis per ĉi tiuj kanaloj:

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Ĉi tie la norma pq-biblioteko, kiu konektas al la datumbazo kaj diras, ke ĝi volas aŭskulti la kanalon (data_updates), kontrolas, ke la konekto estas malfermita kaj ĉio estas en ordo. Mi preterlasas erarkontroladon por ŝpari spacon (ne kontroli estas danĝera).

Poste, ni nesinkrone starigas Ticker, kiu sendos ping ĉiujn 15 sekundojn, kaj komencos aŭskulti la kanalon, al kiu ni abonis. Se ni ricevas ping-on, ni publikigas ĉi tiun ping-on. Se ni ricevas ian eniron, tiam ni publikigas ĉi tiun eniron al ĉiuj abonantoj de ĉi tiu Fanout.

Kiel funkcias Fan-out?

En la rusa tio tradukiĝas kiel "splitter". Ni havas unu objekton, kiu registras abonantojn, kiuj volas ricevi iujn ĝisdatigojn. Kaj tuj kiam ĝisdatigo alvenas al ĉi tiu objekto, ĝi distribuas ĉi tiun ĝisdatigon al ĉiuj siaj abonantoj. Sufiĉe simpla:

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Kiel ĝi estas efektivigita en Go:

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Estas strukturo, ĝi estas sinkronigita uzante Mutexs. Ĝi havas kampon, kiu konservas la staton de la konekto de Fanout al la datumbazo, t.e. ĝi nuntempe aŭskultas kaj ricevos ĝisdatigojn, kaj ankaŭ liston de ĉiuj disponeblaj kanaloj - mapo, kies ŝlosilo estas la kanalo kaj strukturo en la formo de valoroj (esence ĝi neniel uzata).

Du metodoj - Konektita kaj Malkonektita - permesas al ni diri al Fanout, ke ni havas konekton al la bazo, ĝi aperis kaj ke la ligo al la bazo estas rompita. En la dua kazo, vi devas malkonekti ĉiujn klientojn kaj diri al ili, ke ili ne plu povas aŭskulti ion ajn kaj ke ili rekonektas ĉar la konekto al ili fermiĝis.

Ekzistas ankaŭ Abona metodo kiu aldonas la kanalon al la "aŭskultantoj":

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Estas Malsubscribe-metodo, kiu forigas la kanalon de aŭskultantoj se la kliento malkonektas, kaj ankaŭ Publish-metodo, kiu permesas sendi mesaĝon al ĉiuj abonantoj.

Demando: – Kio estas transdonita per ĉi tiu kanalo?

MS: – La modelo kiu ŝanĝiĝis aŭ ping estas transdonita (esence nur nombro, entjero).

MS: - Vi povas sendi ion ajn, sendi ajnan strukturon, publikigi ĝin - ĝi nur iĝas JSON kaj jen ĝi.

MS: – Ni ricevas sciigon de Postgres – ĝi enhavas la tabelnomon kaj identigilon. Surbaze de la tabelnomo kaj identigilo, ni ricevas la rekordon, kiun ni bezonas, kaj tiam ni sendas ĉi tiun strukturon por publikigo.

Infrastrukturo

Kiel ĉi tio aspektas el infrastruktura perspektivo? Ni havas 7 aparatarservilojn: unu el ili estas tute dediĉita al la datumbazo, la aliaj ses funkciigas virtualajn maŝinojn. Estas 6 kopioj de la API: ĉiu virtuala maŝino kun la API funkcias per aparta aparatara servilo - ĉi tio estas por fidindeco.

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Ni havas du fasadojn kun Keepalived instalita por plibonigi alireblecon, tiel ke se io okazas, unu fasado povas anstataŭigi la alian. Ankaŭ - du kopioj de la CMS.

Ekzistas ankaŭ statistika importisto. Estas DB Slave de kiu sekurkopioj estas faritaj periode. Estas Pigeon Pusher, aplikaĵo, kiu sendas puŝajn sciigojn al klientoj, same kiel infrastrukturajn aferojn: Zabbix, Graylog2 kaj Chef.

Fakte, ĉi tiu infrastrukturo estas superflua, ĉar 100 mil povas esti servataj per malpli da serviloj. Sed estis fero — ni uzis ĝin (oni diris al ni, ke eblas — kial ne).

Avantaĝoj de Go

Post kiam ni laboris pri ĉi tiu aplikaĵo, tiaj evidentaj avantaĝoj de Go aperis.

  • Cool http-biblioteko. Per ĝi vi povas krei sufiĉe multe el la skatolo.
  • Plie, kanaloj, kiuj permesis al ni tre facile efektivigi mekanismon por sendi sciigojn al klientoj.
  • La mirinda afero Race-detektilo permesis al ni forigi plurajn kritikajn cimojn (sceniga infrastrukturo). Ĉio, kio funkcias pri surscenigo, estas lanĉita, kompilita per la Race-klavo; kaj ni, sekve, povas rigardi la aranĝan infrastrukturon por vidi kiajn eblajn problemojn ni havas.
  • Minimumismo kaj simpleco de lingvo.

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

Ni serĉas programistojn! Se iu volas, bv.

Viaj demandoj

Demando de la publiko (ĉi-poste – B): – Ŝajnas al mi, ke vi maltrafis unu gravan punkton pri Fan-out. Ĉu mi pravas kompreni, ke kiam oni sendas respondon al kliento, oni blokas se la kliento ne volas legi?

MS: - Ne, ni ne blokas. Unue, ni havas ĉion ĉi malantaŭ nginx, tio estas, ne estas problemoj kun malrapidaj klientoj. Due, la kliento havas kanalon kun bufro - fakte, ni povas meti ĝis cent ĝisdatigojn tie... Se ni ne povas skribi al la kanalo, tiam ĝi forigas ĝin. Se ni vidas, ke la kanalo estas blokita, tiam ni simple fermos la kanalon, kaj jen - la kliento rekonektos se aperos ia problemo. Tial, principe, ĉi tie ne estas blokado.

En: – Ĉu ne eblis tuj sendi rekordon al Aŭskultu/Averti, kaj ne identigilon?

MS: – Aŭskultu/Notigi havas limon de 8 mil bajtoj sur la antaŭŝarĝo, kiun ĝi sendas. Principe eblus sendi, se ni traktus malgrandan kvanton da datumoj, sed ŝajnas al mi, ke tiel [kiel ni faras tion] estas simple pli fidinda. La limigoj estas en Postgres mem.

En: – Ĉu klientoj ricevas ĝisdatigojn pri matĉoj, pri kiuj ili ne interesiĝas?

MS: - Ĝenerale, jes. Kiel regulo, okazas 2-3 matĉoj paralele, kaj eĉ tiam sufiĉe malofte. Se kliento rigardas ion, tiam kutime li rigardas la matĉon kiu okazas. Tiam, la kliento havas lokan datumbazon en kiu ĉiuj ĉi tiuj ĝisdatigoj estas aldonitaj, kaj eĉ sen Interreta konekto, la kliento povas vidi ĉiujn pasintajn matĉojn por kiuj li havas ĝisdatigojn. Esence, ni sinkronigas nian datumbazon sur la servilo kun la loka datumbazo de la kliento por ke li povu labori eksterrete.

En: – Kial vi faris vian propran ORM?

Alexey (unu el la programistoj de Look+): – Tiutempe (estis antaŭ unu jaro) estis malpli da ORM-oj ol nun, kiam ili estas sufiĉe multe. Mia plej ŝatata afero pri la plej multaj ORM-oj tie estas, ke la plej multaj el ili funkcias per malplenaj interfacoj. Tio estas, la metodoj en ĉi tiuj ORM-oj pretas alpreni ion ajn: strukturon, strukturmontrilon, nombron, ion tute sensigniva...

Nia ORM generas strukturojn bazitajn sur la datummodelo. Mi mem. Kaj tial ĉiuj metodoj estas konkretaj, ne uzas reflektadon, ktp. Ili akceptas strukturojn kaj atendas uzi tiujn strukturojn kiuj venas.

En: – Kiom da homoj partoprenis?

MS: – En la komenca etapo, du homoj partoprenis. Ni komencis ie en junio, kaj en aŭgusto la ĉefa parto estis preta (la unua versio). Estis liberigo en septembro.

En: – Kie vi priskribas SSE, vi ne uzas timeout. Kial estas tio?

MS: – Verdire, SSE ankoraŭ estas html5-protokolo: la SSE-normo estas desegnita por komuniki kun retumiloj, laŭ mia kompreno. Ĝi havas kromajn funkciojn por ke retumiloj povu rekonekti (kaj tiel plu), sed ni ne bezonas ilin, ĉar ni havis klientojn, kiuj povus efektivigi ajnan logikon por konekti kaj ricevi informojn. Ni ne faris SSE, sed prefere ion similan al SSE. Ĉi tio ne estas la protokolo mem.
Ne estis bezono. Kiom mi komprenas, klientoj efektivigis la ligan mekanismon preskaŭ de nulo. Ili ne vere zorgis.

En: – Kiajn kromajn ilojn vi uzis?

MS: – Ni plej aktive uzis govet kaj golint por igi la stilon unuigita, same kiel gofmt. Nenio alia estis uzata.

En: – Kion vi uzis por sencimigi?

MS: – Sencimigado estis plejparte efektivigita per testoj. Ni ne uzis ajnan erarserĉilon aŭ GOP.

En: – Ĉu vi povas redoni la lumbildon kie la Publiki funkcio estas efektivigita? Ĉu unuliteraj variablonomoj konfuzas vin?

MS: - Ne. Ili havas sufiĉe "mallarĝan" videblecon. Ili ne estas uzataj aliloke krom ĉi tie (krom la internoj de ĉi tiu klaso), kaj ĝi estas tre kompakta - ĝi bezonas nur 7 liniojn.

En: – Iel ĝi ankoraŭ ne estas intuicia...

MS: - Ne, ne, ĉi tio estas vera kodo! Ne temas pri stilo. Ĝi estas nur tia utilisma, tre malgranda klaso - nur 3 kampoj ene de la klaso...

Miĥail Salosin. Golang Renkontiĝo. Uzante Go en la malantaŭo de la aplikaĵo Look+

MS: – Ĝenerale, ĉiuj datumoj sinkronigitaj kun klientoj (sezonaj matĉoj, ludantoj) ne ŝanĝiĝas. Malglate parolante, se ni faras alian sporton en kiu ni devas ŝanĝi la matĉon, ni simple konsideros ĉion en la nova versio de la kliento, kaj la malnovaj versioj de la kliento estos malpermesitaj.

En: – Ĉu ekzistas pakoj pri administrado de dependaj triaj?

MS: – Ni uzis go dep.

En: – Estis io pri video en la temo de la raporto, sed nenio estis en la raporto pri video.

MS: – Ne, mi havas nenion en la temo pri la video. Ĝi nomiĝas "Rigardu+" - jen la nomo de la aplikaĵo.

En: – Ĉu vi diris, ke ĝi estas elsendita al klientoj?...

MS: – Ni ne okupiĝis pri fluado de video. Ĉi tio estis tute farita de Megafon. Jes, mi ne diris, ke la aplikaĵo estas MegaFon.

MS: – Iru – por sendi ĉiujn datumojn – pri la poentaro, pri matĉokazaĵoj, statistikoj... Go estas la tuta backend por la aplikaĵo. La kliento devas scii de ie kiun ligon uzi por la ludanto por ke la uzanto povu spekti la matĉon. Ni havas ligilojn al videoj kaj riveretoj kiuj estis preparitaj.

Kelkaj reklamoj 🙂

Dankon pro restado ĉe ni. Ĉu vi ŝatas niajn artikolojn? Ĉu vi volas vidi pli interesan enhavon? Subtenu nin farante mendon aŭ rekomendante al amikoj, nuba VPS por programistoj de $4.99, unika analogo de enirnivelaj serviloj, kiu estis inventita de ni por vi: La tuta vero pri VPS (KVM) E5-2697 v3 (6 Kernoj) 10GB DDR4 480GB SSD 1Gbps de $ 19 aŭ kiel dividi servilon? (havebla kun RAID1 kaj RAID10, ĝis 24 kernoj kaj ĝis 40GB DDR4).

Dell R730xd 2 fojojn pli malmultekosta en Equinix Tier IV datumcentro en Amsterdamo? Nur ĉi tie 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 televidilo ekde 199 USD en Nederlando! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - ekde $99! Legu pri Kiel konstrui infrastrukturan korpon. klaso kun la uzo de serviloj Dell R730xd E5-2650 v4 valorantaj 9000 eŭrojn por centono?

fonto: www.habr.com

Aldoni komenton