Python et Httpd une affaire qui marche¶
Afin de rendre le web dynamique il est possible d’utiliser différents interpreteur et différents language
Le serveur httpd de la fondation apache permet de mettre plusieurs laguages. Nous allons nous arrêter sur le language Python.
Cet article a pour but de présenter les différentes possibilités de mise en place de python au sein d’un serveur httpd
CGI Python avec httpd¶
Cette solution est la plus ancienne.
Le principe est que le serveur web lance via un interpréteur python un code python.
Donc si on pose comme question http://test1.fr/test.py , httpd va lancer l’interpreteur python en lui passant pour paramètre test.py. La réponse du programme test.py sera alors traiter comme un réponse http de la part du serveur httpd
En Python, l’excellent module cgi de la bibliothèque standard rend cela encore plus facile.
exemple de mise en place¶
Je ne reviens pas dans la configuration d’un site pour httpd. Le fichier de configuration qui permet le cgi python prend cette forme
# conf/site-available/test1.conf
<VirtualHost *:80>
ServerName test1.fr
ServerAlias *.test1.fr
DocumentRoot "C:/www/test1"
<Directory "C:/www/test1">
Options Indexes FollowSymLinks ExecCGI
AllowOverride None
Order allow,deny
Allow from all
</Directory>
Alias /img "C:/www/common/img"
<Directory "C:/www/common/img">
Order allow,deny
Allow from all
</Directory>
<IfModule dir_module>
DirectoryIndex index.html
</IfModule>
ErrorDocument 404 /missing.html
ErrorLog "C:/www/test1/logs/error.log"
LogLevel info
<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
#CustomLog "C:/www/test1/logs/access.log" common
CustomLog "|bin/rotatelogs.exe C:/www/test1/logs/access-%Y_%m_%d_%H_%M.log 86400" common
#CustomLog "|bin/rotatelogs.exe C:/www/test1/logs/access-%Y_%m_%d.log 5M" common
</IfModule>
<IfModule mime_module>
AddHandler cgi-script .cgi .py
</IfModule>
</VirtualHost>
Il faut noter la présence des directives
- Options Indexes FollowSymLinks ExecCGI
- AddHandler cgi-script .cgi .py
exemple de fichier python¶
le fichier test.py
C:
|_ www
|_ test1
|_ test.py
#!C:/Python/Python27/python.exe
print "Content-Type: text/html"
print
print "<html><head>"
print ""
print "</head><body>"
print "Hello"
print "</body></html>"
Note
la première ligne contient le path de l’interpréteur python
exemple de résultat¶
il est possible de faire afficher un fichier html par python
#!C:/Python/Python27/python.exe
print "Content-Type: text/html"
print ""
path = 'C:/www/test1/index.html'
fichier = open(path,'r')
for i in fichier.readlines():
print i
un premier benchmark avec l’url http://test1.fr/test.py
C:\Program Files\Apache Software Foundation\Apache2.2\bin>ab.exe -t 30 -c 5 http://test1.fr/test.py
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking test1.fr (be patient)
Finished 1111 requests
Server Software: Apache/2.2.22
Server Hostname: test1.fr
Server Port: 80
Document Path: /test.py
Document Length: 45 bytes
Concurrency Level: 5
Time taken for tests: 30.038 seconds
Complete requests: 1111
Failed requests: 0
Write errors: 0
Total transferred: 199256 bytes
HTML transferred: 49995 bytes
Requests per second: 36.99 [#/sec] (mean)
Time per request: 135.185 [ms] (mean)
Time per request: 27.037 [ms] (mean, across all concurrent requests)
Transfer rate: 6.48 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 2 0.6 2 13
Processing: 87 133 37.9 117 318
Waiting: 83 129 37.2 114 315
Total: 89 135 37.9 119 319
Percentage of the requests served within a certain time (ms)
50% 119
66% 132
75% 154
80% 168
90% 195
95% 213
98% 230
99% 239
100% 319 (longest request)
conclusion¶
Même si cette solution est simple à mettre en place elle a beaucoup d’inconvénient:
- chaque demande lance un interpreteur complet: ce qui est très gourmand en ressource
- il n’y a pas de multualisation de ressource: si un programme fait appel à une base de donnée chaque demande va générer une ouverture et une fermeture de session vis à vis du SGBD
Cette solution est rarement employée.
mod_python¶
mod_python permet à l’instar du php d’intégrer directement du code python dans des pages html. Le principale inconvénient est la mise en cache des pages: ce qui signifie que toutes modification de code impose un redémarrage du serveur httpd.
On peut trouver mod_python à l’adresse http://www.modpython.org/
A priori cette méthode n’est plus maintenu pour python > 2.5 sur plateforme windows
WSGI¶
Le module permettant à python de faire du wsgi est disponible sur http://code.google.com/p/modwsgi/.
Décrit dans le PEP 333, indépendant du serveur HTTP, WSGI est un standard d’interface entre le serveur HTTP et Python. Il est plutôt prévu pour fournir aux développeurs d’environnements une interface sur laquelle s’appuyer, afin de garantir que leur environnement tournera sur tous les serveurs HTTP.
WSGI a plusieurs propriétés intéressantes :
- Rapide (pour mon application, qui utilise un SGBD, mesuré avec echoping, le gain de performance est net, la médiane passe de 0,4 s (CGI) à 0,1 s (WSGI), sur la machine locale).
- Une mise en œuvre de WSGI pour Apache, mod_wsgi, a un mode « démon » où les processus qui exécutent le code Python ne sont pas dans le serveur HTTP mais sont des démons séparés, comme avec FastCGI. Ainsi, une bogue dans le code Python (par exemple une fuite de mémoire) n’affectera pas le processus Apache (l’un des problèmes récurrents de mod_python ou de mod_perl).
- Modèle de programmation suffisamment simple pour que les applications CGI puissent être portées très vite.
exemple de mise en place¶
Il suffit de mettre le fichier .so dans le répertoire modules d’apache et de la renommer mod_wsgi.so
C:
|_ Program Files
|_ Apache Software Foundation
|_ Apache2.2
|_ modules
|_ mod_wsgi.so
Je ne reviens pas dans la configuration d’un site pour httpd. Le fichier de configuration qui permet le WSGI python prend cette forme
# conf/site-available/test1.conf
LoadModule wsgi_module modules/mod_wsgi.so
<VirtualHost *:80>
ServerName test1.fr
ServerAlias *.test1.fr
DocumentRoot "C:/www/test1"
<Directory "C:/www/test1">
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>
Alias /img "C:/www/common/img"
<Directory "C:/www/common/img">
Order allow,deny
Allow from all
</Directory>
<IfModule dir_module>
DirectoryIndex index.html
</IfModule>
ErrorDocument 404 /missing.html
ErrorLog "C:/www/test1/logs/error.log"
LogLevel info
<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
#CustomLog "C:/www/test1/logs/access.log" common
CustomLog "|bin/rotatelogs.exe C:/www/test1/logs/access-%Y_%m_%d_%H_%M.log 86400" common
#CustomLog "|bin/rotatelogs.exe C:/www/test1/logs/access-%Y_%m_%d.log 5M" common
</IfModule>
WSGIScriptAlias /myapp "C:/www/test1/wsgi-scripts/myapp.wsgi"
<Directory "C:/www/test1/wsgi-scripts">
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
Il faut noter la présence des directives
- LoadModule qui permet de prendre en charge le module WSGI
- WSGIScriptAlias qui permet d’identifier le script wsgi
La configuration indique une structure suivante
C:
|_ www
|_ test1
|_ wsgi-scripts
|_ myapp.wsgi
exemple de fichier python¶
fichier myapp.wsgi
C:
|_ www
|_ test1
|_ wsgi-scripts
|_ myapp.wsgi
def application(environ, start_response):
status = '200 OK'
output = 'Hello World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
start_response(status, response_headers)
return [output]
exemple de résultat¶
utilisé bottle en mode wsgi¶
Il est possible d’utiliser bottle en mode wsgi ce qui rend l’application pkus performante et plus simple à écrire
fichier myapp.wsgi utilisant bottle
# Change working directory so relative paths (and template lookup) work again
import os
os.chdir(os.path.dirname(__file__))
import bottle
from bottle import route, run, template
# ... build or import your bottle application here ...
# Do NOT use bottle.run() with mod_wsgi
@route('/hello/:name')
def getname(name='World'):
return template('<b>Hello {{name}}</b>!', name=name)
@route('/')
def index(name='World'):
return '<b>Hello Index</b>!'
application = bottle.default_app()
et le résultat
installtion sous linux¶
cela reste simple
apt-get install apache2 libapache2-mod-wsgi python-dev python-pip python-bottle
il faut par la suite configurer apache dans /etc/apache2 (ne configurer que apache2.conf et site-available/test1.conf en modificant les paths par rapport à windows )
et ajouter le répertoire wsgi-scripts
cd /var/www
mkdir wsgi-scripts
chown www-data:www-data wsgi-scripts
Benchmark¶
Il est possible de réaliser un benchmark afin de voir l’impact de l’utilisation de WSGI avec python/bottle
Nous pouvons imaginer la structure suivante
C:
|_ www
|_ test1
|_ index.html
|_ wsgi-scripts
|_ myapp.wsgi
Le fichier myapp.wsgi
# Change working directory so relative paths (and template lookup) work again
import os
os.chdir(os.path.dirname(__file__))
import bottle
from bottle import route, run, template, static_file
import time
@route('/sleep')
def sleep():
time.sleep(1)
return static_file('index.html', root='C:/www/test1')
@route('/')
def index():
return static_file('index.html', root='C:/www/test1')
application = bottle.default_app()
un premier benchmark avec l’url http://test1.fr/index.html
C:\Program Files\Apache Software Foundation\Apache2.2\bin>ab.exe -t 30 -c 5 http://test1.fr/index.html
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking test1.fr (be patient)
Completed 5000 requests
Finished 7699 requests
Server Software: Apache/2.2.22
Server Hostname: test1.fr
Server Port: 80
Document Path: /index.html
Document Length: 39 bytes
Concurrency Level: 5
Time taken for tests: 30.013 seconds
Complete requests: 7699
Failed requests: 0
Write errors: 0
Total transferred: 2502175 bytes
HTML transferred: 300261 bytes
Requests per second: 256.52 [#/sec] (mean)
Time per request: 19.491 [ms] (mean)
Time per request: 3.898 [ms] (mean, across all concurrent requests)
Transfer rate: 81.42 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 2 0.5 2 4
Processing: 13 17 0.5 17 21
Waiting: 3 10 4.4 10 18
Total: 14 19 0.6 20 22
WARNING: The median and mean for the total time are not within a normal deviation
These results are probably not that reliable.
Percentage of the requests served within a certain time (ms)
50% 20
66% 20
75% 20
80% 20
90% 20
95% 21
98% 21
99% 21
100% 22 (longest request)
temps moyen de traitement 3.898 ms
un deuxième benchmark avec l’url http://test1.fr/myapp
Note
Il faut noter que http://test1.fr/myapp et http://test1.fr/index.html donne le même résultat. Donc les différences observées ne proviennent que du module wsgi
C:\Program Files\Apache Software Foundation\Apache2.2\bin>ab.exe -t 30 -c 5 http://test1.fr/myapp
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking test1.fr (be patient)
Completed 5000 requests
Finished 7634 requests
Server Software: Apache/2.2.22
Server Hostname: test1.fr
Server Port: 80
Document Path: /myapp
Document Length: 39 bytes
Concurrency Level: 5
Time taken for tests: 30.005 seconds
Complete requests: 7634
Failed requests: 0
Write errors: 0
Total transferred: 2168056 bytes
HTML transferred: 297726 bytes
Requests per second: 254.43 [#/sec] (mean)
Time per request: 19.652 [ms] (mean)
Time per request: 3.930 [ms] (mean, across all concurrent requests)
Transfer rate: 70.56 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 2 0.6 2 4
Processing: 13 17 0.7 18 21
Waiting: 3 13 3.5 13 18
Total: 14 19 0.6 20 22
WARNING: The median and mean for the processing time are not within a normal deviation
These results are probably not that reliable.
WARNING: The median and mean for the total time are not within a normal deviation
These results are probably not that reliable.
Percentage of the requests served within a certain time (ms)
50% 20
66% 20
75% 20
80% 20
90% 21
95% 21
98% 21
99% 21
100% 22 (longest request)
temps moyen de traitement 3.930 ms soit moins d‘1% d’impact
il faut noter que seul le module wsgi peut impacter le temps de réponse
d’où le résultat du benchmark http://test1.fr/myapp/sleep
C:\Program Files\Apache Software Foundation\Apache2.2\bin>ab.exe -t 30 -c 5 http
://test1.fr/myapp/sleep
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking test1.fr (be patient)
Finished 146 requests
Server Software: Apache/2.2.22
Server Hostname: test1.fr
Server Port: 80
Document Path: /myapp/sleep
Document Length: 39 bytes
Concurrency Level: 5
Time taken for tests: 30.177 seconds
Complete requests: 146
Failed requests: 0
Write errors: 0
Total transferred: 41464 bytes
HTML transferred: 5694 bytes
Requests per second: 4.84 [#/sec] (mean)
Time per request: 1033.451 [ms] (mean)
Time per request: 206.690 [ms] (mean, across all concurrent requests)
Transfer rate: 1.34 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 2 0.5 2 3
Processing: 1003 1004 1.5 1004 1017
Waiting: 1001 1002 1.2 1002 1011
Total: 1005 1006 1.5 1006 1019
Percentage of the requests served within a certain time (ms)
50% 1006
66% 1006
75% 1006
80% 1006
90% 1007
95% 1007
98% 1011
99% 1013
100% 1019 (longest request)
Il faut aussi noter que l’utilisation seul de bottle (sans apache) produit un temps de traitement plus de 2 fois plus long
C:\Program Files\Apache Software Foundation\Apache2.2\bin>ab.exe -t 30 -c 5 http://127.0.0.1:8081/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Finished 4275 requests
Server Software: WSGIServer/0.1
Server Hostname: 127.0.0.1
Server Port: 8081
Document Path: /
Document Length: 6362 bytes
Concurrency Level: 5
Time taken for tests: 30.003 seconds
Complete requests: 4275
Failed requests: 0
Write errors: 0
Total transferred: 27992700 bytes
HTML transferred: 27197550 bytes
Requests per second: 142.49 [#/sec] (mean)
Time per request: 35.091 [ms] (mean)
Time per request: 7.018 [ms] (mean, across all concurrent requests)
Transfer rate: 911.13 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 1 2 0.5 2 5
Processing: 13 33 1.6 32 44
Waiting: 9 28 1.6 28 39
Total: 14 35 1.6 34 47
Percentage of the requests served within a certain time (ms)
50% 34
66% 35
75% 35
80% 36
90% 37
95% 37
98% 39
99% 41
100% 47 (longest request)
Point sur les performances¶
le test concernait la délivrance du fichier index.html
apache | CGI | WSGI | bottle | |
temps moyen de traitement (ms) | 3.898 | 27.03 | 3.930 | 7.018 |
la solution la plus adaptée en python est le WSGI