Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

Hei, jeg heter Evgeniy. Jeg jobber i Yandex.Market-søkeinfrastrukturen. Jeg vil fortelle Habr-miljøet om det indre kjøkkenet i Markedet – og jeg har mye å fortelle. Først av alt, hvordan markedssøket fungerer, prosesser og arkitektur. Hvordan håndterer vi nødsituasjoner: hva skjer hvis en server går ned? Hva om det er 100 slike servere?

Du vil også lære hvordan vi implementerer ny funksjonalitet på en haug med servere samtidig. Og hvordan vi tester komplekse tjenester direkte i produksjon, uten å forårsake noen ulempe for brukerne. Generelt, hvordan Market-søket fungerer slik at alle har det bra.

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

Litt om oss: hvilket problem vi løser

Når du skriver inn tekst, søker etter et produkt etter parametere, eller sammenligner priser i forskjellige butikker, sendes alle forespørsler til søketjenesten. Søk er den største tjenesten i markedet.

Vi behandler alle søkeforespørsler: fra nettstedene market.yandex.ru, beru.ru, Supercheck-tjenesten, Yandex.Advisor, mobilapplikasjoner. Vi inkluderer også produkttilbud i søkeresultater på yandex.ru.

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

Med søketjeneste mener jeg ikke bare selve søket, men også en database med alle tilbudene på Markedet. Skalaen er denne: mer enn en milliard søkeforespørsler behandles per dag. Og alt skal fungere raskt, uten avbrudd og alltid gi ønsket resultat.

Hva er hva: Markedsarkitektur

Jeg vil kort beskrive markedets nåværende arkitektur. Det kan grovt beskrives av diagrammet nedenfor:
Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter
La oss si at en partnerbutikk kommer til oss. Han sier jeg vil selge et leketøy: denne onde katten med en squeaker. Og en annen sint katt uten en squeaker. Og bare en katt. Deretter må butikken utarbeide tilbud som Markedet søker etter. Butikken genererer en spesiell xml med tilbud og kommuniserer veien til denne xml-en gjennom affiliategrensesnittet. Indekseringsprogrammet laster deretter med jevne mellomrom ned denne xml-en, ser etter feil og lagrer all informasjon i en enorm database.

Det er mange slike lagrede xml-filer. En søkeindeks opprettes fra denne databasen. Indeksen lagres i internt format. Etter å ha opprettet indeksen, laster Layout-tjenesten den opp til søkeservere.

Som et resultat vises en sint katt med en squeaker i databasen, og kattens indeks vises på serveren.

Jeg skal fortelle deg hvordan vi søker etter en katt i delen om søkearkitektur.

Markedssøkearkitektur

Vi lever i en verden av mikrotjenester: hver innkommende forespørsel market.yandex.ru forårsaker mange underspørringer, og dusinvis av tjenester er involvert i behandlingen av dem. Diagrammet viser bare noen få:

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter
Forenklet forespørselsbehandlingsopplegg

Hver tjeneste har en fantastisk ting - sin egen balanserer med et unikt navn:

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

Balanseringen gir oss større fleksibilitet i å administrere tjenesten: du kan for eksempel slå av servere, noe som ofte kreves for oppdateringer. Balanseren ser at serveren er utilgjengelig og omdirigerer automatisk forespørsler til andre servere eller datasentre. Når du legger til eller fjerner en server, blir lasten automatisk omfordelt mellom serverne.

Det unike navnet på balanseren avhenger ikke av datasenteret. Når tjeneste A sender en forespørsel til B, omdirigerer B som standard forespørselen til gjeldende datasenter. Hvis tjenesten er utilgjengelig eller ikke eksisterer i gjeldende datasenter, blir forespørselen omdirigert til andre datasentre.

En enkelt FQDN for alle datasentre gjør at tjeneste A kan abstrahere fullstendig fra lokasjoner. Hans forespørsel til tjeneste B vil alltid bli behandlet. Unntaket er tilfellet når tjenesten er plassert i alle datasentre.

Men ikke alt er så rosenrødt med denne balanseren: vi har en ekstra mellomkomponent. Balanseringen kan være ustabil, og dette problemet løses av redundante servere. Det er også en ekstra forsinkelse mellom tjeneste A og B. Men i praksis er den mindre enn 1 ms og for de fleste tjenester er dette ikke kritisk.

Håndtering av det uventede: Balansering av søketjeneste og motstandskraft

Tenk deg at det er en kollaps: du må finne en katt med en squeaker, men serveren krasjer. Eller 100 servere. Hvordan komme seg ut? Skal vi virkelig la brukeren være uten katt?

Situasjonen er skummel, men vi er klare for det. Jeg skal fortelle deg i rekkefølge.

Søkeinfrastrukturen er plassert i flere datasentre:

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

Ved design inkluderer vi muligheten for å stenge ned ett datasenter. Livet er fullt av overraskelser - for eksempel kan en gravemaskin kutte en jordkabel (ja, det skjedde). Kapasiteten i de resterende datasentrene skal være tilstrekkelig til å tåle toppbelastning.

La oss vurdere et enkelt datasenter. Hvert datasenter har samme balanseringsoperasjonsskjema:

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter
En balanserer er minst tre fysiske servere. Denne redundansen er laget for pålitelighet. Balansere kjører på HAProx.

Vi valgte HAProx på grunn av høy ytelse, lave ressurskrav og brede funksjonalitet. Vår søkeprogramvare kjører på hver server.

Sannsynligheten for at én server svikter er lav. Men hvis du har mange servere, øker sannsynligheten for at minst én går ned.

Dette er hva som skjer i virkeligheten: servere krasjer. Derfor er det nødvendig å kontinuerlig overvåke statusen til alle servere. Hvis serveren slutter å svare, kobles den automatisk fra trafikken. Til dette formålet har HAProxy en innebygd helsesjekk. Den går til alle servere en gang i sekundet med en HTTP-forespørsel "/ping".

En annen funksjon ved HAProxy: agent-sjekk lar deg laste alle servere jevnt. For å gjøre dette kobler HAProxy seg til alle servere, og de returnerer vekten sin avhengig av gjeldende belastning fra 1 til 100. Vekten beregnes basert på antall forespørsler i køen for behandling og belastningen på prosessoren.

Nå om å finne katten. Søket resulterer i forespørsler som: /search?text=sint+katt. For at søket skal være raskt, må hele katteindeksen passe inn i RAM. Selv lesing fra SSD er ikke rask nok.

En gang i tiden var tilbudsdatabasen liten, og RAM-en til én server var nok til det. Etter hvert som tilbudsbasen vokste, passet ikke alt inn i denne RAM-en lenger, og dataene ble delt inn i to deler: shard 1 og shard 2.

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter
Men dette skjer alltid: enhver løsning, selv en god, gir opphav til andre problemer.

Balanseren gikk fortsatt til en hvilken som helst server. Men på maskinen der forespørselen kom, var det bare halvparten av indeksen. Resten var på andre servere. Derfor måtte serveren gå til en nabomaskin. Etter å ha mottatt data fra begge serverne, ble resultatene kombinert og rangert på nytt.

Siden balansereren fordeler forespørsler jevnt, var alle servere engasjert i omrangering, og ikke bare å sende data.

Problemet oppsto hvis en naboserver ikke var tilgjengelig. Løsningen var å spesifisere flere servere med ulike prioriteter som en "nabo"-server. Først ble forespørselen sendt til serverne i gjeldende rack. Hvis det ikke kom noe svar, ble forespørselen sendt til alle serverne i dette datasenteret. Og til slutt gikk forespørselen til andre datasentre.
Etter hvert som antallet forslag vokste, ble dataene delt inn i fire deler. Men dette var ikke grensen.

For øyeblikket brukes en konfigurasjon på åtte shards. I tillegg, for å spare enda mer minne, ble indeksen delt inn i en søkedel (som brukes til søk) og en snippedel (som ikke er involvert i søket).

Én server inneholder informasjon for kun ett shard. Derfor, for å søke i hele indeksen, må du søke på åtte servere som inneholder forskjellige shards.

Servere er gruppert i klynger. Hver klynge inneholder åtte søkemotorer og én kodebitserver.

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter
Snittserveren kjører en nøkkelverdidatabase med statiske data. De er nødvendige for å utstede dokumenter, for eksempel en beskrivelse av en katt med en squeaker. Dataene overføres spesielt til en egen server for ikke å laste minnet til søkeservere.

Siden dokument-ID-er bare er unike innenfor én indeks, kan det oppstå en situasjon der det ikke er dokumenter i utdragene. Vel, eller at for én ID vil det være forskjellig innhold. Derfor, for at søket skulle fungere og resultater skulle returneres, var det behov for konsistens på tvers av hele klyngen. Jeg skal fortelle deg nedenfor hvordan vi overvåker konsistens.

Selve søket er strukturert som følger: en søkeforespørsel kan komme til hvilken som helst av de åtte serverne. La oss si at han kom til server 1. Denne serveren behandler alle argumentene og forstår hva og hvordan den skal se etter. Avhengig av den innkommende forespørselen, kan serveren gjøre ytterligere forespørsler til eksterne tjenester for nødvendig informasjon. En forespørsel kan følges av opptil ti forespørsler til eksterne tjenester.

Etter å ha samlet inn nødvendig informasjon, starter et søk i tilbudsdatabasen. For å gjøre dette gjøres underspørringer til alle åtte serverne i klyngen.

Når svarene er mottatt, kombineres resultatene. Til slutt kan det være nødvendig med flere underspørringer til kodebitserveren for å generere resultatene.

Søkespørringer i klyngen ser slik ut: /shard1?text=sint+katt. I tillegg gjøres det konstant underspørringer av skjemaet mellom alle servere i klyngen én gang i sekundet: /status.

henvendelse /status oppdager en situasjon der serveren ikke er tilgjengelig.

Den kontrollerer også at søkemotorversjonen og indeksversjonen er den samme på alle servere, ellers vil det være inkonsistente data i klyngen.

Til tross for at én kodebitserver behandler forespørsler fra åtte søkemotorer, er prosessoren svært lett lastet. Derfor overfører vi nå kodebitdataene til en egen tjeneste.

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

For å overføre data introduserte vi universelle nøkler for dokumenter. Nå er det umulig for en situasjon der innhold fra et annet dokument returneres med én nøkkel.

Men overgangen til en annen arkitektur er ikke fullført ennå. Nå ønsker vi å bli kvitt den dedikerte snippetserveren. Og så bevege deg helt bort fra klyngestrukturen. Dette vil tillate oss å fortsette å skalere enkelt. En ekstra bonus er betydelige jernbesparelser.

Og nå til skumle historier med lykkelige slutter. La oss vurdere flere tilfeller av server utilgjengelighet.

Noe forferdelig skjedde: én server er utilgjengelig

La oss si at én server er utilgjengelig. Da kan de resterende serverne i klyngen fortsette å svare, men søkeresultatene vil være ufullstendige.

Via statussjekk /status naboservere forstår at en ikke er tilgjengelig. Derfor, for å opprettholde fullstendighet, alle servere i klyngen per forespørsel /ping de begynner å svare balanseren at de også er utilgjengelige. Det viser seg at alle serverne i klyngen døde (noe som ikke er sant). Dette er hovedulempen med klyngeordningen vår - det er derfor vi ønsker å komme vekk fra det.

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

Forespørsler som mislykkes med en feil sendes på nytt av balanseren på andre servere.
Balanseren slutter også å sende brukertrafikk til døde servere, men fortsetter å sjekke statusen deres.

Når serveren blir tilgjengelig, begynner den å svare på /ping. Så snart normale svar på ping fra døde servere begynner å komme, begynner balanserere å sende brukertrafikk dit. Klyngedrift er gjenopprettet, hurra.

Enda verre: mange servere er utilgjengelige

En betydelig del av serverne i datasenteret kuttes ned. Hva skal man gjøre, hvor skal man løpe? Balanseren kommer til unnsetning igjen. Hver balanserer lagrer konstant det gjeldende antallet live-servere i minnet. Den beregner hele tiden den maksimale mengden trafikk som det nåværende datasenteret kan behandle.

Når mange servere i et datasenter går ned, innser balansereren at dette datasenteret ikke kan behandle all trafikken.

Da begynner overflødig trafikk å bli tilfeldig fordelt til andre datasentre. Alt fungerer, alle er fornøyde.

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

Slik gjør vi det: publiserer utgivelser

La oss nå snakke om hvordan vi publiserer endringer som er gjort i tjenesten. Her har vi tatt veien for å forenkle prosesser: utrulling av en ny utgivelse er nesten fullstendig automatisert.
Når et visst antall endringer er akkumulert i prosjektet, opprettes en ny utgivelse automatisk og byggingen starter.

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

Deretter rulles tjenesten ut til testing, hvor driftsstabiliteten kontrolleres.

Samtidig lanseres automatisk ytelsestesting. Dette håndteres av en spesialtjeneste. Jeg vil ikke snakke om det nå - beskrivelsen er verdig en egen artikkel.

Hvis publisering i testing er vellykket, startes publisering av utgivelsen i prestable automatisk. Prestable er en spesiell klynge hvor normal brukertrafikk dirigeres. Hvis den returnerer en feil, sender balansereren en ny forespørsel til produksjon.

I prestable måles responstider og sammenlignes med forrige utgivelse i produksjon. Hvis alt er bra, kobler en person seg til: sjekker grafene og resultatene av lasttesting og begynner deretter å rulle ut til produksjon.

Alt det beste går til brukeren: A/B-testing

Det er ikke alltid åpenbart om endringer i en tjeneste vil gi reelle fordeler. For å måle nytten av endringer kom folk opp med A/B-testing. Jeg skal fortelle deg litt om hvordan det fungerer i Yandex.Markedssøk.

Det hele starter med å legge til en ny CGI-parameter som muliggjør ny funksjonalitet. La vår parameter være: market_new_functionality=1. Så i koden aktiverer vi denne funksjonaliteten hvis flagget er tilstede:

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

Ny funksjonalitet rulles ut til produksjon.

For å automatisere A/B-testing er det en dedikert tjeneste som gir detaljert informasjon beskrevet her. Et eksperiment opprettes i tjenesten. Trafikkandelen settes for eksempel til 15 %. Prosentandeler angis ikke for spørringer, men for brukere. Varigheten av eksperimentet er også angitt, for eksempel en uke.

Flere eksperimenter kan kjøres samtidig. I innstillingene kan du angi om skjæring med andre eksperimenter er mulig.

Som et resultat legger tjenesten automatisk til et argument market_new_functionality=1 til 15 % av brukerne. Den beregner også automatisk de valgte beregningene. Etter at eksperimentet er fullført, ser analytikere på resultatene og trekker konklusjoner. Basert på funnene tas det en beslutning om å rulle ut til produksjon eller foredling.

Markedets flinke hånd: testing i produksjon

Det hender ofte at du trenger å teste driften av en ny funksjonalitet i produksjonen, men du er ikke sikker på hvordan den vil oppføre seg under "kamp" under tung belastning.

Det er en løsning: flagg i CGI-parametere kan brukes ikke bare for A/B-testing, men også for å teste ny funksjonalitet.

Vi laget et verktøy som lar deg umiddelbart endre konfigurasjon på tusenvis av servere uten å utsette tjenesten for risiko. Det heter Stop Tap. Den opprinnelige ideen var å raskt kunne deaktivere noe funksjonalitet uten en layout. Så utvidet verktøyet seg og ble mer komplekst.

Tjenesteflytdiagrammet er presentert nedenfor:

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

Flaggverdier settes via API. Administrasjonstjenesten lagrer disse verdiene i databasen. Alle servere går til databasen hvert tiende sekund, pumper ut flaggverdier og bruker disse verdiene på hver forespørsel.

I Stopp trykk kan du angi to typer verdier:

1) Betingede uttrykk. Bruk når en av verdiene er sann. For eksempel:

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

Verdien "3" vil bli brukt når forespørselen behandles på plassering DC1. Og verdien er "4" når forespørselen behandles på den andre klyngen for nettstedet beru.ru.

2) Ubetingede verdier. Bruk som standard hvis ingen av betingelsene er oppfylt. For eksempel:

verdi, verdi!

Hvis en verdi ender med et utropstegn, gis den høyere prioritet.

CGI-parameterparseren analyserer URL-en. Deretter bruker du verdiene fra Stop Tap.

Verdier med følgende prioriteringer brukes:

  1. Med økt prioritet fra Stop Tap (utropstegn).
  2. Verdien fra forespørselen.
  3. Standardverdi fra Stopp trykk.
  4. Standardverdi i kode.

Det er mange flagg som er angitt i betingede verdier - de er nok for alle scenarier kjent for oss:

  • Datasenter.
  • Miljø: produksjon, testing, skygge.
  • Sted: marked, beru.
  • Klyngenummer.

Med dette verktøyet kan du aktivere ny funksjonalitet på en bestemt gruppe servere (for eksempel i bare ett datasenter) og teste driften av denne funksjonaliteten uten noen spesiell risiko for hele tjenesten. Selv om du gjorde en alvorlig feil et sted, begynte alt å falle og hele datasenteret gikk ned, balansere vil omdirigere forespørsler til andre datasentre. Sluttbrukere vil ikke merke noe.

Hvis du oppdager et problem, kan du umiddelbart returnere flagget til dets forrige verdi og endringene vil bli rullet tilbake.

Denne tjenesten har også sine ulemper: utviklerne elsker den veldig mye og prøver ofte å presse alle endringene inn i Stop Tap. Vi prøver å bekjempe misbruk.

Stop Tap-tilnærmingen fungerer bra når du allerede har stabil kode klar til å bli rullet ut til produksjon. Samtidig er du fortsatt i tvil, og du vil sjekke koden i "kamp"-forhold.

Stop Tap er imidlertid ikke egnet for testing under utvikling. Det er en egen klynge for utviklere kalt "skyggeklyngen".

Hemmelig testing: Shadow Cluster

Forespørsler fra en av klyngene dupliseres til skyggeklyngen. Men balansereren ignorerer fullstendig svarene fra denne klyngen. Diagrammet over driften er presentert nedenfor.

Hvordan Yandex.Market-søk fungerer og hva skjer hvis en av serverne svikter

Vi får en testklynge som er i virkelige "kamp"-forhold. Normal brukertrafikk går dit. Maskinvaren i begge klynger er den samme, så ytelse og feil kan sammenlignes.

Og siden balanseren ignorerer svar fullstendig, vil sluttbrukere ikke se svar fra skyggeklyngen. Derfor er det ikke skummelt å gjøre en feil.

Funn

Så hvordan bygget vi opp markedssøket?

For å få alt til å gå knirkefritt deler vi funksjonalitet i separate tjenester. På denne måten kan vi skalere bare de komponentene vi trenger og gjøre komponentene enklere. Det er enkelt å tilordne en separat komponent til et annet team og dele ansvaret for å jobbe med det. Og betydelige besparelser i jern med denne tilnærmingen er et åpenbart pluss.

Skyggeklyngen hjelper oss også: vi kan utvikle tjenester, teste dem i prosessen og ikke forstyrre brukeren.

Vel, testing i produksjon, selvfølgelig. Trenger du å endre konfigurasjon på tusenvis av servere? Enkelt, bruk Stop Tap. På denne måten kan du umiddelbart rulle ut en ferdig kompleks løsning og rulle tilbake til en stabil versjon hvis det oppstår problemer.

Jeg håper jeg klarte å vise hvordan vi gjør markedet raskt og stabilt med en stadig voksende base av tilbud. Hvordan vi løser serverproblemer, håndterer et stort antall forespørsler, forbedrer fleksibiliteten til tjenesten og gjør dette uten å forstyrre arbeidsprosessene.

Kilde: www.habr.com

Legg til en kommentar