Pensieri di apertura
Come sei arrivato alla conclusione che alcune parti del sistema sarebbero andate meglio in un'altra lingua? Stai soffrendo problemi di prestazioni? Quanto sono gravi questi problemi? Se può essere più veloce, è essenziale che sia più veloce?
Asincronia a thread singolo
Esistono diverse domande e altre risorse Web che già trattano le differenze, i vantaggi e gli svantaggi dell'asincronia a thread singolo rispetto alla concorrenza multi-thread. È interessante leggere come si comporta il modello asincrono a thread singolo di Node.js quando l'I / O è il principale collo di bottiglia e ci sono molte richieste che vengono servite contemporaneamente.
Twisted, Tornado e altri modelli asincroni fanno un uso eccellente di un singolo thread. Poiché molta programmazione Web ha molti I / O (rete, database, ecc.), Il tempo trascorso in attesa di chiamate remote aumenta notevolmente. Questo è il tempo che potrebbe essere speso per fare altre cose, come dare il via ad altre chiamate al database, renderizzare pagine e generare dati. L'utilizzo di quel singolo thread è estremamente elevato.
Uno dei maggiori vantaggi dell'asincronia a thread singolo è che utilizza molta meno memoria. Nell'esecuzione multi-thread, ogni thread richiede una certa quantità di memoria riservata. All'aumentare del numero di thread, aumenta anche la quantità di memoria necessaria solo per l'esistenza dei thread. Poiché la memoria è limitata, significa che ci sono limiti al numero di thread che possono essere creati in qualsiasi momento.
Esempio
Nel caso di un server web, fai finta che a ogni richiesta venga assegnato il proprio thread. Supponiamo che siano necessari 1 MB di memoria per ogni thread e che il server Web abbia 2 GB di RAM. Questo server Web sarebbe in grado di elaborare (circa) 2000 richieste in qualsiasi momento prima che non ci sia più memoria sufficiente per elaborare più.
Se il tuo carico è significativamente superiore a questo, le richieste impiegheranno molto tempo (in attesa del completamento di richieste più vecchie) o dovrai lanciare più server nel cluster per espandere il numero di richieste simultanee possibili .
Concorrenza multi-thread
La concorrenza multi-thread si basa invece sull'esecuzione di più attività contemporaneamente. Ciò significa che se un thread viene bloccato in attesa di una chiamata al database per restituire, altre richieste possono essere elaborate contemporaneamente. L'utilizzo del thread è inferiore, ma il numero di thread in esecuzione è molto maggiore.
Il codice multi-thread è anche molto più difficile da ragionare. Ci sono problemi con il blocco, la sincronizzazione e altri divertenti problemi di concorrenza. L'asincronia a thread singolo non presenta gli stessi problemi.
Tuttavia, il codice multi-thread è molto più performante per le attività che richiedono molta CPU . Se non esiste alcuna possibilità per un thread di "cedere" - come una chiamata di rete che normalmente si bloccherebbe - un modello a thread singolo non avrà alcuna concorrenza.
Entrambi possono coesistere
Ovviamente c'è una sovrapposizione tra i due; Non si escludono a vicenda. Ad esempio, il codice multi-thread può essere scritto in modo non bloccante, per utilizzare meglio ogni thread.
La linea di fondo
Ci sono molti altri problemi da considerare, ma mi piace pensare ai due in questo modo:
- Se il tuo programma è associato all'I / O , probabilmente l'asincronia a thread singolo funzionerà abbastanza bene.
- Se il tuo programma è associato alla CPU , probabilmente sarà un sistema multi-thread.
Nel tuo caso particolare, devi determinare quale tipo di lavoro asincrono è stato completato e con quale frequenza si presentano tali compiti.
- Si verificano ad ogni richiesta? In tal caso, probabilmente la memoria diventerà un problema con l'aumentare del numero di richieste.
- Questi compiti sono ordinati? In tal caso, dovrai considerare la sincronizzazione se utilizzi più thread.
- Queste attività sono impegnative per la CPU? In tal caso, un thread singolo è in grado di tenere il passo con il carico?
Non c'è una risposta semplice. È necessario considerare quali sono i casi d'uso e progettare di conseguenza. A volte è meglio un modello asincrono a thread singolo. Altre volte, è necessario utilizzare una serie di thread per ottenere un'elaborazione parallela massiccia.
altre considerazioni
Ci sono anche altri problemi che devi considerare, piuttosto che solo il modello di concorrenza che scegli. Conosci Erlang o Clojure? Pensi di essere in grado di scrivere codice multi-thread sicuro in una di queste lingue in modo da migliorare le prestazioni della tua applicazione? Ci vorrà molto tempo per mettersi al passo con una di queste lingue e la lingua che imparerai ti gioverà in futuro?
E le difficoltà associate alla comunicazione tra questi due sistemi? Sarà eccessivamente complesso mantenere in parallelo due sistemi separati? In che modo il sistema Erlang riceverà attività da Django? In che modo Erlang comunicherà questi risultati a Django? Le prestazioni sono abbastanza significative un problema che vale la complessità aggiunta?
Pensieri finali
Ho sempre trovato Django abbastanza veloce, ed è utilizzato da alcuni siti molto trafficati. Esistono diverse ottimizzazioni delle prestazioni che è possibile apportare per aumentare il numero di richieste e tempi di risposta simultanei. Certo, finora non ho fatto nulla con Celery, quindi le consuete ottimizzazioni delle prestazioni probabilmente non risolveranno alcun problema che potresti avere con questi compiti asincroni.
Naturalmente, c'è sempre il suggerimento di lanciare più hardware al problema. Il costo del provisioning di un nuovo server è più economico del costo di sviluppo e manutenzione di un sottosistema completamente nuovo?
Ho fatto troppe domande a questo punto, ma questa era la mia intenzione. La risposta non sarà facile senza analisi e ulteriori dettagli. Essere in grado di analizzare i problemi si riduce alla conoscenza delle domande da porre, però ... quindi spero di aver aiutato su questo fronte.
Il mio istinto dice che non è necessaria una riscrittura in un'altra lingua. La complessità e il costo saranno probabilmente troppo grandi.
modificare
Risposta al follow-up
Il tuo follow-up presenta alcuni casi d'uso molto interessanti.
1. Django lavora al di fuori delle richieste HTTP
Il tuo primo esempio riguardava la lettura di tag NFC, quindi l'interrogazione del database. Non penso che scrivere questa parte in un'altra lingua ti sarà così utile, semplicemente perché interrogare il database o un server LDAP sarà vincolato dall'I / O di rete (e potenzialmente dalle prestazioni del database). D'altra parte, il numero di richieste simultanee sarà vincolato dal server stesso, poiché ciascun comando di gestione verrà eseguito come processo proprio. Ci saranno tempi di installazione e smontaggio che influiscono sulle prestazioni, poiché non si inviano messaggi a un processo già in esecuzione. Sarai comunque in grado di inviare più richieste contemporaneamente, poiché ognuna sarà una procedura isolata.
Per questo caso, vedo due strade su cui puoi indagare:
- Assicurarsi che il database sia in grado di gestire più query contemporaneamente con il pool di connessioni. (Oracle, ad esempio, richiede di configurare Django di conseguenza
'OPTIONS': {'threaded':True}
.) Potrebbero esserci opzioni di configurazione simili a livello di database o Django che è possibile modificare per il proprio database. Indipendentemente dalla lingua in cui scrivi le query del database, dovrai attendere la restituzione di questi dati prima di poter accendere i LED. Tuttavia, le prestazioni del codice di query possono fare la differenza e Django ORM non è velocissimo ( ma , di solito abbastanza veloce).
- Ridurre al minimo i tempi di installazione / smontaggio. Avere un processo costantemente in esecuzione e inviare messaggi ad esso. (Correggimi se sbaglio, ma questo è ciò su cui la tua domanda originale si sta effettivamente concentrando.) Se questo processo è scritto in Python / Django o in un'altra lingua / framework è trattato sopra. Non mi piace l'idea di utilizzare i comandi di gestione così frequentemente. È possibile avere un piccolo codice in esecuzione costantemente, che spinge i messaggi dei lettori NFC sulla coda dei messaggi, che Celery legge e inoltra a Django? L'installazione e lo smontaggio di un piccolo programma, anche se è scritto in Python (ma non in Django!), Dovrebbe essere migliore dell'avvio e dell'arresto di un programma Django (con tutti i suoi sottosistemi).
Non sono sicuro del server Web che stai utilizzando per Django. mod_wsgi
per Apache consente di configurare il numero di processi e thread all'interno dei processi richiesti dal servizio. Assicurati di modificare la configurazione pertinente del tuo server web per ottimizzare il numero di richieste gestibili.
2. "Passaggio di messaggi" con segnali Django
Anche il tuo secondo caso d'uso è abbastanza interessante; Non sono sicuro di avere le risposte per questo. Se stai eliminando gli esempi di modelli, e desiderio di operare su di essi in seguito, potrebbe essere possibile per serializzare loro JSON.dumps
e poi deserializzare JSON.loads
. In seguito sarà impossibile ricreare completamente il grafico a oggetti (interrogare modelli correlati), poiché i campi correlati sono caricati in modo pigro dal database e quel collegamento non esisterà più.
L'altra opzione sarebbe in qualche modo contrassegnare un oggetto per l'eliminazione e cancellarlo solo alla fine del ciclo richiesta / risposta (dopo che tutti i segnali sono stati revisionati). Potrebbe richiedere un segnale personalizzato per implementarlo, piuttosto che fare affidamento post_delete
.