Kelkfoje pli estas malpli. Dum redukto de ŝarĝo rezultigas kreskantan latentecon

Kiel en plej multaj afiŝoj, estas problemo kun distribuita servo, ni voku ĉi tiun servon Alvin. Ĉi-foje mi ne malkovris la problemon mem, la uloj de la klienta flanko informis min.

Iun tagon mi vekiĝis kun malkontenta retpoŝto pro longaj prokrastoj kun Alvin, kiun ni planis lanĉi baldaŭ. Specife, la kliento spertis 99-an procentan latentecon en la regiono de 50 ms, multe super nia latencia buĝeto. Ĉi tio estis surpriza, ĉar mi vaste testis la servon, precipe pri latenteco, kio estas ofta plendo.

Antaŭ ol mi metis Alvin en testadon, mi faris multajn eksperimentojn kun 40k demandoj je sekundo (QPS), ĉiuj montrante latentecon malpli ol 10ms. Mi estis preta deklari, ke mi ne konsentas kun iliaj rezultoj. Sed denove rigardante la leteron, mi rimarkis ion novan: mi ne ekzakte testis la kondiĉojn, kiujn ili menciis, ilia QPS estis multe pli malalta ol mia. Mi testis je 40k QPS, sed ili nur je 1k. Mi faris alian eksperimenton, ĉi-foje kun pli malalta QPS, nur por trankviligi ilin.

Ĉar mi blogas pri tio, vi verŝajne jam komprenis, ke iliaj nombroj pravas. Mi testis mian virtualan klienton ree kaj ree, kun la sama rezulto: malalta nombro da petoj ne nur pliigas la latentecon, sed pliigas la nombron da petoj kun latenteco de pli ol 10 ms. Alivorte, se ĉe 40k QPS ĉirkaŭ 50 petoj je sekundo superis 50 ms, tiam ĉe 1k QPS estis 100 petoj super 50 ms ĉiusekundo. Paradokso!

Kelkfoje pli estas malpli. Dum redukto de ŝarĝo rezultigas kreskantan latentecon

Malvastigante la serĉon

Kiam vi alfrontas problemon de latenteco en distribuita sistemo kun multaj komponantoj, la unua paŝo estas krei mallongan liston de suspektatoj. Ni enfosu iom pli profunde en la arkitekturon de Alvin:

Kelkfoje pli estas malpli. Dum redukto de ŝarĝo rezultigas kreskantan latentecon

Bona deirpunkto estas listo de finitaj I/O-transiroj (retaj vokoj/diskaj serĉoj, ktp.). Ni provu eltrovi kie estas la prokrasto. Krom la evidenta I/O kun la kliento, Alvin faras kroman paŝon: li aliras la datumbutikon. Tamen, ĉi tiu stokado funkcias en la sama areto kiel Alvin, do la latencia tie devus esti malpli ol ĉe la kliento. Do, la listo de suspektatoj:

  1. Reta voko de kliento al Alvin.
  2. Reta voko de Alvin al la datumvendejo.
  3. Serĉu sur disko en la datumvendejo.
  4. Retovoko de la datumstokejo al Alvin.
  5. Reta voko de Alvin al kliento.

Ni provu forstreki kelkajn punktojn.

Datumstokado havas nenion komunan kun ĝi

La unua afero, kiun mi faris, estis konverti Alvin al ping-ping-servilo, kiu ne procesas petojn. Kiam ĝi ricevas peton, ĝi resendas malplenan respondon. Se la latenteco malpliiĝas, tiam cimo en la efektivigo de Alvin aŭ datuma magazeno estas nenio neaŭdita. En la unua eksperimento ni ricevas la sekvan grafeon:

Kelkfoje pli estas malpli. Dum redukto de ŝarĝo rezultigas kreskantan latentecon

Kiel vi povas vidi, ne estas plibonigo kiam vi uzas la ping-ping-servilon. Ĉi tio signifas, ke la datumstokejo ne pliigas latentecon, kaj la listo de suspektatoj estas duonigita:

  1. Reta voko de kliento al Alvin.
  2. Reta voko de Alvin al kliento.

Bonege! La listo malgrandiĝas rapide. Mi pensis, ke mi preskaŭ eltrovis la kialon.

gRPC

Nun estas la tempo por prezenti vin al nova ludanto: gRPC. Ĉi tio estas liberkoda biblioteko de Guglo por enproceza komunikado CPR... Kvankam gRPC bone optimumigita kaj vaste uzata, ĉi tiu estis mia unua fojo uzi ĝin sur sistemo de ĉi tiu grandeco kaj mi atendis ke mia efektivigo estos suboptimuma - por diri la malpli.

havebleco gRPC en la stako estigis novan demandon: eble ĝi estas mia efektivigo aŭ mi mem gRPC kaŭzante problemon pri latenteco? Aldonante novan suspektaton al la listo:

  1. La kliento vokas la bibliotekon gRPC
  2. biblioteko gRPC faras retvokon al la biblioteko sur la kliento gRPC sur servilo
  3. biblioteko gRPC kontaktas Alvin (neniu operacio en kazo de ping-ponga servilo)

Por doni al vi ideon pri kiel aspektas la kodo, mia realigo de kliento/Alvin ne multe diferencas de la kliento-servilo. nesinkronigitaj ekzemploj.

Noto: La supra listo estas iom simpligita ĉar gRPC ebligas uzi vian propran (ŝablonon?) surfadenan modelon, en kiu la ekzekutstako estas interplektita gRPC kaj uzanta efektivigo. Por simpleco, ni aliĝos al ĉi tiu modelo.

Profilado riparos ĉion

Forstrekinte la datumvendejojn, mi pensis, ke mi preskaŭ finis: "Nun estas facile! Ni apliku la profilon kaj eksciu, kie okazas la prokrasto." mi granda adoranto de precizeca profilado, ĉar CPUoj estas tre rapidaj kaj plej ofte ne estas la proplemkolo. Plej multaj prokrastoj okazas kiam la procesoro devas ĉesi prilabori por fari ion alian. Preciza CPU-Profilo faras ĝuste tion: ĝi precize registras ĉion kuntekstoŝanĝoj kaj klarigas kie okazas prokrastoj.

Mi prenis kvar profilojn: kun alta QPS (malalta latencia) kaj kun ping-ponga servilo kun malalta QPS (alta latencia), kaj ĉe la klienta flanko kaj ĉe la servilo. Kaj ĉiaokaze, mi ankaŭ prenis specimenan procesoran profilon. Kiam mi komparas profilojn, mi kutime serĉas nenormalan vokon. Ekzemple, ĉe la malbona flanko kun alta latencia estas multaj pli da kuntekstŝaltiloj (10 fojojn aŭ pli). Sed en mia kazo, la nombro da kuntekstŝaltiloj estis preskaŭ la sama. Je mia teruro, estis nenio signifa tie.

Plia Sencimigado

Mi estis malespera. Mi ne sciis kiajn aliajn ilojn mi povus uzi, kaj mia sekva plano estis esence ripeti la eksperimentojn kun malsamaj varioj prefere ol klare diagnozi la problemon.

Kio se

De la komenco, mi zorgis pri la specifa 50ms-latenteco. Ĉi tio estas tre granda tempo. Mi decidis, ke mi tranĉos pecojn el la kodo ĝis mi povos eltrovi precize, kiu parto kaŭzas ĉi tiun eraron. Poste venis eksperimento, kiu funkciis.

Kiel kutime, postvide ŝajnas, ke ĉio estis evidenta. Mi metis la klienton sur la saman maŝinon kiel Alvin - kaj sendis peton al localhost. Kaj la kresko de latenco malaperis!

Kelkfoje pli estas malpli. Dum redukto de ŝarĝo rezultigas kreskantan latentecon

Io misas kun la reto.

Lernante retajn inĝenierajn kapablojn

Mi devas konfesi: mia scio pri retaj teknologioj estas terura, precipe konsiderante la fakton, ke mi laboras kun ili ĉiutage. Sed la reto estis la ĉefa suspektato, kaj mi devis lerni kiel sencimigi ĝin.

Feliĉe, Interreto amas tiujn, kiuj volas lerni. La kombinaĵo de ping kaj tracert ŝajnis sufiĉe bona komenco por sencimigi retajn transportproblemojn.

Unue, mi lanĉis PsPing al la TCP-haveno de Alvin. Mi uzis la defaŭltajn agordojn - nenio speciala. El pli ol mil pingoj, neniu superis 10 ms, escepte de la unua por varmigado. Ĉi tio estas kontraŭa al la observita pliiĝo de latenteco de 50 ms ĉe la 99-a percentilo: tie, por ĉiu 100 petoj, ni devus esti vidinta ĉirkaŭ unu peton kun latenco de 50 ms.

Tiam mi provis spuro: Eble estas problemo ĉe unu el la nodoj laŭ la itinero inter Alvin kaj la kliento. Sed la spurilo ankaŭ revenis kun malplenaj manoj.

Do ne mia kodo, la gRPC-efektivigo aŭ la reto kaŭzis la prokraston. Mi komencis zorgi, ke mi neniam komprenos ĉi tion.

Nun pri kio OS ni estas

gRPC vaste uzata en Linukso, sed ekzotika en Vindozo. Mi decidis provi eksperimenton, kiu funkciis: mi kreis Linuksan virtualan maŝinon, kompilis Alvin por Linukso, kaj deplojis ĝin.

Kelkfoje pli estas malpli. Dum redukto de ŝarĝo rezultigas kreskantan latentecon

Kaj jen kio okazis: la Linuksa ping-ponga servilo ne havis la samajn prokrastojn kiel simila Vindoza gastiganto, kvankam la datumfonto ne estis malsama. Rezultas, ke la problemo estas en la efektivigo de gRPC por Vindozo.

La algoritmo de Nagle

Dum ĉi tiu tempo mi pensis, ke mi mankas flago gRPC. Nun mi komprenas, kio ĝi vere estas gRPC Vindoza flago mankas. Mi trovis internan RPC-bibliotekon, kiun mi certas, ke ĝi funkcios bone por ĉiuj flagoj agordita Winsock. Poste mi aldonis ĉiujn ĉi tiujn flagojn al gRPC kaj deplojis Alvin sur Vindozo, en flikita Vindoza ping-ponga servilo!

Kelkfoje pli estas malpli. Dum redukto de ŝarĝo rezultigas kreskantan latentecon

Preskaŭ Farita: mi komencis forigi la aldonitajn flagojn unuope ĝis la regreso revenis, por ke mi povu precizigi la kaŭzon. Ĝi estis fifama TCP_NODELAY, la algoritmoŝaltilo de Nagle.

La algoritmo de Nagle provas redukti la nombron da pakaĵetoj senditaj tra reto prokrastante la dissendon de mesaĝoj ĝis la pakaĵeto superas certan nombron da bajtoj. Kvankam ĉi tio povas esti bela por la averaĝa uzanto, ĝi estas detrua por realtempaj serviloj ĉar la OS prokrastos kelkajn mesaĝojn, kaŭzante malfruojn ĉe malalta QPS. U gRPC ĉi tiu flago estis metita en la Linukso-efektivigo por TCP-ingoj, sed ne en Vindozo. Mi estas ĉi tio korektita.

konkludo

La pli alta latenteco ĉe malalta QPS estis kaŭzita de OS-optimumigo. Retrospektive, profilado ne detektis latentecon ĉar ĝi estis farita en kernreĝimo prefere ol en uzanta reĝimo. Mi ne scias ĉu la algoritmo de Nagle povas esti observita per ETW-kaptoj, sed ĝi estus interese.

Koncerne la localhost-eksperimenton, ĝi verŝajne ne tuŝis la realan interkonektan kodon kaj la algoritmo de Nagle ne funkciis, do la latenciaj problemoj malaperis kiam la kliento atingis Alvin per localhost.

La venontan fojon, kiam vi vidos pliiĝon de latenteco, ĉar la nombro da petoj je sekundo malpliiĝas, la algoritmo de Nagle devus esti en via listo de suspektatoj!

fonto: www.habr.com

Aldoni komenton