Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Mikhail Salosin (në tekstin e mëtejmë - MS): - Pershendetje te gjitheve! Emri im eshte Majkell. Unë punoj si zhvillues i backend-it në MC2 Software dhe do të flas për përdorimin e Go në pjesën e pasme të aplikacionit celular Look+.

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

A i pëlqen dikush këtu hokej?

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Atëherë ky aplikacion është për ju. Është për Android dhe iOS dhe përdoret për të parë transmetime të ngjarjeve të ndryshme sportive në internet dhe të regjistruara. Aplikacioni përmban gjithashtu statistika të ndryshme, transmetime me tekst, tabela për konferenca, turne dhe informacione të tjera të dobishme për fansat.

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Gjithashtu në aplikacion ka një gjë të tillë si momentet video, d.m.th. mund të shikoni momentet më të rëndësishme të ndeshjeve (golat, luftimet, gjuajtjet, etj.). Nëse nuk dëshironi të shikoni të gjithë transmetimin, mund të shikoni vetëm ato më interesantet.

Çfarë keni përdorur në zhvillim?

Pjesa kryesore u shkrua në Go. API-ja me të cilën komunikonin klientët celularë ishte shkruar në Go. Një shërbim për dërgimin e njoftimeve push në telefonat celularë është shkruar gjithashtu në Go. Ne gjithashtu duhej të shkruanim ORM-në tonë, për të cilën mund të flasim një ditë. Epo, disa shërbime të vogla u shkruan në Go: ndryshimi i madhësisë dhe ngarkimi i imazheve për redaktorët...

Ne përdorëm PostgreSQL si bazën e të dhënave. Ndërfaqja e redaktuesit u shkrua në Ruby on Rails duke përdorur perlë ActiveAdmin. Importimi i statistikave nga një ofrues statistikash shkruhet gjithashtu në Ruby.

Për testet e sistemit API, kemi përdorur Python unittest. Memcached përdoret për të kontrolluar thirrjet e pagesave API, "Chef" përdoret për të kontrolluar konfigurimin, Zabbix përdoret për të mbledhur dhe monitoruar statistikat e brendshme të sistemit. Graylog2 është për mbledhjen e regjistrave, Slate është dokumentacion API për klientët.

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Zgjedhja e protokollit

Problemi i parë që hasëm: na duhej të zgjidhnim një protokoll për ndërveprimin midis klientëve backend dhe celularë, bazuar në pikat e mëposhtme...

  • Kërkesa më e rëndësishme: të dhënat për klientët duhet të përditësohen në kohë reale. Kjo do të thotë, të gjithë ata që po shikojnë aktualisht transmetimin duhet të marrin përditësime pothuajse menjëherë.
  • Për të thjeshtuar gjërat, supozuam se të dhënat që sinkronizohen me klientët nuk fshihen, por fshihen duke përdorur flamuj të veçantë.
  • Të gjitha llojet e kërkesave të rralla (të tilla si statistikat, përbërjet e ekipeve, statistikat e ekipit) merren nga kërkesat e zakonshme GET.
  • Plus, sistemi duhej të mbështeste lehtësisht 100 mijë përdorues në të njëjtën kohë.

Bazuar në këtë, ne kishim dy opsione protokolli:

  1. Pritet në internet. Por ne nuk na duheshin kanale nga klienti në server. Na duhej vetëm të dërgonim përditësime nga serveri te klienti, kështu që një websocket është një opsion i tepërt.
  2. Ngjarjet e dërguara nga serveri (SSE) u shfaqën siç duhet! Është mjaft e thjeshtë dhe në thelb plotëson gjithçka që na nevojitet.

Ngjarjet e dërguara nga serveri

Disa fjalë se si funksionon kjo gjë...

Ai funksionon në krye të një lidhjeje http. Klienti dërgon një kërkesë, serveri përgjigjet me Content-Type: text/event-stream dhe nuk e mbyll lidhjen me klientin, por vazhdon të shkruajë të dhëna në lidhje:

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Të dhënat mund të dërgohen në një format të rënë dakord me klientët. Në rastin tonë, ne e dërguam atë në këtë formë: emri i strukturës së ndryshuar (person, lojtar) u dërgua në fushën e ngjarjes dhe JSON me fusha të reja, të ndryshuara për lojtarin u dërgua në fushën e të dhënave.

Tani le të flasim për mënyrën se si funksionon vetë ndërveprimi.

  • Gjëja e parë që bën klienti është të përcaktojë herën e fundit që është kryer sinkronizimi me shërbimin: ai shikon bazën e tij lokale të të dhënave dhe përcakton datën e ndryshimit të fundit të regjistruar prej tij.
  • Ai dërgon një kërkesë me këtë datë.
  • Si përgjigje, ne i dërgojmë atij të gjitha përditësimet që kanë ndodhur që nga ajo datë.
  • Pas kësaj, ai lidhet me kanalin e drejtpërdrejtë dhe nuk mbyllet derisa të ketë nevojë për këto përditësime:

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Ne i dërgojmë një listë ndryshimesh: nëse dikush shënon një gol, ne ndryshojmë rezultatin e ndeshjes, nëse ai lëndohet, kjo dërgohet edhe në kohë reale. Kështu, klientët marrin menjëherë të dhëna të përditësuara në furnizimin e ngjarjeve të ndeshjes. Periodikisht, në mënyrë që klienti të kuptojë se serveri nuk ka vdekur, se asgjë nuk i ka ndodhur, ne dërgojmë një vulë kohore çdo 15 sekonda - në mënyrë që të dijë se gjithçka është në rregull dhe nuk ka nevojë të rilidhet.

Si shërbehet lidhja e drejtpërdrejtë?

  • Para së gjithash, ne krijojmë një kanal në të cilin do të merren përditësimet e buferuara.
  • Pas kësaj, ne abonojmë këtë kanal për të marrë përditësime.
  • Ne vendosëm kokën e saktë në mënyrë që klienti të dijë se gjithçka është në rregull.
  • Dërgoni ping-un e parë. Ne thjesht regjistrojmë vulën kohore aktuale të lidhjes.
  • Pas kësaj, ne lexojmë nga kanali në një lak derisa kanali i përditësimit të mbyllet. Kanali merr periodikisht ose vulën kohore aktuale ose ndryshimet që ne po shkruajmë tashmë për të hapur lidhjet.

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Problemi i parë që hasëm ishte ky: për çdo lidhje të hapur me klientin, ne krijuam një kohëmatës që shënonte një herë në 15 sekonda - rezulton se nëse do të kishim 6 mijë lidhje të hapura me një makinë (me një server API), 6 u krijuan mijëra kohëmatës. Kjo bëri që makina të mos mbante ngarkesën e kërkuar. Problemi nuk ishte aq i dukshëm për ne, por morëm një ndihmë të vogël dhe e rregulluam.

Si rezultat, tani ping-u ynë vjen nga i njëjti kanal nga vjen përditësimi.

Prandaj, ekziston vetëm një kohëmatës që shënon një herë në 15 sekonda.

Këtu ka disa funksione ndihmëse - dërgimi i kokës, ping-ut dhe vetë strukturës. Kjo do të thotë, emri i tabelës (personi, ndeshja, sezoni) dhe informacioni në lidhje me këtë hyrje transmetohen këtu:

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Mekanizmi për dërgimin e përditësimeve

Tani pak se nga vijnë ndryshimet. Kemi disa persona, redaktorë, që e shikojnë transmetimin në kohë reale. Ata krijojnë të gjitha ngjarjet: dikush u përjashtua, dikush u plagos, një lloj zëvendësimi...

Duke përdorur një CMS, të dhënat hyjnë në bazën e të dhënave. Pas kësaj, baza e të dhënave njofton serverët API për këtë duke përdorur mekanizmin Listen/Notify. Serverët API tashmë ua dërgojnë këtë informacion klientëve. Kështu, ne në thelb kemi vetëm disa serverë të lidhur me bazën e të dhënave dhe nuk ka ngarkesë të veçantë në bazën e të dhënave, sepse klienti nuk ndërvepron drejtpërdrejt me bazën e të dhënave në asnjë mënyrë:

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

PostgreSQL: Dëgjo/Njofto

Mekanizmi Listen/Njoftim në Postgres ju lejon të njoftoni abonentët e ngjarjeve se një ngjarje ka ndryshuar - disa regjistrime janë krijuar në bazën e të dhënave. Për ta bërë këtë, ne kemi shkruar një shkas dhe funksion të thjeshtë:

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Gjatë futjes ose ndryshimit të një rekord, ne thërrasim funksionin notify në kanalin data_updates, duke kaluar aty emrin e tabelës dhe identifikuesin e rekordit që është ndryshuar ose futur.

Për të gjitha tabelat që duhet të sinkronizohen me klientin, ne përcaktojmë një shkas, i cili, pas ndryshimit / përditësimit të një rekord, thërret funksionin e treguar në rrëshqitjen më poshtë.
Si abonohet API për këto ndryshime?

Krijohet një mekanizëm Fanout - ai dërgon mesazhe te klienti. Ai mbledh të gjitha kanalet e klientëve dhe dërgon përditësimet që ka marrë përmes këtyre kanaleve:

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Këtu biblioteka standarde pq, e cila lidhet me bazën e të dhënave dhe thotë se dëshiron të dëgjojë kanalin (data_updates), kontrollon që lidhja është e hapur dhe gjithçka është në rregull. Po heq kontrollin e gabimeve për të kursyer hapësirë ​​(moskontrolli është i rrezikshëm).

Më pas, ne vendosim në mënyrë asinkrone Ticker, i cili do të dërgojë një ping çdo 15 sekonda dhe do të fillojmë të dëgjojmë kanalin ku jemi abonuar. Nëse marrim një ping, ne e publikojmë këtë ping. Nëse marrim një lloj hyrjeje, atëherë ne e publikojmë këtë hyrje për të gjithë abonentët e këtij Fanout.

Si funksionon Fan-out?

Në rusisht kjo përkthehet si "ndarëse". Ne kemi një objekt që regjistron pajtimtarët që duan të marrin disa përditësime. Dhe sapo të arrijë një përditësim në këtë objekt, ai e shpërndan këtë përditësim tek të gjithë abonentët e tij. Mjaft e thjeshtë:

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Si zbatohet në Go:

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Ekziston një strukturë, ajo sinkronizohet duke përdorur Mutexes. Ka një fushë që ruan gjendjen e lidhjes së Fanout me bazën e të dhënave, d.m.th. është duke dëgjuar dhe do të marrë përditësime, si dhe një listë të të gjitha kanaleve të disponueshme - harta, çelësi i së cilës është kanali dhe struktura në formën e vlerat (në thelb nuk përdoret në asnjë mënyrë).

Dy metoda - Connected dhe Disconnected - na lejojnë t'i themi Fanout se kemi një lidhje me bazën, ajo është shfaqur dhe se lidhja me bazën është prishur. Në rastin e dytë, duhet të shkëputni të gjithë klientët dhe t'u thoni se nuk mund të dëgjojnë më asgjë dhe se ata rilidhen sepse lidhja me ta është mbyllur.

Ekziston gjithashtu një metodë Subscribe që shton kanalin te "dëgjuesit":

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Ekziston një metodë Unsubscribe, e cila heq kanalin nga dëgjuesit nëse klienti shkëputet, si dhe një metodë Publish, e cila ju lejon të dërgoni një mesazh për të gjithë abonentët.

Pyetje: – Çfarë transmetohet përmes këtij kanali?

ZNJ: – Transmetohet modeli që ka ndryshuar ose ping (në thelb vetëm një numër, numër i plotë).

ZNJ: – Mund të dërgoni çdo gjë, të dërgoni çdo strukturë, ta publikoni – ajo thjesht kthehet në JSON dhe kaq.

ZNJ: – Ne marrim një njoftim nga Postgres – ai përmban emrin e tabelës dhe identifikuesin. Bazuar në emrin e tabelës dhe identifikuesin, marrim regjistrimin që na nevojitet dhe më pas e dërgojmë këtë strukturë për publikim.

Infrastrukturë

Si duket kjo nga perspektiva e infrastrukturës? Ne kemi 7 serverë harduerikë: njëri prej tyre i dedikohet plotësisht bazës së të dhënave, gjashtë të tjerët funksionojnë makina virtuale. Ka 6 kopje të API-së: çdo makinë virtuale me API funksionon në një server të veçantë harduerësh - kjo është për besueshmëri.

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Ne kemi dy frontende me Keepalived të instaluar për të përmirësuar aksesueshmërinë, në mënyrë që nëse ndodh diçka, njëra ballore mund të zëvendësojë tjetrën. Gjithashtu - dy kopje të CMS.

Ekziston edhe një importues statistikash. Ekziston një DB Slave nga i cili bëhen kopje rezervë në mënyrë periodike. Ekziston Pigeon Pusher, një aplikacion që dërgon njoftime push për klientët, si dhe gjëra të infrastrukturës: Zabbix, Graylog2 dhe Chef.

Në fakt, kjo infrastrukturë është e tepërt, sepse 100 mijë mund të shërbehen me më pak serverë. Por kishte hekur - ne e përdorëm atë (na u tha se ishte e mundur - pse jo).

Të mirat e Go

Pasi punuam në këtë aplikacion, u shfaqën përparësi të tilla të dukshme të Go.

  • Bibliotekë e lezetshme http. Me të mund të krijoni shumë nga kutia.
  • Plus, kanale që na lejuan të zbatonim shumë lehtë një mekanizëm për dërgimin e njoftimeve për klientët.
  • Gjëja e mrekullueshme Race Detector na lejoi të eliminojmë disa gabime kritike (infrastruktura e skenës). Çdo gjë që funksionon në skenë lëshohet, e përpiluar me tastin Race; dhe ne, në përputhje me rrethanat, mund të shikojmë infrastrukturën e skenës për të parë se çfarë problemesh të mundshme kemi.
  • Minimalizmi dhe thjeshtësia e gjuhës.

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

Ne jemi në kërkim të zhvilluesve! Nëse dikush dëshiron, ju lutem.

Pyetjet tuaja

Pyetje nga audienca (në tekstin e mëtejmë – B): – Më duket se keni humbur një pikë të rëndësishme në lidhje me Fan-out. A kam të drejtë të kuptoj se kur i dërgoni një përgjigje një klienti, ju bllokoni nëse klienti nuk dëshiron të lexojë?

ZNJ: - Jo, nuk po bllokojmë. Së pari, ne i kemi të gjitha këto pas nginx, domethënë nuk ka probleme me klientët e ngadaltë. Së dyti, klienti ka një kanal me një bufer - në fakt, ne mund të vendosim deri në njëqind përditësime atje... Nëse nuk mund t'i shkruajmë kanalit, atëherë ai e fshin atë. Nëse shohim që kanali është i bllokuar, atëherë thjesht do ta mbyllim kanalin, dhe kaq - klienti do të rilidhet nëse lind ndonjë problem. Prandaj, në parim, nuk ka asnjë bllokim këtu.

NË: – A nuk mund të jetë e mundur që menjëherë të dërgohet një regjistrim te Listen/Njoftimi, dhe jo një tabelë identifikuese?

ZNJ: – Listen/Notify ka një kufi prej 8 mijë bajte në parangarkimin që dërgon. Në parim, do të ishte e mundur të dërgohej nëse do të kishim të bënim me një sasi të vogël të dhënash, por mua më duket se kjo mënyrë [mënyra se si e bëjmë atë] është thjesht më e besueshme. Kufizimet janë në vetë Postgres.

NË: – A marrin klientët përditësime për ndeshjet për të cilat nuk janë të interesuar?

ZNJ: - Në përgjithësi, po. Si rregull, 2-3 ndeshje zhvillohen paralelisht, madje edhe atëherë mjaft rrallë. Nëse një klient po shikon diçka, atëherë zakonisht ai po shikon ndeshjen që po zhvillohet. Pastaj, klienti ka një bazë të dhënash lokale në të cilën janë shtuar të gjitha këto përditësime, dhe madje edhe pa një lidhje interneti, klienti mund të shikojë të gjitha ndeshjet e kaluara për të cilat ai ka përditësime. Në thelb, ne sinkronizojmë bazën tonë të të dhënave në server me bazën e të dhënave lokale të klientit në mënyrë që ai të mund të punojë jashtë linje.

NË: – Pse e bëtë ORM-në tuaj?

Alexey (një nga zhvilluesit e Look+): – Në atë kohë (ishte një vit më parë) kishte më pak ORM se tani, kur ka mjaft të tilla. Gjëja ime e preferuar për shumicën e ORM-ve atje është se shumica e tyre funksionojnë në ndërfaqe boshe. Kjo do të thotë, metodat në këto ORM janë gati të marrin përsipër gjithçka: një strukturë, një tregues strukture, një numër, diçka krejtësisht të parëndësishme ...

ORM ynë gjeneron struktura bazuar në modelin e të dhënave. Veten time. Dhe prandaj të gjitha metodat janë konkrete, nuk përdorin reflektim, etj. Ata pranojnë struktura dhe presin të përdorin ato struktura që vijnë.

NË: – Sa persona morën pjesë?

ZNJ: – Në fazën fillestare morën pjesë dy persona. Ne filluam diku në qershor, dhe në gusht pjesa kryesore ishte gati (versioni i parë). Kishte një lëshim në shtator.

NË: – Aty ku përshkruani SSE, nuk përdorni timeout. Pse eshte ajo?

ZNJ: – Për të qenë i sinqertë, SSE është ende një protokoll html5: standardi SSE është krijuar për të komunikuar me shfletuesit, me sa kuptoj unë. Ka veçori shtesë që shfletuesit të mund të rilidhen (dhe kështu me radhë), por ne nuk na duhen, sepse kishim klientë që mund të zbatonin çdo logjikë për lidhjen dhe marrjen e informacionit. Ne nuk bëmë SSE, por diçka të ngjashme me SSE. Ky nuk është vetë protokolli.
Nuk kishte nevojë. Me sa kuptoj unë, klientët e zbatuan mekanizmin e lidhjes pothuajse nga e para. Ata nuk u interesuan vërtet.

NË: – Çfarë shërbimesh shtesë keni përdorur?

ZNJ: – Ne përdorëm në mënyrë më aktive govet dhe golin për të unifikuar stilin, si dhe gofmt. Asgjë tjetër nuk u përdor.

NË: – Çfarë keni përdorur për korrigjimin e gabimeve?

ZNJ: – Korrigjimi u krye kryesisht duke përdorur teste. Ne nuk kemi përdorur asnjë korrigjues ose GOP.

NË: – A mund ta ktheni rrëshqitjen ku zbatohet funksioni Publish? A ju ngatërrojnë emrat e ndryshoreve me një shkronjë?

ZNJ: - Jo. Ata kanë një shtrirje mjaft "të ngushtë" të dukshmërisë. Ata nuk përdoren askund tjetër përveç këtu (përveç pjesëve të brendshme të kësaj klase), dhe është shumë kompakt - duhen vetëm 7 rreshta.

NË: – Disi nuk është ende intuitive…

ZNJ: - Jo, jo, ky është një kod i vërtetë! Nuk ka të bëjë me stilin. Është thjesht një klasë kaq utilitare, shumë e vogël - vetëm 3 fusha brenda klasës...

Mikhail Salosin. Takimi Golang. Duke përdorur Go në pjesën e pasme të aplikacionit Look+

ZNJ: – Në përgjithësi, të gjitha të dhënat që sinkronizohen me klientët (ndeshjet e sezonit, lojtarët) nuk ndryshojnë. Përafërsisht, nëse bëjmë një sport tjetër në të cilin duhet të ndryshojmë ndeshjen, thjesht do të marrim parasysh gjithçka në versionin e ri të klientit dhe versionet e vjetra të klientit do të ndalohen.

NË: – A ka ndonjë paketë të menaxhimit të varësisë nga palët e treta?

ZNJ: – Ne përdorëm go dep.

NË: – Kishte diçka për video në temën e raportit, por nuk kishte asgjë në raport për video.

ZNJ: – Jo, nuk kam asgjë në temë për videon. Quhet "Look+" - ky është emri i aplikacionit.

NË: – Ju thatë që u transmetohet klientëve?..

ZNJ: – Ne nuk ishim të përfshirë në transmetimin e videos. Kjo është bërë tërësisht nga Megafon. Po, nuk thashë që aplikacioni ishte MegaFon.

ZNJ: – Shko – për dërgimin e të gjitha të dhënave – për rezultatin, për ngjarjet e ndeshjeve, statistikat... Go është e gjithë mbështetja e aplikacionit. Klienti duhet të dijë nga diku se cilën lidhje të përdorë për lojtarin në mënyrë që përdoruesi të mund të shikojë ndeshjen. Ne kemi lidhje me videot dhe transmetimet që janë përgatitur.

Disa reklama 🙂

Faleminderit që qëndruat me ne. A ju pëlqejnë artikujt tanë? Dëshironi të shihni përmbajtje më interesante? Na mbështesni duke bërë një porosi ose duke rekomanduar miqve, cloud VPS për zhvilluesit nga 4.99 dollarë, një analog unik i serverëve të nivelit të hyrjes, i cili u shpik nga ne për ju: E gjithë e vërteta rreth VPS (KVM) E5-2697 v3 (6 bërthama) 10 GB DDR4 480 GB SSD 1 Gbps nga 19 dollarë ose si të ndani një server? (e disponueshme me RAID1 dhe RAID10, deri në 24 bërthama dhe deri në 40 GB DDR4).

Dell R730xd 2 herë më lirë në qendrën e të dhënave Equinix Tier IV në Amsterdam? Vetëm këtu 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV nga 199$ në Holandë! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - nga 99 dollarë! Lexoni rreth Si të ndërtohet korporata e infrastrukturës. klasë me përdorimin e serverëve Dell R730xd E5-2650 v4 me vlerë 9000 euro për një qindarkë?

Burimi: www.habr.com

Shto një koment