2013-03-15 25 views
7

dubito che questo è ancora possibile, ma qui è il problema e soluzione proposta (la fattibilità della soluzione proposta è l'oggetto di questa domanda):


Ho alcuni "dati globali" che devono essere disponibili per tutte le richieste. Sto persistendo questi dati su Riak e usando Redis come livello di cache per la velocità di accesso (per ora ...). I dati sono suddivisi in circa 30 blocchi logici, ciascuno di circa 8 KB.persistente oggetto Python in memoria per il server nginx/uwsgi

Ogni richiesta è richiesta per leggere 4 di questi blocchi da 8 KB, con conseguente 32 KB di dati letti da Redis o Riak. Questo è in AGGIUNTA a qualsiasi dato specifico della richiesta che dovrebbe anche essere letto (che è piuttosto un po ').

Supponendo anche 3000 richieste al secondo (questo non è un server live, quindi non ho numeri reali, ma 3000ps è un'ipotesi ragionevole, potrebbe essere più), questo significa 96KBps di trasferimento da Redis o Riak in ADDITION alle altre già non insignificanti chiamate effettuate dalla logica dell'applicazione. Inoltre, Python sta analizzando il JSON di questi oggetti da 8 KB 3000 volte al secondo.


Tutto questo - soprattutto Python dover ripetutamente deserializzare i dati - sembra come uno spreco assoluto, e una soluzione perfettamente elegante sarebbe di appena hanno i dati deserializzate nella cache in un oggetto nativo in memoria in Python, che posso aggiornare periodicamente come e quando tutti questi dati "statici" diventano obsoleti. Una volta in pochi minuti (o ore), anziché 3000 volte al secondo.

Ma non so se questo è possibile. Avresti realisticamente bisogno di un'applicazione "sempre in esecuzione" per memorizzare nella cache tutti i dati in essa contenuti. E so che questo non è il caso della combinazione nginx + uwsgi + python (rispetto a qualcosa di simile al nodo) - i dati in memoria di python NON verranno persi per tutte le richieste a mia conoscenza, a meno che non mi sbagli terribilmente.

Sfortunatamente questo è un sistema che ho "ereditato" e quindi non posso apportare troppe modifiche in termini di tecnologia di base, né sono sufficientemente informato su come funziona la combinazione nginx + uwsgi + python in termini di avvio Processi Python e dati persistenti in memoria di Python - il che significa che potrei essere terribilmente in errore con la mia ipotesi sopra!


Quindi, consulenza diretta dal fatto che questa soluzione avrebbe funzionato + riferimenti a materiale che possa aiutarmi a capire come il nginx + uwsgi + pitone avrebbe funzionato in termini di iniziare nuovi processi e allocazione di memoria, sarebbe di grande aiuto .

P.S:

  1. sono passati attraverso alcune della documentazione per nginx, uwsgi ecc ma non hanno compreso appieno le ramificazioni per il mio caso d'uso ancora. Spero di fare qualche progresso su quello che sta andando avanti ora

  2. Se la cosa in memoria POTREBBE risolvere, vorrei buttare Redis, dato che sto memorizzando SOLO i dati statici che ho citato sopra. Ciò rende la cache Python persistente in-process in-memory ancora più attraente per me, riducendo una parte in movimento nel sistema e almeno quattro round-trip di rete per richiesta.

+0

Che aspetto hanno questi dati? Quanto è rigida la struttura? Quanto spesso cambia? – abarnert

+0

Insieme enorme di coppie chiave-valore con alcune operazioni di nidificazione. Struttura campione (espressa come JSON): {chiave1: valore1, chiave2: {sottochiave1: [valore1, valore2, valore3], sottochiave2: valore2}, chiave3: [valore1, valore2, valore3]}. Quindi una quantità ragionevole di varietà nei dati, ma come ho detto è abbastanza statica e posso sopportare con l'aggiornamento solo ogni pochi minuti o anche ogni 1 ora. –

risposta

3

Quello che stai suggerendo non è direttamente fattibile. Poiché i nuovi processi possono essere fatti girare su e giù al di fuori del tuo controllo, non c'è modo di mantenere i dati nativi di Python in memoria.

Tuttavia, ci sono alcuni modi per aggirare questo.

Spesso, un livello di archiviazione a valori-chiave è tutto ciò che serve. E a volte, avere buffer di dimensioni fisse per valori (che è possibile utilizzare direttamente come oggetti str/bytearray, tutto ciò che è necessario per struct in là o in altro modo serializzare) è tutto ciò che serve. In tal caso, lo caching framework integrato di uWSGI si prenderà cura di tutto ciò di cui hai bisogno.

Se è necessario un controllo più preciso, è possibile osservare come la cache è implementata in cima a SharedArea e fare qualcosa di personalizzato. Tuttavia, non lo consiglierei. Fondamentalmente ti dà lo stesso tipo di API che ottieni con un file, e gli unici veri vantaggi rispetto all'uso di un file sono che il server gestirà la vita del file; funziona in tutte le lingue supportate da uWSGI, anche quelle che non consentono i file; e rende più facile la migrazione della cache personalizzata in una cache distribuita (multi-computer) se in futuro è necessario. Non penso che nessuno di quelli sia rilevante per te.

Un altro modo per ottenere un'archiviazione di valore chiave piatta, ma senza i buffer a dimensione fissa, è con lo stdlib di Python anydbm. La ricerca del valore-chiave è altrettanto pignola: assomiglia a un dict, tranne che viene eseguito il backup su un database BDB (o simile) su disco, memorizzato nella cache come appropriato in memoria, invece di essere memorizzato in un tabella hash della memoria.

Se è necessario gestire alcuni altri tipi semplici - tutto ciò che è incredibilmente veloce per un/pickle, come int s-si può prendere in considerazione shelve.

se la struttura è abbastanza rigido, è possibile utilizzare il database chiave-valore per il livello superiore, ma l'accesso ai valori attraverso un ctypes.Structure, o DE/serializzare con struct. Ma di solito, se riesci a farlo, puoi anche eliminare il livello più alto, a quel punto la tua intera faccenda è solo una grande Structure o Array.

A quel punto, si può semplicemente utilizzare un file normale per la conservazione a entrambi i mmap esso (per ctypes), o semplicemente open e read esso (per struct).

Oppure utilizzare multiprocessing's Shared ctypes Objects per accedere direttamente a Structure da un'area di memoria condivisa.

Nel frattempo, se non si ha realmente bisogno di tutti i dati della cache tutto il tempo, solo pezzi ogni volta, questo è esattamente ciò che i database servono. Ancora una volta, anydbm, ecc. Possono essere tutto ciò che serve, ma se hai una struttura complessa, disegna un diagramma ER, trasformalo in un set di tabelle e usa qualcosa come MySQL.

+0

Con dati misti (float e stringhe) alcune ricerche generali per benchmark hanno mostrato che simplejson è più veloce di cpickle. Questo è il motivo per cui stavo parlando dell'esempio JSON, e anche "accantonare" ecc. Al momento sembrano fuori dal campo di applicazione. E leggere i file in realtà è più veloce di colpire Redis - ad esempio su localhost? Ovviamente, considerando che stiamo parlando di circa 31 KB di dati statici, sembra che colpirebbe la cache del disco ancora e ancora piuttosto che colpire il mandrino. "Il caching di uwsgi" sembra DAVVERO interessante, e quasi perfetto, a meno che non incappassi in qualche problema. –

+0

Suppongo che la cache uwsgi sia accessibile "in-process" da Python e sia naturalmente più-x più veloce di Redis. Sembra giusto, ma solo confermando che la mia comprensione delle regole di base qui è corretta. –

+0

A 32KB, la differenza tra la memoria condivisa, un file 'mmap'ed o' leggere un file da "disco" (come dici tu, in realtà la cache del disco del sistema operativo), ecc. Probabilmente non avrà molta importanza.L'invio su un socket TCP localhost non dovrebbe aggiungere molto. (Ma ovviamente, test.) Per quanto riguarda il problema di JSON-vs-pickle, se hai solo un livello (un ditt pieno di float e stringhe), non memorizzerai dati misti; ogni valore è solo un semplice float o stringa. Ma tu no; hai qualche livello, quindi ... accantonare è fuori. – abarnert

0

ho mai effettivamente provato io stesso, ma si potrebbe forse usare uWSGI di SharedArea per compiere quello che stai cercando?

+0

Dovresti costruirne un bel po 'per renderlo utilizzabile. Fondamentalmente, questo ti dà lo stesso tipo di API che ottieni da un oggetto file; questo è tutto. Come dice "Avviso" nella parte superiore della pagina collegata: "SharedArea è un meccanismo di livello molto basso. Per un'alternativa più semplice da utilizzare, consulta i framework Caching e Queue". – abarnert

0

"I dati in memoria di Python NON verranno mantenuti per tutte le richieste a mia conoscenza, a meno che non mi sbagli terribilmente."

si sbaglia.

l'intero punto di utilizzo di uwsgi su, ad esempio, il meccanismo CGI è quello di mantenere i dati tra thread e salvare il sovraccarico di inizializzazione per ogni chiamata. deve impostare processes = 1 nel file .ini oppure, in base alla configurazione di uwsgi, potrebbe avviare più di 1 processo di lavoro per conto dell'utente. registrare env e cercare 'wsgi.multiprocess': False e 'wsgi.multithread': True e tutti i thread uwsgi.core per il singolo operatore dovrebbero mostrare gli stessi dati.

è anche possibile vedere quanti processi di lavoro e thread "core" sotto ciascuno, si utilizza il built-in stats-server.

ecco perché uwsgi fornisce le funzioni lock e unlock per la manipolazione degli archivi di dati tramite più thread.

puoi facilmente testarlo aggiungendo una route /status nella tua app che scarica semplicemente una rappresentazione json del tuo oggetto dati globale e la visualizza ogni tanto dopo le azioni che aggiornano lo store.

1

Non hai detto nulla sulla stesura di questi dati, è statico? In questo caso, la soluzione è semplice, e non ho idea di cosa succede con tutte le risposte "non è fattibile".

Uwsgi worker sono applicazioni sempre attive. Quindi i dati vengono permanentemente conservati tra le richieste. Tutto quello che devi fare è immagazzinare cose in una variabile globale, è così. E ricorda che è per lavoratore, e gli operatori si riavviano di volta in volta, quindi hai bisogno di strategie di caricamento/invalidazione corrette.

Se i dati vengono aggiornati molto raramente (raramente per riavviare il server quando lo fa), è possibile risparmiare ancora di più. Basta creare gli oggetti durante la costruzione di app. In questo modo, verranno creati esattamente una volta, quindi tutti i lavoratori sborseranno il master e riutilizzeranno gli stessi dati. Ovviamente, è copy-on-write, quindi se lo aggiorni, perderai i benefici della memoria (la stessa cosa succederà se Python decide di compattare la sua memoria durante l'esecuzione di gc, quindi non è molto prevedibile).

+1

Whoa, necropost. Non ho notato la data prima. Mi chiedo perché SO ha deciso di mostrarmi questo post? – letitbee

Problemi correlati