Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

Saluton, mia nomo estas Evgeniy. Mi laboras en la serĉa infrastrukturo Yandex.Market. Mi volas rakonti al la Habr-komunumo pri la interna kuirejo de la Merkato – kaj estas multo por rakonti. Antaŭ ĉio, kiel la Merkata serĉo funkcias, procezoj kaj arkitekturo. Kiel ni traktas krizajn situaciojn: kio okazas se unu servilo malfunkcias? Kio se estas 100 tiaj serviloj?

Vi ankaŭ lernos kiel ni efektivigas novajn funkciojn en amaso da serviloj samtempe. Kaj kiel ni testas kompleksajn servojn rekte en produktado, sen kaŭzi ajnan ĝenon al uzantoj. Ĝenerale, kiel funkcias la Merkata serĉo por ke ĉiuj havu bonan tempon.

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

Iom pri ni: kian problemon ni solvas

Kiam vi enigas tekston, serĉas produkton laŭ parametroj aŭ komparas prezojn en malsamaj vendejoj, ĉiuj petoj estas senditaj al la serĉservo. Serĉo estas la plej granda servo en la Merkato.

Ni procesas ĉiujn serĉpetojn: de la retejoj market.yandex.ru, beru.ru, la Supercheck-servo, Yandex.Advisor, moveblaj aplikoj. Ni ankaŭ inkluzivas produktajn ofertojn en serĉrezultoj ĉe yandex.ru.

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

Per serĉservo mi celas ne nur la serĉon mem, sed ankaŭ datumbazon kun ĉiuj ofertoj sur la Merkato. La skalo estas jena: pli ol miliardo da serĉpetoj estas procesitaj ĉiutage. Kaj ĉio devus funkcii rapide, sen interrompoj kaj ĉiam produkti la deziratan rezulton.

Kio estas kio: Merkata arkitekturo

Mi mallonge priskribos la nunan arkitekturon de la Merkato. Ĝi povas esti proksimume priskribita per la diagramo malsupre:
Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas
Ni diru partnera vendejo venas al ni. Li diras, ke mi volas vendi ludilon: tiun ĉi malbonan katon kun knaro. Kaj alia kolera kato sen grincado. Kaj nur kato. Tiam la vendejo devas prepari ofertojn, por kiuj la Merkato serĉas. La vendejo generas specialan xml kun ofertoj kaj komunikas la vojon al ĉi tiu xml per la filia interfaco. La indeksilo tiam periode elŝutas ĉi tiun xml, kontrolas erarojn kaj konservas ĉiujn informojn en grandegan datumbazon.

Estas multaj tiaj konservitaj xml-oj. Serĉa indekso estas kreita el ĉi tiu datumbazo. La indekso estas konservita en interna formato. Post kreado de la indekso, la Layout-servo alŝutas ĝin al serĉserviloj.

Kiel rezulto, en la datumbazo aperas kolera kato kun grincaĵo, kaj la indekso de la kato aperas sur la servilo.

Mi rakontos al vi kiel ni serĉas katon en la parto pri serĉarkitekturo.

Merkata serĉarkitekturo

Ni vivas en mondo de mikroservoj: ĉiu envenanta peto market.yandex.ru kaŭzas multajn subdemandojn, kaj dekoj da servoj estas implikitaj en ilia prilaborado. La diagramo montras nur kelkajn:

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas
Simpligita peto prilaborado

Ĉiu servo havas mirindan aferon - sian propran ekvilibron kun unika nomo:

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

La ekvilibristo donas al ni pli grandan flekseblecon en la administrado de la servo: vi povas, ekzemple, malŝalti servilojn, kio ofte estas postulata por ĝisdatigoj. La ekvilibristo vidas, ke la servilo estas neatingebla kaj aŭtomate redirektas petojn al aliaj serviloj aŭ datumcentroj. Aldonante aŭ forigante servilon, la ŝarĝo estas aŭtomate redistribuita inter la serviloj.

La unika nomo de la ekvilibristo ne dependas de la datumcentro. Kiam servo A faras peton al B, tiam defaŭlte ekvilibristo B alidirektas la peton al la nuna datumcentro. Se la servo estas neatingebla aŭ ne ekzistas en la nuna datumcentro, tiam la peto estas redirektita al aliaj datumcentroj.

Ununura FQDN por ĉiuj datumcentroj permesas al servo A tute abstrakti de lokoj. Lia peto al servo B estos ĉiam procesita. La escepto estas la kazo kiam la servo situas en ĉiuj datumcentroj.

Sed ne ĉio estas tiel roza kun ĉi tiu ekvilibristo: ni havas aldonan mezan komponanton. La ekvilibristo povas esti malstabila, kaj ĉi tiu problemo estas solvita per redundaj serviloj. Estas ankaŭ plia prokrasto inter servoj A kaj B. Sed praktike ĝi estas malpli ol 1 ms kaj por plej multaj servoj tio ne estas kritika.

Traktante la Neatenditan: Serĉu Servan Ekvilibron kaj Resilientecon

Imagu, ke estas kolapso: vi devas trovi katon kun grincaĵo, sed la servilo kraŝas. Aŭ 100 serviloj. Kiel eliri? Ĉu ni vere lasos la uzanton sen kato?

La situacio estas timiga, sed ni estas pretaj por ĝi. Mi diros al vi en ordo.

La serĉinfrastrukturo situas en pluraj datumcentroj:

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

Dum desegnado, ni inkluzivas la eblecon fermi unu datumcentron. La vivo estas plena de surprizoj - ekzemple elkavatoro povas tranĉi subteran kablon (jes, tio okazis). La kapablo en la ceteraj datumcentroj devus esti sufiĉa por elteni pintan ŝarĝon.

Ni konsideru ununuran datumcentron. Ĉiu datumcentro havas la saman ekvilibran operacioskemon:

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas
Unu ekvilibristo estas almenaŭ tri fizikaj serviloj. Ĉi tiu redundo estas farita por fidindeco. Ekvilibroj funkcias per HAProx.

Ni elektis HAProx pro ĝia alta rendimento, malaltaj rimedpostuloj kaj larĝa funkcieco. Nia serĉprogramaro funkcias ene de ĉiu servilo.

La verŝajneco de malsukceso de unu servilo estas malalta. Sed se vi havas multajn servilojn, pliiĝas la verŝajneco, ke almenaŭ unu falos.

Jen kio okazas en la realo: serviloj kraŝas. Tial necesas konstante kontroli la staton de ĉiuj serviloj. Se la servilo ĉesas respondi, ĝi estas aŭtomate malkonektita de trafiko. Por ĉi tiu celo, HAProxy havas enkonstruitan sankontrolon. Ĝi iras al ĉiuj serviloj unufoje sekundon kun HTTP-peto "/ping".

Alia trajto de HAProxy: agent-check permesas vin ŝarĝi ĉiujn servilojn egale. Por fari tion, HAProxy konektas al ĉiuj serviloj, kaj ili redonas sian pezon depende de la nuna ŝarĝo de 1 ĝis 100. La pezo estas kalkulita surbaze de la nombro da petoj en la vosto por prilaborado kaj la ŝarĝo sur la procesoro.

Nun pri trovado de la kato. La serĉo rezultigas petojn kiel: /serĉo?teksto=kolera+kato. Por ke la serĉo estu rapida, la tuta kata indekso devas konveni en RAM. Eĉ legado de la SSD ne estas sufiĉe rapida.

Iam, la oferta datumbazo estis malgranda, kaj la RAM de unu servilo sufiĉis por ĝi. Dum la ofertbazo kreskis, ĉio ne plu taŭgas en ĉi tiu RAM, kaj la datumoj estis dividitaj en du partojn: peceto 1 kaj peceto 2.

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas
Sed ĉi tio ĉiam okazas: ĉiu solvo, eĉ bona, estigas aliajn problemojn.

La ekvilibristo ankoraŭ iris al iu ajn servilo. Sed sur la maŝino, kie venis la peto, estis nur duono de la indekso. La resto estis sur aliaj serviloj. Tial, la servilo devis iri al iu najbara maŝino. Post ricevado de datumoj de ambaŭ serviloj, la rezultoj estis kombinitaj kaj rerangoigitaj.

Ĉar la ekvilibristo distribuas petojn egale, ĉiuj serviloj okupiĝis pri re-rango, kaj ne nur sendado de datumoj.

La problemo okazis se najbara servilo ne estis disponebla. La solvo estis specifi plurajn servilojn kun malsamaj prioritatoj kiel "najbara" servilo. Unue, la peto estis sendita al la serviloj en la nuna rako. Se ne estis respondo, la peto estis sendita al ĉiuj serviloj en ĉi tiu datumcentro. Kaj laste, la peto iris al aliaj datumcentroj.
Ĉar la nombro da proponoj kreskis, la datumoj estis dividitaj en kvar partojn. Sed ĉi tio ne estis la limo.

Nuntempe, agordo de ok fragmentoj estas uzata. Krome, por ŝpari eĉ pli da memoro, la indekso estis dividita en serĉparton (kiu estas uzata por serĉado) kaj fragmentoparto (kiu ne estas implikita en la serĉo).

Unu servilo enhavas informojn por nur unu peceto. Tial, por serĉi la plenan indekson, vi devas serĉi sur ok serviloj enhavantaj malsamajn pecetojn.

Serviloj estas grupigitaj en aretojn. Ĉiu areto enhavas ok serĉilojn kaj unu fragmentservilon.

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas
La fragmentservilo prizorgas ŝlosilvaloran datumbazon kun senmovaj datumoj. Ili estas bezonataj por eldoni dokumentojn, ekzemple priskribon de kato kun knaro. La datumoj estas speciale transdonitaj al aparta servilo por ne ŝargi la memoron de serĉserviloj.

Ĉar dokumentidentigiloj estas unikaj nur ene de unu indekso, povus ekesti situacio kie ne estas dokumentoj en la fragmentoj. Nu, aŭ ke por unu identigilo estos malsama enhavo. Tial, por ke la serĉo funkciu kaj rezultoj estu resenditaj, estis bezono de konsistenco tra la tuta areto. Mi rakontos al vi ĉi-sube kiel ni kontrolas konsistencon.

La serĉo mem estas strukturita jene: serĉpeto povas veni al iu ajn el la ok serviloj. Ni diru, ke li venis al servilo 1. Ĉi tiu servilo prilaboras ĉiujn argumentojn kaj komprenas kion kaj kiel serĉi. Depende de la envenanta peto, la servilo povas fari pliajn petojn al eksteraj servoj por la necesaj informoj. Unu peto povas esti sekvita de ĝis dek petoj al eksteraj servoj.

Post kolektado de la necesaj informoj, serĉo komenciĝas en la oferta datumbazo. Por fari tion, subdemandoj estas faritaj al ĉiuj ok serviloj en la areto.

Post kiam la respondoj estas ricevitaj, la rezultoj estas kombinitaj. En la fino, pluraj pliaj subdemandoj al la fragmentservilo eble estos bezonataj por generi la rezultojn.

Serĉdemandoj ene de la areto aspektas kiel: /shard1?text=angry+cat. Krome, subdemandoj de la formo estas konstante faritaj inter ĉiuj serviloj ene de la areto unufoje sekundon: /statuso.

Peto /statuso detektas situacion kie la servilo ne estas disponebla.

Ĝi ankaŭ kontrolas, ke la serĉilo-versio kaj la indeksa versio estas samaj en ĉiuj serviloj, alie estos malkonsekvencaj datumoj ene de la areto.

Malgraŭ tio, ke unu fragmento-servilo prilaboras petojn de ok serĉiloj, ĝia procesoro estas tre malpeze ŝarĝita. Tial ni nun transdonas la fragmentajn datumojn al aparta servo.

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

Por transdoni datumojn, ni enkondukis universalajn ŝlosilojn por dokumentoj. Nun estas neeble por situacio kie enhavo de alia dokumento estas resendita per unu ŝlosilo.

Sed la transiro al alia arkitekturo ankoraŭ ne estas kompleta. Nun ni volas forigi la dediĉitan fragmentan servilon. Kaj tiam malproksimiĝu de la clusterstrukturo entute. Ĉi tio permesos al ni daŭre grimpi facile. Plia gratifiko estas signifa fera ŝparado.

Kaj nun al timigaj rakontoj kun feliĉaj finoj. Ni konsideru plurajn kazojn de servilo nehavebleco.

Okazis io terura: unu servilo ne disponeblas

Ni diru, ke unu servilo estas neatingebla. Tiam la ceteraj serviloj en la areto povas daŭrigi respondi, sed la serĉrezultoj estos nekompletaj.

Per statokontrolo /statuso najbaraj serviloj komprenas ke unu estas neatingebla. Tial, por konservi kompletecon, ĉiuj serviloj en la areto laŭ peto /ping ili komencas respondi al la ekvilibristo, ke ili ankaŭ estas neatingeblaj. Montriĝas, ke ĉiuj serviloj en la areto mortis (kio ne estas vera). Ĉi tio estas la ĉefa malavantaĝo de nia clusterskemo - tial ni volas foriri de ĝi.

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

Petoj kiuj malsukcesas kun eraro estas resendita de la ekvilibristo sur aliaj serviloj.
La ekvilibristo ankaŭ ĉesas sendi uzanttrafikon al mortintaj serviloj, sed daŭre kontrolas ilian staton.

Kiam la servilo fariĝas disponebla, ĝi komencas respondi /ping. Tuj kiam normalaj respondoj al ping-oj de mortintaj serviloj komencas alveni, ekvilibristoj komencas sendi uzanttrafikon tien. Aretfunkciado estas restarigita, hurdu.

Eĉ pli malbone: multaj serviloj estas neatingeblaj

Grava parto de la serviloj en la datumcentro estas tranĉita. Kion fari, kien kuri? La ekvilibristo venas al la savo denove. Ĉiu ekvilibristo konstante konservas en memoro la nunan nombron da vivaj serviloj. Ĝi konstante kalkulas la maksimuman kvanton de trafiko, kiun la nuna datumcentro povas prilabori.

Kiam multaj serviloj en datumcentro malaltiĝas, la ekvilibristo rimarkas, ke ĉi tiu datumcentro ne povas prilabori la tutan trafikon.

Tiam la troa trafiko komencas esti hazarde distribuita al aliaj datumcentroj. Ĉio funkcias, ĉiuj estas feliĉaj.

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

Kiel ni faras ĝin: eldonado de eldonoj

Nun ni parolu pri kiel ni publikigas ŝanĝojn faritajn al la servo. Ĉi tie ni prenis la vojon de simpligado de procezoj: lanĉi novan eldonon estas preskaŭ tute aŭtomatigita.
Kiam certa nombro da ŝanĝoj estas akumulitaj en la projekto, nova eldono aŭtomate kreiĝas kaj ĝia konstruo komenciĝas.

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

Tiam la servo estas lanĉita al testado, kie la stabileco de operacio estas kontrolita.

Samtempe, aŭtomata agado-testado estas lanĉita. Ĉi tio estas pritraktata de speciala servo. Mi ne parolos pri ĝi nun - ĝia priskribo estas inda je aparta artikolo.

Se publikigo en testado estas sukcesa, publikigo de la eldono en prestablo estas aŭtomate komencita. Prestable estas speciala areto kie normala uzanttrafiko estas direktita. Se ĝi resendas eraron, la ekvilibristo faras re-peton al produktado.

En prestablo, respondaj tempoj estas mezuritaj kaj komparitaj kun la antaŭa eldono en produktado. Se ĉio estas en ordo, tiam homo konektas: kontrolas la grafikaĵojn kaj rezultojn de ŝarĝotestado kaj tiam komencas ruliĝi al produktado.

Ĉio plej bona iras al la uzanto: A/B-testado

Ne ĉiam estas evidente ĉu ŝanĝoj al servo alportos realajn avantaĝojn. Por mezuri la utilecon de ŝanĝoj, homoj elpensis A/B-testadon. Mi rakontos al vi iom pri kiel ĝi funkcias en Yandex.Market-serĉo.

Ĉio komenciĝas per aldono de nova CGI-parametro, kiu ebligas novan funkcion. Nia parametro estu: market_new_functionality=1. Tiam en la kodo ni ebligas ĉi tiun funkcion se la flago ĉeestas:

If (cgi.experiments.market_new_functionality) {
// enable new functionality
}

Nova funkcieco estas lanĉita al produktado.

Por aŭtomatigi A/B-testadon, ekzistas dediĉita servo, kiu provizas detalajn informojn priskribita ĉi tie. Eksperimento estas kreita en la servo. La trafika kotizo estas metita, ekzemple, 15%. Procentoj estas fiksitaj ne por demandoj, sed por uzantoj. La daŭro de la eksperimento ankaŭ estas indikita, ekzemple, semajno.

Pluraj eksperimentoj povas esti rulitaj samtempe. En la agordoj vi povas specifi ĉu intersekco kun aliaj eksperimentoj eblas.

Kiel rezulto, la servo aŭtomate aldonas argumenton market_new_functionality=1 al 15% de uzantoj. Ĝi ankaŭ aŭtomate kalkulas la elektitajn metrikojn. Post kiam la eksperimento estas finita, analizistoj rigardas la rezultojn kaj faras konkludojn. Surbaze de la trovoj, decido estas farita al produktado aŭ rafinado.

La lerta mano de merkato: testado en produktado

Ofte okazas, ke vi devas testi la funkciadon de nova funkcio en produktado, sed vi ne certas kiel ĝi kondutos en "batalaj" kondiĉoj sub peza ŝarĝo.

Estas solvo: flagoj en CGI-parametroj povas esti uzataj ne nur por A/B-testado, sed ankaŭ por testi novajn funkciojn.

Ni kreis ilon, kiu ebligas vin tuj ŝanĝi agordon en miloj da serviloj sen elmontri la servon al riskoj. Ĝi nomiĝas Stop Tap. La originala ideo estis povi rapide malŝalti iujn funkciojn sen aranĝo. Tiam la ilo disetendiĝis kaj fariĝis pli kompleksa.

La serva fludiagramo estas prezentita sube:

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

Flagvaloroj estas fiksitaj per la API. La administra servo konservas ĉi tiujn valorojn en la datumbazo. Ĉiuj serviloj iras al la datumbazo unufoje ĉiujn dek sekundojn, pumpas flagajn valorojn kaj aplikas ĉi tiujn valorojn al ĉiu peto.

En la Stop-klapo vi povas agordi du specojn de valoroj:

1) Kondiĉaj esprimoj. Apliki kiam unu el la valoroj estas vera. Ekzemple:

{
	"condition":"IS_DC1",
	"value":"3",
}, 
{
	"condition": "CLUSTER==2 and IS_BERU", 
	"value": "4!" 
}

La valoro "3" estos aplikata kiam la peto estas procesita en loko DC1. Kaj la valoro estas "4" kiam la peto estas procesita sur la dua areto por la retejo beru.ru.

2) Senkondiĉaj valoroj. Apliki defaŭlte se neniu el la kondiĉoj estas plenumita. Ekzemple:

valoro, valoro!

Se valoro finiĝas per ekkrio, ĝi ricevas pli altan prioritaton.

La CGI-parametro-analizilo analizas la URL. Tiam aplikas la valorojn de la Stop Tap.

Valoroj kun la jenaj prioritatoj estas aplikataj:

  1. Kun pliigita prioritato de la Stop Tap (ekkrio).
  2. La valoro de la peto.
  3. Defaŭlta valoro de Haltu frapeto.
  4. Defaŭlta valoro en kodo.

Estas multaj flagoj, kiuj estas indikitaj en kondiĉaj valoroj - ili sufiĉas por ĉiuj scenaroj konataj de ni:

  • Datumcentro.
  • Medio: produktado, testado, ombro.
  • Venue: market, beru.
  • Areto nombro.

Per ĉi tiu ilo, vi povas ebligi novan funkcion en certa grupo da serviloj (ekzemple, en nur unu datumcentro) kaj testi la funkciadon de ĉi tiu funkcio sen ia aparta risko por la tuta servo. Eĉ se vi faris gravan eraron ie, ĉio komencis fali kaj la tuta datumcentro falis, ekvilibristoj redirektos petojn al aliaj datumcentroj. Finaj uzantoj nenion rimarkos.

Se vi rimarkas problemon, vi povas tuj redoni la flagon al ĝia antaŭa valoro kaj la ŝanĝoj estos reigitaj.

Ĉi tiu servo ankaŭ havas siajn malavantaĝojn: la programistoj tre amas ĝin kaj ofte provas puŝi ĉiujn ŝanĝojn en la Stop Tap. Ni provas batali misuzon.

La aliro Stop Tap funkcias bone kiam vi jam havas stabilan kodon preta por esti lanĉita al produktado. Samtempe, vi ankoraŭ havas dubojn, kaj vi volas kontroli la kodon en "batalaj" kondiĉoj.

Tamen, Stop Tap ne taŭgas por testado dum disvolviĝo. Estas aparta areto por programistoj nomata "ombra areto".

Sekreta Testado: Ombra Areto

Petoj de unu el la aretoj estas duobligitaj al la ombrogrupo. Sed la ekvilibristo tute ignoras la respondojn de ĉi tiu aro. La diagramo de ĝia funkciado estas prezentita sube.

Kiel Yandex.Market-serĉo funkcias kaj kio okazas se unu el la serviloj malsukcesas

Ni ricevas testan areton, kiu estas en realaj "batalaj" kondiĉoj. Normala uzanttrafiko iras tien. La aparataro en ambaŭ aretoj estas la sama, do agado kaj eraroj povas esti komparitaj.

Kaj ĉar la ekvilibristo tute ignoras respondojn, finaj uzantoj ne vidos respondojn de la ombrogrupo. Tial ne estas timiga erari.

trovoj

Do, kiel ni konstruis la Merkatan serĉon?

Por ke ĉio iru glate, ni apartigas funkciojn en apartajn servojn. Tiel ni povas grimpi nur tiujn komponantojn, kiujn ni bezonas kaj simpligi la komponantojn. Estas facile asigni apartan komponanton al alia teamo kaj dividi la respondecojn por labori pri ĝi. Kaj signifa ŝparado en fero kun ĉi tiu aliro estas evidenta pluso.

La ombrogrupo ankaŭ helpas nin: ni povas disvolvi servojn, testi ilin en la procezo kaj ne ĝeni la uzanton.

Nu, testado en produktado, kompreneble. Ĉu vi bezonas ŝanĝi agordon en miloj da serviloj? Facile, uzu la Haltfrapon. Tiel vi povas tuj elmeti pretan kompleksan solvon kaj reveni al stabila versio se aperos problemoj.

Mi esperas, ke mi povis montri kiel ni faras la Merkaton rapida kaj stabila kun ĉiam kreskanta bazo de ofertoj. Kiel ni solvas servilproblemojn, traktas grandegan nombron da petoj, plibonigas la flekseblecon de la servo kaj faras tion sen interrompi laborprocezojn.

fonto: www.habr.com

Aldoni komenton