Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

Ciao, mi chiamo Evgeniy. Lavoro nell'infrastruttura di ricerca Yandex.Market. Voglio parlare alla comunità Habr della cucina interna del Mercato - e ho molto da dire. Innanzitutto come funziona, i processi e l'architettura della ricerca di mercato. Come affrontiamo le situazioni di emergenza: cosa succede se un server va giù? E se ci fossero 100 server di questo tipo?

Imparerai anche come implementiamo nuove funzionalità su più server contemporaneamente. E come testiamo servizi complessi direttamente in produzione, senza arrecare alcun disagio agli utenti. In generale, come funziona la ricerca di mercato in modo che tutti si divertano.

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

Un po' di noi: quale problema risolviamo

Quando inserisci del testo, cerchi un prodotto in base ai parametri o confronti i prezzi in diversi negozi, tutte le richieste vengono inviate al servizio di ricerca. La ricerca è il servizio più grande sul mercato.

Elaboriamo tutte le richieste di ricerca: dai siti market.yandex.ru, beru.ru, il servizio Supercheck, Yandex.Advisor, applicazioni mobili. Includiamo anche offerte di prodotti nei risultati di ricerca su yandex.ru.

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

Per servizio di ricerca intendo non solo la ricerca stessa, ma anche un database con tutte le offerte del Mercato. La scala è questa: più di un miliardo di richieste di ricerca vengono elaborate ogni giorno. E tutto dovrebbe funzionare rapidamente, senza interruzioni e produrre sempre il risultato desiderato.

Cosa è cosa: architettura del mercato

Descriverò brevemente l’attuale architettura del Mercato. Può essere approssimativamente descritto dal diagramma seguente:
Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce
Diciamo che un negozio partner viene da noi. Dice che voglio vendere un giocattolo: questo gatto malvagio con un cigolio. E un altro gatto arrabbiato senza strillare. E solo un gatto. Quindi il negozio deve preparare le offerte che il mercato ricerca. Il negozio genera un xml speciale con le offerte e comunica il percorso verso questo xml attraverso l'interfaccia di affiliazione. L'indicizzatore quindi scarica periodicamente questo xml, controlla gli errori e salva tutte le informazioni in un enorme database.

Esistono molti XML salvati di questo tipo. Da questo database viene creato un indice di ricerca. L'indice è archiviato in formato interno. Dopo aver creato l'indice, il servizio Layout lo carica sui server di ricerca.

Di conseguenza, nel database viene visualizzato un gatto arrabbiato che strilla e l'indice del gatto viene visualizzato sul server.

Ti dirò come cerchiamo un gatto nella parte relativa all'architettura di ricerca.

Architettura della ricerca di mercato

Viviamo in un mondo di microservizi: ogni richiesta in arrivo mercato.yandex.ru provoca molte sottoquery e dozzine di servizi sono coinvolti nella loro elaborazione. Il diagramma ne mostra solo alcuni:

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce
Schema di elaborazione delle richieste semplificato

Ogni servizio ha una cosa meravigliosa: il proprio bilanciatore con un nome univoco:

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

Il bilanciatore ci offre una maggiore flessibilità nella gestione del servizio: è possibile, ad esempio, disattivare i server, cosa spesso necessaria per gli aggiornamenti. Il bilanciatore vede che il server non è disponibile e reindirizza automaticamente le richieste ad altri server o data center. Quando si aggiunge o si rimuove un server, il carico viene ridistribuito automaticamente tra i server.

Il nome univoco del bilanciatore non dipende dal data center. Quando il servizio A effettua una richiesta a B, per impostazione predefinita il bilanciatore B reindirizza la richiesta al data center corrente. Se il servizio non è disponibile o non esiste nel data center corrente, la richiesta viene reindirizzata ad altri data center.

Un unico FQDN per tutti i data center consente al servizio A di astrarre completamente dalle posizioni. La sua richiesta al servizio B verrà sempre evasa. L'eccezione è il caso in cui il servizio si trova in tutti i data center.

Ma non tutto è così roseo con questo bilanciatore: abbiamo un componente intermedio aggiuntivo. Il bilanciatore potrebbe essere instabile e questo problema viene risolto da server ridondanti. C'è anche un ulteriore ritardo tra i servizi A e B. Ma in pratica è inferiore a 1 ms e per la maggior parte dei servizi questo non è critico.

Affrontare l'imprevisto: bilanciamento e resilienza dei servizi di ricerca

Immagina che ci sia un collasso: devi trovare un gatto che cigola, ma il server si blocca. O 100 server. Come uscire? Lasceremo davvero l'utente senza gatto?

La situazione è spaventosa, ma siamo pronti. Te lo dirò in ordine.

L'infrastruttura di ricerca si trova in diversi data center:

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

Durante la progettazione includiamo la possibilità di chiudere un data center. La vita è piena di sorprese: ad esempio, un escavatore può tagliare un cavo sotterraneo (sì, è successo). La capacità dei restanti data center dovrebbe essere sufficiente a sopportare i picchi di carico.

Consideriamo un singolo data center. Ogni data center ha lo stesso schema operativo del bilanciatore:

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce
Un bilanciatore è composto da almeno tre server fisici. Questa ridondanza è fatta per l'affidabilità. I bilanciatori funzionano su HAProx.

Abbiamo scelto HAProx per le sue elevate prestazioni, i bassi requisiti di risorse e l'ampia funzionalità. Il nostro software di ricerca viene eseguito all'interno di ciascun server.

La probabilità che un server si guasti è bassa. Ma se disponi di molti server, aumenta la probabilità che almeno uno non funzioni.

Questo è ciò che accade nella realtà: i server si bloccano. Pertanto è necessario monitorare costantemente lo stato di tutti i server. Se il server smette di rispondere, viene automaticamente disconnesso dal traffico. A questo scopo, HAProxy dispone di un controllo dello stato integrato. Va a tutti i server una volta al secondo con una richiesta HTTP “/ping”.

Un'altra caratteristica di HAProxy: il controllo dell'agente consente di caricare tutti i server in modo uniforme. Per fare ciò, HAProxy si connette a tutti i server e restituiscono il loro peso in base al carico corrente da 1 a 100. Il peso viene calcolato in base al numero di richieste in coda per l'elaborazione e al carico sul processore.

Ora parliamo di trovare il gatto. I risultati della ricerca sono richieste come: /search?text=arrabbiato+gatto. Affinché la ricerca sia veloce, l'intero indice cat deve essere contenuto nella RAM. Anche la lettura dall'SSD non è abbastanza veloce.

Una volta il database delle offerte era piccolo e la RAM di un server era sufficiente. Man mano che la base dell'offerta cresceva, tutto non rientrava più in questa RAM e i dati venivano divisi in due parti: frammento 1 e frammento 2.

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce
Ma questo accade sempre: qualsiasi soluzione, anche buona, fa sorgere altri problemi.

Il bilanciatore è comunque andato su qualsiasi server. Ma sulla macchina da cui è arrivata la richiesta, c'era solo metà dell'indice. Il resto era su altri server. Pertanto, il server doveva andare su qualche macchina vicina. Dopo aver ricevuto i dati da entrambi i server, i risultati sono stati combinati e riclassificati.

Poiché il bilanciatore distribuisce le richieste in modo uniforme, tutti i server sono stati impegnati nella riclassificazione e non solo nell'invio di dati.

Il problema si verificava se un server vicino non era disponibile. La soluzione è stata quella di specificare diversi server con priorità diverse come server “vicini”. Innanzitutto la richiesta è stata inviata ai server nel rack attuale. Se non c'era risposta, la richiesta veniva inviata a tutti i server di questo data center. E infine la richiesta è arrivata ad altri data center.
Man mano che il numero delle proposte cresceva, i dati sono stati divisi in quattro parti. Ma questo non era il limite.

Attualmente viene utilizzata una configurazione di otto frammenti. Inoltre, per risparmiare ancora più memoria, l'indice è stato diviso in una parte di ricerca (che viene utilizzata per la ricerca) e una parte di snippet (che non è coinvolta nella ricerca).

Un server contiene informazioni per un solo frammento. Pertanto, per effettuare la ricerca nell'indice completo, è necessario effettuare la ricerca su otto server contenenti frammenti diversi.

I server sono raggruppati in cluster. Ogni cluster contiene otto motori di ricerca e un server di snippet.

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce
Il server dello snippet esegue un database di valori-chiave con dati statici. Sono necessari per rilasciare documenti, ad esempio la descrizione di un gatto che cigola. I dati vengono trasferiti appositamente su un server separato in modo da non caricare la memoria dei server di ricerca.

Poiché gli ID dei documenti sono univoci solo all'interno di un indice, potrebbe verificarsi una situazione in cui non sono presenti documenti negli snippet. Bene, o che per un ID ci saranno contenuti diversi. Pertanto, affinché la ricerca funzionasse e i risultati venissero restituiti, era necessaria coerenza nell’intero cluster. Di seguito ti spiegherò come monitoriamo la coerenza.

La ricerca stessa è strutturata come segue: una richiesta di ricerca può arrivare a uno qualsiasi degli otto server. Diciamo che è arrivato al server 1. Questo server elabora tutti gli argomenti e capisce cosa e come cercare. A seconda della richiesta in arrivo, il server può effettuare ulteriori richieste a servizi esterni per le informazioni necessarie. Una richiesta può essere seguita da un massimo di dieci richieste a servizi esterni.

Dopo aver raccolto le informazioni necessarie, inizia una ricerca nel database delle offerte. Per fare ciò, vengono effettuate delle sottoquery a tutti gli otto server del cluster.

Una volta ricevute le risposte, i risultati vengono combinati. Alla fine, potrebbero essere necessarie molte altre sottoquery al server dello snippet per generare i risultati.

Le query di ricerca all'interno del cluster hanno il seguente aspetto: /shard1?text=arrabbiato+cat. Inoltre, vengono costantemente effettuate sottoquery del modulo tra tutti i server all'interno del cluster una volta al secondo: /stato.

Richiesta /stato rileva una situazione in cui il server non è disponibile.

Controlla inoltre che la versione del motore di ricerca e la versione dell'indice siano le stesse su tutti i server, altrimenti ci saranno dati incoerenti all'interno del cluster.

Nonostante il fatto che un server di snippet elabori le richieste da otto motori di ricerca, il suo processore viene caricato molto poco. Pertanto, ora stiamo trasferendo i dati dello snippet a un servizio separato.

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

Per trasferire i dati abbiamo introdotto chiavi universali per i documenti. Ora è impossibile che il contenuto di un altro documento venga restituito utilizzando una chiave.

Ma la transizione verso un’altra architettura non è ancora completa. Ora vogliamo sbarazzarci del server snippet dedicato. E poi allontanarsi del tutto dalla struttura a grappolo. Ciò ci consentirà di continuare a crescere facilmente. Un ulteriore vantaggio è il notevole risparmio di ferro.

E ora passiamo alle storie spaventose a lieto fine. Consideriamo diversi casi di indisponibilità del server.

È successo qualcosa di terribile: un server non è disponibile

Diciamo che un server non è disponibile. Quindi i restanti server nel cluster potranno continuare a rispondere, ma i risultati della ricerca saranno incompleti.

Tramite controllo dello stato /stato i server vicini capiscono che uno non è disponibile. Pertanto, per mantenere la completezza, tutti i server del cluster per richiesta /ping iniziano a rispondere al bilanciatore che anche loro non sono disponibili. Si scopre che tutti i server nel cluster sono morti (il che non è vero). Questo è il principale svantaggio del nostro schema di cluster: ecco perché vogliamo abbandonarlo.

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

Le richieste che falliscono con un errore vengono reinviate dal bilanciatore su altri server.
Il bilanciatore inoltre smette di inviare il traffico degli utenti ai server inattivi, ma continua a controllarne lo stato.

Quando il server diventa disponibile, inizia a rispondere /ping. Non appena iniziano ad arrivare le normali risposte ai ping dai server morti, i bilanciatori iniziano a inviare lì il traffico degli utenti. Il funzionamento del cluster è ripristinato, evviva.

Ancora peggio: molti server non sono disponibili

Una parte significativa dei server nel data center viene tagliata. Cosa fare, dove correre? Il bilanciatore viene di nuovo in soccorso. Ogni bilanciatore memorizza costantemente in memoria il numero attuale di server live. Calcola costantemente la quantità massima di traffico che l'attuale data center può elaborare.

Quando molti server in un data center non funzionano, il sistema di bilanciamento si rende conto che questo data center non è in grado di elaborare tutto il traffico.

Quindi il traffico in eccesso inizia a essere distribuito in modo casuale ad altri data center. Tutto funziona, tutti sono felici.

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

Come lo facciamo: pubblicare comunicati

Ora parliamo di come pubblichiamo le modifiche apportate al servizio. Qui abbiamo intrapreso la strada della semplificazione dei processi: il lancio di una nuova release è quasi completamente automatizzato.
Quando nel progetto si accumula un certo numero di modifiche, viene creata automaticamente una nuova versione e viene avviata la sua compilazione.

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

Quindi il servizio viene sottoposto a test, dove viene verificata la stabilità del funzionamento.

Allo stesso tempo, viene avviato il test automatico delle prestazioni. Questo è gestito da un servizio speciale. Non ne parlerò adesso: la sua descrizione merita un articolo a parte.

Se la pubblicazione in testing ha esito positivo, viene avviata automaticamente la pubblicazione della release in pretable. Prestable è un cluster speciale in cui viene diretto il normale traffico degli utenti. Se restituisce un errore, il bilanciatore effettua una nuova richiesta alla produzione.

In prestabilito, i tempi di risposta vengono misurati e confrontati con la versione precedente in produzione. Se tutto va bene, una persona si connette: controlla i grafici e i risultati dei test di carico e poi inizia a passare alla produzione.

Tutto il meglio va all'utente: test A/B

Non è sempre ovvio se le modifiche a un servizio porteranno benefici reali. Per misurare l’utilità dei cambiamenti, le persone hanno ideato dei test A/B. Ti dirò un po 'come funziona nella ricerca Yandex.Market.

Tutto inizia con l'aggiunta di un nuovo parametro CGI che abilita nuove funzionalità. Sia il nostro parametro: mercato_nuova_funzionalità=1. Poi nel codice abilitiamo questa funzionalità se il flag è presente:

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

La nuova funzionalità è in fase di implementazione in produzione.

Per automatizzare i test A/B, esiste un servizio dedicato che fornisce informazioni dettagliate descritto qui. Viene creato un esperimento nel servizio. La quota di traffico è fissata, ad esempio, al 15%. Le percentuali non sono impostate per le query, ma per gli utenti. Viene indicata anche la durata dell'esperimento, ad esempio una settimana.

È possibile eseguire più esperimenti contemporaneamente. Nelle impostazioni è possibile specificare se è possibile l'intersezione con altri esperimenti.

Di conseguenza, il servizio aggiunge automaticamente un argomento mercato_nuova_funzionalità=1 al 15% degli utenti. Inoltre calcola automaticamente le metriche selezionate. Una volta completato l’esperimento, gli analisti esaminano i risultati e traggono conclusioni. Sulla base dei risultati, viene presa la decisione di passare alla produzione o al perfezionamento.

L'abile mano del mercato: i test in produzione

Accade spesso che sia necessario testare il funzionamento di una nuova funzionalità in produzione, ma non si è sicuri di come si comporterà in condizioni di “combattimento” sotto carico pesante.

Esiste una soluzione: i flag nei parametri CGI possono essere utilizzati non solo per test A/B, ma anche per testare nuove funzionalità.

Abbiamo realizzato uno strumento che ti consente di modificare istantaneamente la configurazione su migliaia di server senza esporre a rischi il servizio. Si chiama "Stop Tap". L'idea originale era quella di poter disabilitare rapidamente alcune funzionalità senza un layout. Poi lo strumento si è ampliato ed è diventato più complesso.

Di seguito viene presentato il diagramma di flusso del servizio:

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

I valori dei flag vengono impostati tramite l'API. Il servizio di gestione memorizza questi valori nel database. Tutti i server vanno al database una volta ogni dieci secondi, emettono valori di flag e applicano questi valori a ciascuna richiesta.

Nel tap Stop è possibile impostare due tipologie di valori:

1) Espressioni condizionali. Applicare quando uno dei valori è vero. Per esempio:

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

Il valore "3" verrà applicato quando la richiesta verrà elaborata nella posizione DC1. E il valore è "4" quando la richiesta viene elaborata sul secondo cluster per il sito beru.ru.

2) Valori incondizionati. Applica per impostazione predefinita se nessuna delle condizioni è soddisfatta. Per esempio:

valore, valore!

Se un valore termina con un punto esclamativo, gli viene assegnata una priorità più alta.

Il parser dei parametri CGI analizza l'URL. Quindi applica i valori dallo Stop Tap.

Vengono applicati valori con le seguenti priorità:

  1. Con maggiore priorità dallo Stop Tap (punto esclamativo).
  2. Valore da richiesta.
  3. Valore predefinito da Interrompi tocco.
  4. Valore predefinito nel codice.

Esistono molti flag indicati in valori condizionali: sono sufficienti per tutti gli scenari a noi noti:

  • Banca dati.
  • Ambiente: produzione, test, ombra.
  • Luogo: mercato, beru.
  • Numero di cluster.

Con questo strumento è possibile abilitare nuove funzionalità su un determinato gruppo di server (ad esempio in un solo data center) e testare il funzionamento di tale funzionalità senza particolari rischi per l'intero servizio. Anche se hai commesso un grave errore da qualche parte, tutto ha iniziato a crollare e l'intero data center è andato in tilt, i bilanciatori reindirizzeranno le richieste ad altri data center. Gli utenti finali non noteranno nulla.

Se noti un problema, puoi riportare immediatamente il flag al suo valore precedente e le modifiche verranno annullate.

Questo servizio ha anche i suoi svantaggi: gli sviluppatori lo adorano moltissimo e spesso cercano di inserire tutte le modifiche nello Stop Tap. Stiamo cercando di combattere gli abusi.

L'approccio Stop Tap funziona bene quando si dispone già di un codice stabile pronto per essere distribuito in produzione. Allo stesso tempo, hai ancora dei dubbi e vuoi controllare il codice in condizioni di "combattimento".

Tuttavia, Stop Tap non è adatto per i test durante lo sviluppo. Esiste un cluster separato per gli sviluppatori chiamato “cluster ombra”.

Test segreti: Ammasso d'Ombra

Le richieste provenienti da uno dei cluster vengono duplicate nel cluster shadow. Ma il bilanciatore ignora completamente le risposte di questo cluster. Lo schema del suo funzionamento è presentato di seguito.

Come funziona la ricerca Yandex.Market e cosa succede se uno dei server fallisce

Otteniamo un cluster di prova che si trova in reali condizioni di "combattimento". Il traffico utente normale va lì. L'hardware in entrambi i cluster è lo stesso, quindi è possibile confrontare prestazioni ed errori.

E poiché il sistema di bilanciamento ignora completamente le risposte, gli utenti finali non vedranno le risposte dal cluster ombra. Pertanto, non è spaventoso commettere un errore.

risultati

Quindi, come abbiamo costruito la ricerca di mercato?

Per far sì che tutto vada liscio, separiamo le funzionalità in servizi separati. In questo modo possiamo ridimensionare solo i componenti di cui abbiamo bisogno e semplificarli. È facile assegnare un componente separato a un altro team e condividere le responsabilità di lavorarci. E un notevole risparmio di ferro con questo approccio è un vantaggio ovvio.

Anche lo shadow cluster ci aiuta: possiamo sviluppare servizi, testarli nel processo e non disturbare l'utente.

Beh, test in produzione, ovviamente. Hai bisogno di modificare la configurazione su migliaia di server? Facile, usa lo Stop Tap. In questo modo puoi implementare immediatamente una soluzione complessa già pronta e tornare a una versione stabile in caso di problemi.

Spero di essere riuscito a mostrare come rendiamo il mercato veloce e stabile con una base di offerte in continua crescita. Come risolviamo i problemi del server, gestiamo un numero enorme di richieste, miglioriamo la flessibilità del servizio e lo facciamo senza interrompere i processi lavorativi.

Fonte: habr.com

Aggiungi un commento