Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

Hej, jag heter Evgeniy. Jag arbetar i Yandex.Markets sökinfrastruktur. Jag vill berätta för Habr-gemenskapen om Marknadens inre kök – och jag har mycket att berätta. Först och främst hur marknadssökningen fungerar, processer och arkitektur. Hur hanterar vi nödsituationer: vad händer om en server går ner? Vad händer om det finns 100 sådana servrar?

Du kommer också att lära dig hur vi implementerar ny funktionalitet på ett gäng servrar samtidigt. Och hur vi testar komplexa tjänster direkt i produktionen, utan att orsaka några olägenheter för användarna. I allmänhet, hur marknadssökningen fungerar så att alla har det bra.

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

Lite om oss: vilket problem vi löser

När du skriver in text, söker efter en produkt på parametrar eller jämför priser i olika butiker skickas alla förfrågningar till söktjänsten. Sök är den största tjänsten på marknaden.

Vi behandlar alla sökförfrågningar: från webbplatserna market.yandex.ru, beru.ru, Supercheck-tjänsten, Yandex.Advisor, mobilapplikationer. Vi inkluderar även produkterbjudanden i sökresultaten på yandex.ru.

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

Med söktjänst menar jag inte bara själva sökningen utan även en databas med alla erbjudanden på marknaden. Skalan är denna: mer än en miljard sökförfrågningar behandlas per dag. Och allt ska fungera snabbt, utan avbrott och alltid ge önskat resultat.

Vad är vad: Marknadsarkitektur

Jag kommer kort att beskriva marknadens nuvarande arkitektur. Det kan grovt beskrivas med diagrammet nedan:
Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas
Låt oss säga att en partnerbutik kommer till oss. Han säger att jag vill sälja en leksak: den här elaka katten med ett gnisslande. Och en annan arg katt utan gnissling. Och bara en katt. Sedan behöver butiken förbereda erbjudanden som Marknaden söker efter. Butiken genererar en speciell xml med erbjudanden och kommunicerar vägen till denna xml genom affiliate-gränssnittet. Indexeraren laddar sedan med jämna mellanrum ner denna xml, letar efter fel och sparar all information i en enorm databas.

Det finns många sådana sparade xml-filer. Ett sökindex skapas från denna databas. Indexet lagras i internt format. Efter att ha skapat indexet laddar Layout-tjänsten upp det till sökservrar.

Som ett resultat visas en arg katt med ett pip i databasen, och kattens index visas på servern.

Jag ska berätta hur vi söker efter en katt i delen om sökarkitektur.

Marknadssökningsarkitektur

Vi lever i en värld av mikrotjänster: varje inkommande förfrågan market.yandex.ru orsakar många underfrågor och dussintals tjänster är inblandade i deras bearbetning. Diagrammet visar bara några få:

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas
Förenklat bearbetningsschema för begäran

Varje tjänst har en underbar sak - sin egen balanserare med ett unikt namn:

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

Balanseraren ger oss större flexibilitet i att hantera tjänsten: du kan till exempel stänga av servrar, vilket ofta krävs för uppdateringar. Balanseraren ser att servern inte är tillgänglig och omdirigerar automatiskt förfrågningar till andra servrar eller datacenter. När du lägger till eller tar bort en server omfördelas lasten automatiskt mellan servrarna.

Balansarens unika namn beror inte på datacentret. När tjänst A gör en begäran till B, omdirigerar balansör B som standard begäran till det aktuella datacentret. Om tjänsten inte är tillgänglig eller inte finns i det aktuella datacentret, omdirigeras begäran till andra datacenter.

Ett enda FQDN för alla datacenter tillåter tjänst A att helt abstrahera från platser. Hans begäran till tjänst B kommer alltid att behandlas. Undantaget är fallet när tjänsten finns i alla datacenter.

Men allt är inte så rosenrött med denna balanserare: vi har ytterligare en mellankomponent. Balanseraren kan vara instabil och detta problem löses av redundanta servrar. Det finns också en extra fördröjning mellan tjänster A och B. Men i praktiken är det mindre än 1 ms och för de flesta tjänster är detta inte kritiskt.

Att hantera det oväntade: Söktjänstens balansering och motståndskraft

Föreställ dig att det är en kollaps: du måste hitta en katt med ett pip, men servern kraschar. Eller 100 servrar. Hur tar man sig ut? Kommer vi verkligen att lämna användaren utan katt?

Situationen är skrämmande, men vi är redo för det. Jag ska berätta i ordning.

Sökinfrastrukturen finns i flera datacenter:

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

Vid design inkluderar vi möjligheten att stänga ett datacenter. Livet är fullt av överraskningar – till exempel kan en grävmaskin skära av en jordkabel (ja, det hände). Kapaciteten i de återstående datacenterna bör vara tillräcklig för att klara toppbelastning.

Låt oss överväga ett enda datacenter. Varje datacenter har samma balanseringsfunktion:

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas
En balanserare är minst tre fysiska servrar. Denna redundans är gjord för tillförlitlighet. Balanserare körs på HAProx.

Vi valde HAProx på grund av dess höga prestanda, låga resurskrav och breda funktionalitet. Vår sökmjukvara körs inuti varje server.

Sannolikheten för att en server misslyckas är låg. Men om du har många servrar ökar sannolikheten att minst en går ner.

Detta är vad som händer i verkligheten: servrar kraschar. Därför är det nödvändigt att ständigt övervaka statusen för alla servrar. Om servern slutar svara kopplas den automatiskt från trafik. För detta ändamål har HAProxy en inbyggd hälsokontroll. Den går till alla servrar en gång i sekunden med en HTTP-begäran "/ping".

En annan funktion hos HAProxy: agent-check låter dig ladda alla servrar jämnt. För att göra detta ansluter HAProxy till alla servrar, och de returnerar sin vikt beroende på aktuell belastning från 1 till 100. Vikten beräknas utifrån antalet förfrågningar i kön för bearbetning och belastningen på processorn.

Nu om att hitta katten. Sökresultaten i förfrågningar som: /search?text=arg+katt. För att sökningen ska gå snabbt måste hela kattindexet passa in i RAM. Även att läsa från SSD:n är inte tillräckligt snabbt.

En gång i tiden var erbjudandedatabasen liten, och RAM-minnet på en server räckte för det. Allt eftersom utbudsbasen växte passade inte längre allt in i detta RAM-minne, och data delades upp i två delar: shard 1 och shard 2.

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas
Men detta händer alltid: varje lösning, även en bra, ger upphov till andra problem.

Balanseraren gick fortfarande till vilken server som helst. Men på maskinen där förfrågan kom fanns bara hälften av indexet. Resten fanns på andra servrar. Därför var servern tvungen att gå till någon närliggande maskin. Efter att ha tagit emot data från båda servrarna kombinerades resultaten och rangordnades om.

Eftersom balansören fördelar förfrågningar jämnt, var alla servrar engagerade i omrangering, och inte bara att skicka data.

Problemet uppstod om en angränsande server inte var tillgänglig. Lösningen var att ange flera servrar med olika prioriteringar som en "grannserver". Först skickades begäran till servrarna i det aktuella racket. Om det inte kom något svar skickades begäran till alla servrar i detta datacenter. Och slutligen gick förfrågan till andra datacenter.
I takt med att antalet förslag växte delades uppgifterna upp i fyra delar. Men detta var inte gränsen.

För närvarande används en konfiguration av åtta skärvor. Dessutom, för att spara ännu mer minne, delades indexet upp i en sökdel (som används för sökning) och en utdragsdel (som inte är inblandad i sökningen).

En server innehåller information för endast en shard. För att söka i hela indexet måste du därför söka på åtta servrar som innehåller olika skärvor.

Servrar är grupperade i kluster. Varje kluster innehåller åtta sökmotorer och en utdragsserver.

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas
Snuttservern kör en nyckel-värdesdatabas med statisk data. De behövs för att utfärda dokument, till exempel en beskrivning av en katt med ett pip. Data överförs speciellt till en separat server för att inte ladda minnet på sökservrarna.

Eftersom dokument-ID:n är unika endast inom ett index kan en situation uppstå där det inte finns några dokument i utdragen. Tja, eller att det för ett ID kommer att finnas olika innehåll. Därför, för att sökningen skulle fungera och resultat skulle returneras, fanns det ett behov av konsekvens över hela klustret. Jag berättar nedan hur vi övervakar konsekvens.

Själva sökningen är strukturerad enligt följande: en sökförfrågan kan komma till vilken som helst av de åtta servrarna. Låt oss säga att han kom till server 1. Den här servern bearbetar alla argument och förstår vad och hur den ska leta efter. Beroende på den inkommande förfrågan kan servern göra ytterligare förfrågningar till externa tjänster för nödvändig information. En begäran kan följas av upp till tio förfrågningar till externa tjänster.

Efter att ha samlat in nödvändig information börjar en sökning i erbjudandedatabasen. För att göra detta görs underfrågor till alla åtta servrarna i klustret.

När svaren har tagits emot kombineras resultaten. I slutändan kan flera underfrågor till kodavsnittsservern behövas för att generera resultaten.

Sökfrågor inom klustret ser ut så här: /shard1?text=arg+katt. Dessutom görs underfrågor av formuläret ständigt mellan alla servrar inom klustret en gång i sekunden: /status.

utredning /status upptäcker en situation där servern inte är tillgänglig.

Den kontrollerar också att sökmotorversionen och indexversionen är samma på alla servrar, annars kommer det att finnas inkonsekventa data inom klustret.

Trots det faktum att en utdragsserver bearbetar förfrågningar från åtta sökmotorer, är dess processor mycket lätt laddad. Därför överför vi nu utdragsdata till en separat tjänst.

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

För att överföra data introducerade vi universella nycklar för dokument. Nu är det omöjligt för en situation där innehåll från ett annat dokument returneras med en nyckel.

Men övergången till en annan arkitektur är inte klar än. Nu vill vi bli av med den dedikerade snippetservern. Och sedan gå bort från klusterstrukturen helt och hållet. Detta gör att vi kan fortsätta att skala enkelt. En extra bonus är betydande järnbesparingar.

Och nu till skrämmande historier med lyckliga slut. Låt oss överväga flera fall av serverns otillgänglighet.

Något hemskt hände: en server är inte tillgänglig

Låt oss säga att en server inte är tillgänglig. Sedan kan de återstående servrarna i klustret fortsätta att svara, men sökresultaten kommer att vara ofullständiga.

Via statuskontroll /status angränsande servrar förstår att en inte är tillgänglig. Därför, för att bibehålla fullständighet, alla servrar i klustret per begäran /ping de börjar svara balanseraren att de inte heller är tillgängliga. Det visar sig att alla servrar i klustret dog (vilket inte är sant). Detta är den största nackdelen med vårt klusterschema - det är därför vi vill komma bort från det.

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

Förfrågningar som misslyckas med ett fel skickas om av balansören på andra servrar.
Balanseraren slutar också att skicka användartrafik till döda servrar, men fortsätter att kontrollera deras status.

När servern blir tillgänglig börjar den svara på /ping. Så fort normala svar på pingar från döda servrar börjar komma, börjar balanserare skicka användartrafik dit. Klusterdriften är återställd, hurra.

Ännu värre: många servrar är inte tillgängliga

En betydande del av servrarna i datacentret skärs ner. Vad ska man göra, vart ska man springa? Balanseraren kommer till undsättning igen. Varje balansör lagrar konstant det aktuella antalet liveservrar i minnet. Den beräknar hela tiden den maximala mängden trafik som det aktuella datacentret kan bearbeta.

När många servrar i ett datacenter går ner inser balansören att detta datacenter inte kan hantera all trafik.

Sedan börjar överskottstrafiken fördelas slumpmässigt till andra datacenter. Allt fungerar, alla är nöjda.

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

Så här gör vi: publicerar releaser

Låt oss nu prata om hur vi publicerar ändringar som gjorts i tjänsten. Här har vi tagit vägen för att förenkla processer: att rulla ut en ny version är nästan helt automatiserad.
När ett visst antal ändringar ackumuleras i projektet skapas en ny version automatiskt och dess uppbyggnad startar.

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

Därefter rullas tjänsten ut till testning, där driftstabiliteten kontrolleras.

Samtidigt lanseras automatisk prestandatestning. Detta sköts av en särskild tjänst. Jag kommer inte att prata om det nu - dess beskrivning är värd en separat artikel.

Om publiceringen i testning lyckas startas publiceringen av releasen i prestable automatiskt. Prestable är ett speciellt kluster dit normal användartrafik dirigeras. Om det returnerar ett fel, gör balanseraren en ny begäran till produktion.

I prestable mäts svarstider och jämförs med tidigare release i produktion. Om allt är bra, ansluter en person: kontrollerar graferna och resultaten av lasttestning och börjar sedan rulla ut till produktion.

Allt det bästa går till användaren: A/B-testning

Det är inte alltid självklart om förändringar av en tjänst ger verkliga fördelar. För att mäta nyttan av förändringar kom folk på A/B-testning. Jag ska berätta lite om hur det fungerar i Yandex.Marketsökning.

Det hela börjar med att lägga till en ny CGI-parameter som möjliggör ny funktionalitet. Låt vår parameter vara: market_new_functionality=1. Sedan aktiverar vi den här funktionen i koden om flaggan finns:

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

Ny funktionalitet rullas ut till produktion.

För att automatisera A/B-testning finns det en dedikerad tjänst som ger detaljerad information beskrivs här. Ett experiment skapas i tjänsten. Trafikandelen sätts till exempel till 15 %. Procentsatser ställs inte in för frågor, utan för användare. Experimentets varaktighet anges också, till exempel en vecka.

Flera experiment kan köras samtidigt. I inställningarna kan du ange om korsning med andra experiment är möjlig.

Som ett resultat lägger tjänsten automatiskt till ett argument market_new_functionality=1 till 15 % av användarna. Den beräknar också automatiskt de valda mätvärdena. Efter att experimentet är avslutat tittar analytiker på resultaten och drar slutsatser. Baserat på resultaten fattas beslut om att rulla ut till produktion eller förädling.

Marknadens skickliga hand: testning i produktionen

Det händer ofta att du behöver testa driften av en ny funktionalitet i produktionen, men du är inte säker på hur den kommer att bete sig i "stridsförhållanden" under tung belastning.

Det finns en lösning: flaggor i CGI-parametrar kan användas inte bara för A/B-testning, utan också för att testa ny funktionalitet.

Vi gjorde ett verktyg som låter dig omedelbart ändra konfiguration på tusentals servrar utan att utsätta tjänsten för risker. Det heter Stop Tap. Den ursprungliga idén var att snabbt kunna inaktivera viss funktionalitet utan en layout. Sedan expanderade verktyget och blev mer komplext.

Serviceflödesdiagrammet presenteras nedan:

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

Flaggvärden ställs in via API:et. Managementtjänsten lagrar dessa värden i databasen. Alla servrar går till databasen en gång var tionde sekund, pumpar ut flaggvärden och tillämpar dessa värden på varje begäran.

I stoppknappen kan du ställa in två typer av värden:

1) Villkorsuttryck. Använd när ett av värdena är sant. Till exempel:

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

Värdet "3" kommer att tillämpas när begäran behandlas på plats DC1. Och värdet är "4" när begäran behandlas på det andra klustret för webbplatsen beru.ru.

2) Ovillkorliga värden. Använd som standard om inget av villkoren är uppfyllda. Till exempel:

värde, värde!

Om ett värde slutar med ett utropstecken ges det högre prioritet.

CGI-parameterparsern analyserar URL:en. Applicerar sedan värdena från Stop Tap.

Värden med följande prioriteringar tillämpas:

  1. Med ökad prioritet från Stop Tap (utropstecken).
  2. Värdet från begäran.
  3. Standardvärde från Stop tap.
  4. Standardvärde i kod.

Det finns många flaggor som anges i villkorliga värden - de räcker för alla scenarier som vi känner till:

  • Datacenter.
  • Miljö: produktion, testning, skugga.
  • Plats: marknad, beru.
  • Klusternummer.

Med det här verktyget kan du aktivera ny funktionalitet på en viss grupp av servrar (till exempel i endast ett datacenter) och testa funktionen av denna funktionalitet utan någon särskild risk för hela tjänsten. Även om du gjorde ett allvarligt misstag någonstans, allt började falla och hela datacentret gick ner, balanserar kommer att omdirigera förfrågningar till andra datacenter. Slutanvändare kommer inte att märka något.

Om du upptäcker ett problem kan du omedelbart återställa flaggan till dess tidigare värde och ändringarna kommer att återställas.

Den här tjänsten har också sina nackdelar: utvecklarna älskar den väldigt mycket och försöker ofta trycka in alla ändringar i Stop Tap. Vi försöker bekämpa missbruk.

Stop Tap-metoden fungerar bra när du redan har stabil kod redo att rullas ut till produktion. Samtidigt har du fortfarande tvivel, och du vill kontrollera koden under "stridsförhållanden".

Stop Tap är dock inte lämplig för testning under utveckling. Det finns ett separat kluster för utvecklare som kallas "skuggkluster".

Hemliga tester: Shadow Cluster

Förfrågningar från ett av klustren dupliceras till skuggklustret. Men balanseraren ignorerar helt svaren från detta kluster. Diagrammet över dess funktion presenteras nedan.

Hur Yandex.Marketsökning fungerar och vad händer om en av servrarna misslyckas

Vi får ett testkluster som är i verkliga "stridsförhållanden". Normal användartrafik går dit. Hårdvaran i båda klustren är densamma, så prestanda och fel kan jämföras.

Och eftersom balanseraren helt ignorerar svar, kommer slutanvändare inte att se svar från skuggklustret. Därför är det inte skrämmande att göra ett misstag.

Resultat

Så, hur byggde vi upp marknadssökningen?

För att allt ska gå smidigt delar vi upp funktionalitet i separata tjänster. På så sätt kan vi skala endast de komponenter som vi behöver och göra komponenterna enklare. Det är lätt att tilldela en separat komponent till ett annat team och dela ansvaret för att arbeta med det. Och betydande besparingar i järn med detta tillvägagångssätt är ett uppenbart plus.

Skuggklustret hjälper oss också: vi kan utveckla tjänster, testa dem i processen och inte störa användaren.

Jo, testning i produktion, förstås. Behöver du ändra konfigurationen på tusentals servrar? Enkelt, använd Stop Tap. På så sätt kan du omedelbart rulla ut en färdig komplex lösning och rulla tillbaka till en stabil version om problem uppstår.

Jag hoppas att jag kunde visa hur vi gör marknaden snabb och stabil med en ständigt växande bas av erbjudanden. Hur vi löser serverproblem, hanterar ett stort antal förfrågningar, förbättrar flexibiliteten i tjänsten och gör detta utan att avbryta arbetsprocesser.

Källa: will.com

Lägg en kommentar