
uWSGI e' un server wsgi scritto in c che permette il deploy di applicazioni web python senza l'overhead tipico dell'ambiente fastcgi/scgi + flup.
Aggiunge inoltre alcune caratteristiche uniche per debuggare e monitorare le applicazioni tramite un sistema di logging su file.
Un'applicazione wsgi deve rispettare pochi e semplici requisiti, una semplice applicazione potrebbe essere la seguente (salvata all'interno del file hello.py):
def wsgiapp(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yield 'Hello World\n'Vogliamo farla girare nel nostro account Unbit (per cui ovviamente avremo gia' creato la docroot dentro /www).
Copiamo il file hello.py (che non e' altro che un modulo python) dentro /lib/python2.5/site-packages nella nostra home. In questo modo il modulo hello diventa disponibile nel PYTHONPATH.
Andiamo sul pannello e nelle opzioni del dominio spuntiamo il flag wsgi. Nel campo wsgi_module impostiamo hello (il nome del nostro modulo) e in wsgi_callable wsgiapp che e' il nome della funzione da richiamare.
Attendiamo i soliti 30 secondi e visitiamo il nostro sito che restituira' il classico Hello World.
(per l'elenco completo consultare http://projects.unbit.it/uwsgi )
funzione harakiri per autoterminare il server uWSGI se una richiesta impiega troppo tempo
Ogni processo uWSGI puo' gestire piu' applicazioni montate sotto url differenti.
Vogliamo montare la nostra applicazione hello sotto /saluta e (crepi l'avarizia) anche sotto /ciao, mentre tutte le altre richieste saranno mappate su apache come se uWSGI non fosse attivo.
Cancelliamo i campi wsgi_module e wsgi callable ed editiamo wsgi_mountpoints in questo modo:
/saluta pippo wsgiapp /ciao pippo wsgiapp
Attendiamo i soliti 30 secondi e visitiamo le due url. Il sistema funzionera', ma la vecchia applicazione montata sotto / e' ancora disponibile all'interno del server uWSGI. Dobbiamo quindi uccidere uWSGI (per forzare il riavvio) tramite la Console Unbit o tramite ssh ( o qualsiasi altro sistema utile).
Se si aggiungono applicazioni non c'e' bisogno di riavviare uWSGI, in tutti gli altri casi (come la modifica ai file delle nostre applicazioni) va ricreato l'ambiente uccidendo il/i processo/i
Ora tutte le richiesta a /saluta e /ciao saranno gestite da uWSGI, le altre da apache in maniera classica. E' quindi possibile integrare ambienti cgi (ad esempio php) con applicazioni python.
I moduli wsgi disponibili nel panorama opensource (ad esempio mod_wsgi per Apache e Nginx) permettono l'uso di script wsgi per definire le applicazioni.
Ad esempio un'applicazione Django (chiamata mysite) puo' essere gestita con questo script wsgi:
import os os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' import django.core.handlers.wsgi application = django.core.handlers.wsgi.WSGIHandler()
Salviamo lo script come mysite_wsgi.py (attenzione di solito questi script hanno estensione .wsgi) e copiamolo all'interno della directory dei moduly python (/lib/python2.5/site-packages).
Impostiamo nel campo wsgi_script (nelle opzioni del dominio) mysite_wsgi (senza estenzione in quanto e' un modulo python) e attendiamo i soliti 30 secondi.
Ovviamente l'applicazione mysite dovra' risiedere all'interno del PYTHONPATH (altrimento python non riuscira' a trovarla). Se la nostra applicazione risiede nella docroot non c'e' problema poiche' la docroot viene aggiunta automaticamente al PYTHONPATH se invece e' fuori dalla docroot o in una sua sottodirectory aggiungiamo il path nella voce wsgi_pythonpath del pannello. Questo campo permette di estendere il nostro PYTHONPATH in base alle esigenze delle varie applicazioni.
E' importante ricordare che /www (e le sue sottodirectory) e' l'unica directory a cui apache puo' accedere per servire file statici, questo permettera' di definire contesti ad alta sicurezza dove la docroot contiene solo i file statici e uWSGI e' l'unico processo ad accedere al codice dell'applicazione.
Vogliamo montare un'istanza di Trac sotto /vivatrac
I moduli di Trac sono gia' nel PYTHONPATH di sistema, mentre i nostri progetti sono dentro /trac nella home dell'account (impostazione di default).
Lo script wsgi e' il seguente, chiamatelo come volete
import os os.environ['TRAC_ENV_PARENT_DIR'] = '/accounts/pippo/trac' import trac.web.main application = trac.web.main.dispatch_request
...dove pippo e' il nome del nostro account. (ovviamente e' possibile rendere lo script piu' portabile leggendo il contenuto della variabile d'ambiente HOME)
Nel parametro del pannello wsgi_mountpoints aggiungiamo:
/vivatrac trac_script
dove trac_script e' il nome dello script wsgi caricato.
Ora abbiamo un'applicazione Django sotto /, un ambiente Trac sotto /vivatrac e 2 helloworld sotto /saluta e /ciao
Esistono 2 timeout rilevanti in uWSGI. Il primo (gestito da wsgi_timeout) e' il tempo massimo che Apache puo' attendere la risposta ad una richiesta fatta a uWSGI. Scaduto il timeout apache restituisce un errore. Ovviamente l'errore non implica che la richiesta che sta gestendo uWSGI sia interrotta, ma solo che Apache per motivi di sicurezza non puo' tenere occupato uno slot in attesa di una risposta che potrebbe non arrivare mai.
Per bloccare invece una richiesta direttamente all'interno di uWSGI, l'unico modo e' che quest'ultimo si autotermini (TM)
Per farlo all'inizio di ogni richiesta viene impostato un segnale UNIX di allarme (SIGALRM) che una volta scaduto richiama la funzione di harakiri.
Il timeout dell'allarme puo' essere impostato tramite il parametro wsgi_harakiri_timeout (richiede il riavvio di uWSGI), se e' a 0 la funzione di harakiri viene disabilitata.
Ovviamente un'applicazione wsgi potrebbe sovrascrivere l'handler del SIGALRM ma a quel punto la colpa e' solo vostra se il sito va giu'
uWSGI supporta la syscall sendfile(), per i piu' tecnici si tratta di un modo efficiente di inviare un flusso di dati da un file descriptor a un socket senza disturbare lo userspace.
Sebbene sia molto efficiente e' preferibile far servire (ove possibile) i file statici direttamente ad apache. Questo evita di dover passare la richiesta a uWSGI e tenerlo impegnato in operazioni che puo' fare qualcun altro.
Il parametro wsgi_offf permette di elencare le url per cui si vuole che il controllo non sia passato a uWSGI. Ad esempio in ambiente Django e' opportuno elencare in wsgi_off /media
Sebbene python non sia certo un divoratore di memoria come invece lo sono alcuni suoi cugini, e' buona norma prima di andare in produzione, verificare il consumo di address space e rss della propria applicazione sotto carico.
Il flag wsgi_memory_debug aggiunge al file di log di uWSGI (vedi prossima sezione) il quantitativo di address space e rss occupati dopo ogni richiesta. Questa funzionalita' introduce un piccolo overhead, e' quindi opportuno disabilitarla una volta andati in produzione.
uWSGI non sarebbe un buon server senza un sistema di logging adeguato. Per ogni richiesta vengono riportate la data, l'ip del richiedente, il numero di variabili passate da apache, il tempo impiegato a processare la singola richiesta (in millisecondi), stato http, protocollo http,bytes restituiti e qualche altra cosina utile.
Di default ogni processo uWSGI logga nello stderr_log. Tramite il parametro wsgi_logfile e' possibile specificare un file alternativo per ogni dominio (path relativo alla home)
Per alcune applicazioni soggette ad alto traffico (o semplicemente scritte male) puo' fare la differenza generare piu' processi uWSGI. Il parametro wsgi_nfork imposta il numero di processi da avviare (richiede il riavvio di uWSGI ad ogni modifica).
Fate attenzione che eventuali moduli o tecnologie usate dalla vostra applicazioni supportino la concorrenza.
Il supporto di python per i thread e' molto controverso, tuttavia in alcuni casi puo' essere utile generare thread nel corso di una richiesta wsgi. Questo tipo di tecnica e' supportata da uWSGI. Il thread generato ottiene cpu da uWSGI quando quest'ultimo e' impegnato in operazioni che non richiedono l'ambiente python.
Se volete eseguire script cgi in un ambiente WSGI potete elencare le uri degli script in wsgi_off oppure abilitare il flag wsgi_as_fallback_handler. Questa opzione passa il controllo della richiesta HTTP ad uWSGI solo se non stati definiti altri handler dal webserver. Nel caso degli script CGI Apache imposta l'handler cgi-script quindi uWSGI non entrera' mai in gioco (a meno che il file sia inesistente). Questa opzione e' comunque potenzialmente pericolosa poiche' vi costringera' a controllare qualsiasi file sia presente nella vostra docroot. Abilitatela solo se costretti e, in tal caso, prendete anche in considerazione una riprogettazione per poterla disabilitare il prima possibile.
uWSGI da solo serve a poco. I componenti chiave sono la gestione dei processi dei Kernel Unbit e il modulo Apache che comunica con uWSGI. Tuttavia i sorgenti di uWSGI sono disponibili per incentivare l'implementazione di nuove funzionalita' e scovare eventuali bug o ottimizzazioni da effettuare.
Non e' escluso in futuro che si possano implementare dei moduli di integrazione con gli standard fastcgi/scgi/ajp o piu' semplicemente http.
uWSGI (l'ultima modifica รจ del 2009-12-03 11:58:40, fatta da RobertoDeIoris)