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

Nicola Larosa nico a tekNico.net
Sab 15 Mar 2014 07:42:28 CET


Manlio Perillo wrote:
> Come vedi diventa molto complicato, specialmente se ad esempio devi
> fare il parsing di un protocollo come HTTP (puoi vedere il codice di
> Twisted se ti interessa).

Twisted non si limita alle callback lisce ma ne gestisce esplicitamente i
flusso (con i Deferred), e supporta anche la sintassi pseudo sincrona,
stile coroutine, tramite le inline callbacks (opzioni 1, 2 e 3 sotto).

Lo stesso fa Tornado.


> Tra l'altro il motivo perchè tornado va di moda è perchè permette di
> avere il codice che è praticamente lo stesso di quello "normale", ma
> che si comporta in modo completamente diverso.

Cioè la sintassi pseudo sincrona. Tornado supporta anche la sintassi a
callback "gestita" (tramite i Future).


> Twisted offre un framework per la programmazione asincrona da anni,
> ma non è mai stato di moda, perchè molto più difficile.

Non ha avuto grande successo per vari altri motivi: avanti sui suoi
tempi, documentazione carente, e anche insistenza sulla sintassi a
callback preferita alle inline callback.


> Considerato tutti i problemi che gli utenti hanno con tornado e
> friends (e che nemmeno sanno di sapere), direi che, come sempre,
> "explicit is better than implicit".

Poco ma sicuro. Creare punti impliciti di cambio di contesto, come fanno
gevent ed eventlet, e come fanno i thread preemptive, è ingestibile.


Un linguaggio recente come Go usa solo la sintassi pseudo sincrona,
tramite goroutine e channel. Però lo scheduler è preemptive e il runtime
fa un mapping N-to-M delle goroutine ai thread di sistema, permettendo di
usare in modo trasparente tutti i core di CPU disponibili.

Quest'approccio consente di mischiare in modo trasparente codice sincrono
e asincrono. Devo invocare un adapter db sincrono, cioè bloccante? Non
devo preoccuparmi di incapsulare manualmente la chiamata in un thread o
processo separato: il runtime si accorgerà che il thread assegnato è
bloccato, e sposterà automaticamente le altre goroutine assegnate a quel
thread su altri thread.

Essendo comunque un sistema preemptive a stato condiviso, rimane al
programmatore la responsabilità di non reintrodurre i problemi del
multithreading classico. Il linguaggio incoraggia l'approccio robusto, ma
non lo rende obbligato.

Approfondimenti:

The Go scheduler
<http://morsmachine.dk/go-scheduler>

Why is this Go code blocking?
<http://stackoverflow.com/questions/12413510/why-is-this-go-code-blocking>


> Il mio suggerimento è sempre quello di imparare prima le basi (vicino
> a quello che realmente succede) e solo dopo utilizzare cose che
> rendono la programmazione e manutenzione più semplice.

Se con questo intendi passare per la sintassi a callback prima di usare
la pseudo sincrona, non sono d'accordo. Non si tratta di basi, è
semplicemente un modo diverso, molto meno leggibile, di scrivere codice
asincrono.

Ormai le Promises ci sono anche in Javascript, non ha senso insistere col
vecchio modello. Però andare troppo oltre e rinunciare agli yield, che
marcano i punti di cambio di contesto, significa andarsela a cercare.

Glyph Lefkowitz, cioè Mr. Twisted, fa un'ottima panoramica delle opzioni
disponibili, ed illustra bene i pericoli di usare una sintassi implicita:

"1. Straight callbacks: Twisted’s IProtocol, JavaScript’s on<foo> idiom,
where you give a callback to something which will call it later and then
return control to something (usually a main loop) which will execute
those callbacks,

2. “Managed” callbacks, or Futures: Twisted’s Deferred, JavaScript’s
Promises/A[+], E’s Promises, where you create a dedicated
result-that-will-be-available-in-the-future object and return it for the
caller to add callbacks to,

3. Explicit coroutines: Twisted’s @inlineCallbacks, Tulip’s yield from
coroutines, C#’s async/await, where you have a syntactic feature that
explicitly suspends the current routine,

4. and finally, implicit coroutines: Java’s “green threads”, Twisted’s
Corotwine, eventlet, gevent, where any function may switch the entire
stack of the current thread of control by calling a function which
suspends it.

One of these things is not like the others; one of these things just
doesn’t belong."

Unyielding - Deciphering Glyph
<https://glyph.twistedmatrix.com/2014/02/unyielding.html>

L'opzione fuori posto è ovviamente la quarta.

-- 
Nicola Larosa - http://www.tekNico.net/

A plus sign is just a square with collapsed sides,
after passing through a hash sign:
     ◽ # +
(Where are my medications when I need them?)
  - Nicola Larosa, February 2014


Maggiori informazioni sulla lista Python