2010-08-08 13 views
6

Lavoro sul comando manage.py che crea circa 200 thread per controllare gli host remoti. La configurazione del mio database mi consente di utilizzare 120 connessioni, quindi ho bisogno di utilizzare un qualche tipo di pool. Ho provato con filo separato, come questoUtilizzo di Django ORM nei thread ed evitando l'eccezione "too many clients" utilizzando BoundedSemaphore

class Pool(Thread): 
    def __init__(self): 
     Thread.__init__(self)   
     self.semaphore = threading.BoundedSemaphore(10) 

    def give(self, trackers): 
     self.semaphore.acquire() 
     data = ... some ORM (not lazy, query triggered here) ... 
     self.semaphore.release() 
     return data 

passo istanza di questo oggetto per ogni thread check-, ma ancora ottenere "OperationalError: FATAL: sorry, troppi clienti già" dentro oggetto Pool dopo init-ing 120 fili. Mi aspetto che verranno aperte solo 10 connessioni al database e che i thread attenderanno lo slot del semaforo gratuito. Posso controllare che il semaforo funzioni commentando "release()", in tal caso solo 10 thread funzioneranno e altri attenderanno fino alla terminazione dell'app.

Per quanto ho capito, ogni thread apre una nuova connessione al database anche se la chiamata effettiva è all'interno di thread diversi, ma perché? C'è un modo per eseguire tutte le query del database all'interno di un solo thread?

+0

Per trovare il problema del DB effettivo, suppongo che abbiamo bisogno di più codice sql. Ma, perché non raccogli l'intero thread in modo da eseguire solo circa 20-30 in quel momento? – KillianDS

+0

Ciao, il mio problema è che Django crea una connessione per ogni thread che tocchi qualsiasi codice ORM, anche se la vera manipolazione ORM avviene in thread diversi. (L'ho controllato rimuovendo tutto il codice da altri thread tranne che per la chiamata all'istanza di Pool give). Quindi nel mio caso non c'è SQL tranne che per ottenere dati in give. Finché ho il correttore di classe (Discussione): def run (self): self.getter.give (self.trackers) viene creata una connessione (i tracker sono una lista di stringhe qui). E dopo aver raggiunto il limite di 120 connessioni il mio database inizia a dare eccezioni. – Riz

+0

btw, sono stato in grado di risolvere questo problema in un modo - chiudendo manualmente la connessione dopo ogni richiesta db, le prestazioni sono accettabili, ma sono ancora curioso di conoscere questo problema. – Riz

risposta

13

L'ORM di Django gestisce le connessioni del database nelle variabili locali del thread. Quindi ogni thread diverso che accede all'ORM creerà la propria connessione. Lo puoi vedere nelle prime righe di django/db/backends/__init__.py.

Se si desidera limitare il numero di connessioni database effettuate, è necessario limitare il numero di thread diversi che effettivamente accedono all'ORM. Una soluzione potrebbe essere quella di implementare un servizio che deleghi le richieste ORM a un pool di thread ORM dedicati. Per trasmettere le richieste e i loro risultati da e ad altri thread dovrai implementare una sorta di meccanismo di trasmissione dei messaggi. Dato che questo è un tipico problema produttore/consumatore, i documenti Python sulla thread dovrebbero fornire alcuni suggerimenti su come ottenerlo.

Modifica: Ho cercato su google "pool di connessioni django". Ci sono molte persone che si lamentano del fatto che Django non fornisce un vero e proprio pool di connessioni. Alcuni di questi sono riusciti a integrare un pacchetto di pool separato. Per PostgreSQL, darei un'occhiata al middleware pgpool.

+0

Sono stato in grado di risolvere questo problema in due modi, utilizzando pgpool2 o semaforo con la chiamata manuale connection.close() in entrambi i casi. Il pool di thread ORM è quello che ho provato per primo, ma per qualche motivo Django crea una nuova connessione per ogni thread anche se non ci sono ORM effettivi, non appena si tenta di accedere a qualsiasi thread diverso con ORM - ecco che arriva una nuova connessione. P.S. pgpool sembrava una buona soluzione, ma non ero in grado di risolvere questo problema usando solo esso, perché ho una app longrunning (non molti piccoli) e pgpool non è stato in grado di fornire nuove connessioni in pool in quanto erano tutti utilizzati dalla mia app – Riz

+0

domanda è: in quale contesto di thread chiamate Pool.give()? Se invochi un metodo di un oggetto che eredita da Thread, quel metodo viene comunque eseguito dal thread chiamante, non necessariamente dal thread associato all'oggetto.Se interpreto correttamente il tuo commento precedente sopra, i thread chiamanti sono i tuoi thread di controllo. Ciò significa che tutti i 200 thread di controllo tentano di creare una connessione DB privata (poiché la connessione è archiviata in locale). I tuoi semafori limitano solo il numero di accessi ORM paralleli, non il numero di connessioni effettuate. –

+0

A partire da 1,6, possiamo avere connessioni persistenti in Django, dare un'occhiata a https://docs.djangoproject.com/en/1.8/ref/settings/#conn-max-age – dotz

Problemi correlati