[Python] web: sync vs. async

Roberto De Ioris roberto a unbit.it
Mar 6 Dic 2011 19:43:12 CET


> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Il 06/12/2011 18:34, Giampaolo Rodolà ha scritto:
>> Il 03 dicembre 2011 16:23, Manlio Perillo <manlio.perillo a gmail.com> ha
>> scritto:
>>>> Una cosa di questo tipo non avrebbe ugualmente funzionato?
>>>>
>>>> # pseudo codice
>>>> import multiprocessing
>>>> from somehttpd import HTTPServer
>>>>
>>>> CPUS = multiprocessing.cpu_count()
>>>> server = HTTPServer()
>>>> # create child processes to act as workers
>>>> for x in range(CPUS - 1):
>>>>     Process(target=server.serve_forever).start()
>>>> # main process also acts as a worker
>>>> server.serve_forever()
>>>>
>>>>
>>>> Se si, ti saresti evitato l'onere di ascoltare su porte multiple e
>>>> ovviamente tutta la complessità aggiuntiva che ne deriva.
>>>>
>>>
>>> Questo è essenzialmente l'approccio usato da Nginx.
>>>
>>> Però c'è un dettaglio subdolo: con questo metodo ciascun processo
>>> figlio
>>> chiama la accept sullo stesso file descriptor ereditato dal padre e ci
>>> potrebbero essere dei problemi subdoli.
>>>
>>> Ad esempio il problema chiamato "thundering herd" oppure (ma su questo
>>> non riesco a trovare dei riferimenti, l'ho letto dall'autore di Nginx)
>>> il sistema operativo potrebbe **non** distribuire il carico equamente
>>> su
>>> tutti i sotto processi.
>>>
>>>
>>> Ciao  Manlio
>>
>> Riusciresti a darmi qualche dettaglio in più?
>
> Per il "Thundering herd problem", trovi una breve descrizione su
> Wikipedia:
> http://en.wikipedia.org/wiki/Thundering_herd_problem
>
> Vedi anche:
> http://www.citi.umich.edu/projects/linux-scalability/reports/accept.html
>
>
> Il problema è menzionato anche nel libro "UNIX Network Programming".
> In particolare, oltre al problema di prestazioni che per pochi processi
> non dovrebbe essere preoccupante, c'è un altro problema con quello che
> fai: chiamare accept sullo stesso file descriptor ereditato potrebbe
> **non** funzionare su alcuni sistemi; in questi casi hai bosogno di
> serializzare la chiamata con un lock o mutex (che è quello che,
> opzionalmente, fa Nginx [1]).
>
> Ti consiglio di fare dei test e di documentarti meglio (io non so
> nemmeno cosa è cambiato in versioni recenti di Linux).
>


accept() non e' piu' soggetta al thundering herd da un po', mentre
epoll_wait() lo e', e pure di brutto.

Dai test effettuati per uWSGI e' comunque meglio avere il thundering herd
(basta saperlo gestire nel codice) che un locking costante quando il
numero di epoll_wait() in ascolto non supera (o supera di poco) il numero
di core cpu. Se invece il numero e' abnorme (ad esempio quando si va in
preforking+multithread) su Linux e' meglio il locking (per lo meno dei
thread all'interno dello stesso processo)

Ad oggi ne' io ne' i ragazzi di unicorn (server ruby di cui gunicorn e' il
porting in python) abbiamo rilevato un impatto del thundering herd tale da
doverlo gestire diversamente. Di idea completamente opposta i ragazzi di
passenger che invece preferiscono gestire tutto in user space con una
porta aperta per ogni processo. Evidentemente si sentono stra-sicuri del
loro load-balancer interno, o sanno qualcosa che io non so :)

-- 
Roberto De Ioris
http://unbit.it


Maggiori informazioni sulla lista Python