[Python] Non blocking http server e integrazione con database relazionali

Giampaolo Rodola' g.rodola a gmail.com
Sab 15 Mar 2014 01:48:05 CET


2014-03-13 19:35 GMT+01:00 Balan Victor <balan.victor0 a gmail.com>:

> Di recente ho letto un po di tornado, e in particolare mi sono soffermato
> sul modulo tornado.httpserver(Non-blocking HTTP server). Stando a quello
> che c'è scritto sulla documentazione ufficiale parla di "non-blocking,
> single-threaded HTTP server" e di risolvere il problemi di tipo C10K. Qua
> sembra interessante, anche se non ho la minima idea di come funzioni. Sono
> rimasto perplesso quando ho provato a cercare qualche ORM da usare con
> tornado e non ho trovato nulla. Dopo un po di ricerche, da quello che ho
> capito, un orm non è fatto per lavorare in maniera asincrona. E non ho
> neppure trovato una libreria per collegarsi a qualche tipo di database
> relazione(a parte quella con MySql ma sembra non più supportata).
>
> Detto questo, non riesco a capire l'utilità di un HTTP Server con
> performance elevatissime ma che non permetta una minima interazione con il
> database.
>
> Probabilmente sopra ho scritto delle cavolate ma mi mancano completamente
> le basi per questo tipo di argomenti e volevo capire meglio come funzionano
> e quali sono i campi di applicazione di tecnologie simili.
>
> _______________________________________________
> Python mailing list
> Python a lists.python.it
> http://lists.python.it/mailman/listinfo/python
>
>
Hai intravisto giusto a la domanda non è per niente stupida.
I server asincroni performano e scalano enormemente meglio delle
controparti basate su processi/thread multipli (un esempio lampante è
pyftpdlib: https://code.google.com/p/pyftpdlib/wiki/Benchmarks - si guardi
sopratutto l'utilizzo di memoria) a patto che, appunto, tu "non blocchi".

Quando arrivi al punto in cui sei costretto a bloccare (es: non hai uno
strato apposito che interagisce col db in maniera asincrona) hai una sola
possibilità: usare un thread (o un sottoprocesso).
Fare questo significa essenzialmente ripassare la palla all'IO loop e
quando il thread ha terminato prendere il risultato della funzione e
processarlo.

Fare questo a mano in maniera "sana" non è semplice, ed è infatti per
questo che solitamente i framework ti mettono a disposizione interfacce
apposite.
Nel caso specifico di Tulip/asyncio il codice dovrebbe essere una cosa di
questo tipo:

from tulip import tasks, coroutine

@coroutine
def foo():
        fut = self.run_in_executor(long_running_db_call)
        yield from tasks.wait(fut)
        ret = fut.result()

La magia nel codice sopra sta nello "yield from" che letteralmente si legge
come "lancia  long_running_db_call in un thread, vai a fare altro e ritorna
qui quando il thread è terminato".

Con Twisted l'approccio è diverso in quanto anzichè le coroutines utilizzi
le callback ma il concetto che sta alla base è il medesimo (lancia il
thread, vai a fare altro e processa il risultato del thread quando ha
terminato):
https://twistedmatrix.com/documents/12.2.0/core/howto/threading.html

Con Tornado casualmente non ho mai avuto a che fare con parti "bloccanti"
ma dando un'occhiata qui pare sia possibile in maniera analoga a
tulip/asyncio:
http://www.tornadoweb.org/en/stable/concurrent.html

L'unico caso in cui non sceglierei l'asincrono è dove TUTTE le tue
richieste sono bloccanti per un motivo o per un altro, nel qual caso
avresti prestazioni/scalabilità uguali o minori (più probabile) rispetto a
soluzioni nate espressamente per  utilizzare il modello di concorrenza
"classico" (thread / multi processi, esempio classico: django + gunicorn).

-- 
Giampaolo - http://grodola.blogspot.com
-------------- parte successiva --------------
Un allegato HTML è stato rimosso...
URL: <http://lists.python.it/pipermail/python/attachments/20140315/8be57192/attachment-0001.html>


Maggiori informazioni sulla lista Python