Outils pour utilisateurs

Outils du site


programmation_serveur_php_python_nodejs

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
programmation_serveur_php_python_nodejs [2019/11/27 22:03] josephprogrammation_serveur_php_python_nodejs [2020/05/19 08:29] (Version actuelle) joseph
Ligne 2: Ligne 2:
 # Comment programmer un serveur web ? # Comment programmer un serveur web ?
  
-* Côté client (c'est à dire ce qui s'affiche dans le navigateur du visiteur du site), on fait du HTML (contenu) + CSS (mise en page) + Javascript (aspect dynamique des pages).+* **Côté client** (c'est à dire ce qui s'affiche dans le navigateur du visiteur du site), on fait du HTML (contenu) + CSS (mise en page) + Javascript (aspect dynamique des pages).
  
-* Mais comment programmer ce qu'il se passe *côté serveur* ? Exemple : lorsqu'on poste sur Faceb**k, Twitter, lorsqu'on uploade une photo en ligne, il faut bien que du code s'exécute sur un *serveur distant* pour enregistrer tout cela dans une base de données, etc. +* Mais comment programmer ce qu'il se passe **côté serveur** ? Exemple : lorsqu'on poste sur Faceb**k, Twitter, lorsqu'on uploade une photo en ligne, il faut bien que du code s'exécute sur un *serveur distant* pour enregistrer tout cela dans une base de données, etc. 
  
 C'est ce que nous allons voir ici. C'est ce que nous allons voir ici.
  
-# PHP+## PHP
  
 PHP est **le** langage par excellence pour programmer côté serveur. Statistique 2018 : PHP est **le** langage par excellence pour programmer côté serveur. Statistique 2018 :
Ligne 28: Ligne 28:
 Avantages : Avantages :
  
-* Il y a souvent rien à installer, car Apache et PHP sont installés par défaut sur les hébergements webs mutualisés. En gros, que se passe-t-il ? Lorsque Apache reçoit une requête pour `http://example.com/monsite/index.php`, il voit que c'est un .php et il passe la requête à PHP (via [mod_php](https://stackoverflow.com/questions/2712825/what-is-mod-php/2712839#2712839)) qui exécute le code et produit une chaîne de caractères (string) en sortie, et Apache livre cela au "client" dans son navigateur.+* Il y a souvent rien à installer, car Apache et PHP sont installés par défaut sur les hébergements webs mutualisés. En gros, que se passe-t-il ? Lorsque Apache reçoit une requête pour `http://example.com/monsite/index.php`, il voit que c'est un .php et il passe la requête en interne à PHP (via [mod_php](https://stackoverflow.com/questions/2712825/what-is-mod-php/2712839#2712839), donc sans avoir à démarrer un nouveau process) qui exécute le code et produit une chaîne de caractères (string) en sortie, et Apache livre cela au "client" dans son navigateur.
  
 * Cela est très rapide (à chaque requête, le process Apache qui s'occupe de cette requête [démarre un nouveau thread pour PHP](https://stackoverflow.com/questions/5171639/creation-of-new-process-for-each-request-of-web-page/5171656#5171656) et cela se fait très rapidement, lire aussi [cet article](https://abhinavsingh.com/how-does-php-echos-a-hello-world-behind-the-scene/) pour plus de détails) * Cela est très rapide (à chaque requête, le process Apache qui s'occupe de cette requête [démarre un nouveau thread pour PHP](https://stackoverflow.com/questions/5171639/creation-of-new-process-for-each-request-of-web-page/5171656#5171656) et cela se fait très rapidement, lire aussi [cet article](https://abhinavsingh.com/how-does-php-echos-a-hello-world-behind-the-scene/) pour plus de détails)
Ligne 34: Ligne 34:
 * On peut [mixer très facilement du HTML et du PHP](https://www.php.net/manual/en/language.basic-syntax.phpmode.php). * On peut [mixer très facilement du HTML et du PHP](https://www.php.net/manual/en/language.basic-syntax.phpmode.php).
  
-# Python+## Python
  
-## Méthode simple (mais bizarrement assez peu connue)+Dans toute la suite, on supposera qu'un classique serveur web Apache tourne sur la machine. On supposera aussi que le `VirtualHost` soit réglé en `AllowOverride All` (c'est la valeur par défaut pour certaines versions d'Apache ; cela permet de définir des paramètres directement dans les fichiers `.htaccess`, ce qui est pratique):
  
-Il y a une [méthode toute simple](https://stackoverflow.com/questions/58414076/how-to-let-the-webserver-e-g-apache-call-python-directly/58414570#58414570) pour faire de la programmation web en Python, avec une logique un peu similaire à PHP :+``` 
 +<VirtualHost *:80> 
 +    ServerName example.com 
 +    DocumentRoot /home/www/example/ 
 +    <Directory /> 
 +      AllowOverride All 
 +      Require all granted 
 +    </Directory> 
 +  </VirtualHost>  
 +``` 
 + 
 + 
 +### Méthode simple (old-school): mod_cgi 
 + 
 +* Installer [mod_cgi](http://httpd.apache.org/docs/current/mod/mod_cgi.html) (mais il est souvent déjà installé) et l'activer avec `a2enmod cgi`. 
 + 
 +* Créer un fichier `/home/www/example/app.py` ayant une permission en exécution: 
 +    ``` 
 +    #!/usr/bin/python3 
 +    print("Content-Type: text/html"
 +    print(""
 +    print("Hello world"
 +    ```` 
 +    Dans `.htaccess`, faire figurer: 
 +    ``` 
 +    Options +ExecCGI 
 +    SetEnv PYTHONIOENCODING utf8 
 +    AddHandler cgi-script .py 
 +    ``` 
 +     
 +* Visitez http://example.com/app.py, ça doit marcher!     
 + 
 +NB: Utiliser CGI revient à démarrer un nouveau process (= un nouvel interpréteur Python) pour chaque requête, ce qui est potentiellement plus lent (dans mes tests 90ms Python 2.7 / 140ms Python 3, au lieu de 34ms avec `mod_python` ou `mod_wsgi`) que les méthodes détaillées ci-après. 
 + 
 + 
 +### Méthode simple: mod_python 
 + 
 +Il y a une [méthode relativement simple](https://stackoverflow.com/questions/58414076/how-to-let-the-webserver-e-g-apache-call-python-directly/58414570#58414570) pour faire de la programmation web en Python, avec une logique similaire à celle détaillée en PHP :
  
 1. Installer le module `mod_python` : 1. Installer le module `mod_python` :
Ligne 55: Ligne 92:
  
 4. Ouvrir `https://www.example.com/test.py` dans le navigateur, ça marche ! 4. Ouvrir `https://www.example.com/test.py` dans le navigateur, ça marche !
- 
  
 Avantages : Avantages :
Ligne 61: Ligne 97:
 * logique toute simple, identique à ce qu'on fait en PHP * logique toute simple, identique à ce qu'on fait en PHP
  
-* pas de librairie / framework à utiliser, c'est donc très simple+* pas de librairie / framework à utiliser, c'est donc très léger
  
-Inconvénients :+* *"Embedded mod_python embeds Python inside Apache; no process is forked"*, voir [ici](https://stackoverflow.com/questions/219110/how-python-web-frameworks-wsgi-and-cgi-fit-together/520194#520194), ce qui semble donc optimisé niveau performance (l'interpréteur Python n'a pas besoin de redémarrer un nouveau process pour chaque requête !)
  
-* il doit y en avoir mais j'ai pas encore trouvé (j'éditerai ultérieurement)+### Méthode classique: avec un framework web (Bottle, Flask, etc.)
  
-Todo :  +La méthode la plus courante pour faire du web en Python est assez différente des méthodes présentées précédemment.
- +
-* mesurer le timing précis pour voir si c'est performant +
- +
-## La méthode classique +
- +
-La méthode "classique" pour faire du web en Python est assez différente des méthodes présentées précédemment.+
  
 Contrairement à avoir, comme précédemment, pour chaque requête, un script qui s'exécute puis se termine, il s'agit ici d'avoir **un process Python qui tourne en permanence**, et attend les requêtes au fur et à mesure, dans une boucle infinie (Event loop). Contrairement à avoir, comme précédemment, pour chaque requête, un script qui s'exécute puis se termine, il s'agit ici d'avoir **un process Python qui tourne en permanence**, et attend les requêtes au fur et à mesure, dans une boucle infinie (Event loop).
Ligne 87: Ligne 117:
     @route('/')     @route('/')
     def index():     def index():
-        return template('Bienvenue sur le site!')+        return 'Bienvenue sur le site!'
  
     run(host='localhost', port=8080)  # boucle infinie, qui attend des requêtes sur le port 8080     run(host='localhost', port=8080)  # boucle infinie, qui attend des requêtes sur le port 8080
Ligne 95: Ligne 125:
 * [bottle.py](https://bottlepy.org/docs/dev/), c'est le plus simple que j'ai testé et il marche super bien. Avantage: c'est un micro-framework en [1 seul fichier .py](https://github.com/bottlepy/bottle/blob/master/bottle.py). * [bottle.py](https://bottlepy.org/docs/dev/), c'est le plus simple que j'ai testé et il marche super bien. Avantage: c'est un micro-framework en [1 seul fichier .py](https://github.com/bottlepy/bottle/blob/master/bottle.py).
 * [Flask](https://flask.palletsprojects.com/), grosso modo pareil, peut-être un peu plus complet * [Flask](https://flask.palletsprojects.com/), grosso modo pareil, peut-être un peu plus complet
-* Django, réputé comme très complet, jamais testé+* Django, réputé comme très complet, jamais testé personnellement
  
-Le code précédent fonctionne bien, mais "écoute" sur le port 8080, donc on peut y accéder avec `https://example.com:8080/`.  Comment donc "relier" ce process Python avec Apache qui écoute, quant à lui, sur le port 80 ?+Le code précédent fonctionne bien, mais "écoute" sur le port 8080, donc on peut y accéder avec `https://example.com:8080/`.  
  
-* soit en faisant un `.htaccess` qui indique à Apache de rediriger une requête vers le serveur Python+Comment donc "relier" ce process Python avec Apache qui écoute, quant à lui, sur le port 80 ?
  
-        RewriteEngine On +* **Soit avec une règle de réécriture d'URL pour rediriger les requêtes, d'Apache vers le serveur Python** 
-        RewriteRule /(.*) http://localhost:8080/$1 [P,L] + 
-        +    Mettre ceci dans le `.htaccess` : 
 +    ``` 
 +    RewriteEngine On 
 +    RewriteRule /(.*) http://localhost:8080/$1 [P,L] 
 +    ```
     Il faut aussi se débrouiller manuellement pour que le script Python tourne **sans arrêt**, même si on ferme la fenêtre du terminal / le SSH. Exemple dans Bash :      Il faut aussi se débrouiller manuellement pour que le script Python tourne **sans arrêt**, même si on ferme la fenêtre du terminal / le SSH. Exemple dans Bash : 
 +    ```    
 +    nohup python mysever.py &
 +    ``` 
 +    ou avec `screen` (voir un tuto à ce sujet) : `screen -S pythonserver`, `python myserver.py`, puis CTRL+A+D pour "détacher" le terminal, ou avec `systemd`.
          
-        nohup python mysever.py & +* **Soit en faisant un "reverse proxy"** 
-         + 
-    ou avec `screen(voir un tuto à ce sujet) : `screen -S pythonserver``python myserver.py`, puis CTRL+A+D pour "détacher" le terminal+    Ajouter cela dans la configuration Apache `<VirtualHost>` : 
 +    ``` 
 +    ProxyPass / http://localhost:8080/ 
 +    ProxyPassReverse / http://localhost:8080/ 
 +    ```  
 +    De même que pour le point précédent, il faut lancer le script `python` manuellement et s'assurer qu'il tourne continuellement.
          
-soit avec `mod_wsgi` (méthode souvent recommandée)+**Soit avec `mod_wsgi`** 
  
-    Faire:+    Cette méthode est souvent plébiscitée, voir [ici](https://bottlepy.org/docs/dev/deployment.html#apache-mod-wsgi) pour un exemple complet avec Bottle + `mod_wsgi`.
  
-        apt-get install libapache2-mod-wsgi +    On installe ce module Apache avec : 
-         +    ``` 
-    puis mettre [ça](https://stackoverflow.com/questions/49964550/manually-stop-processes-launched-by-mod-wsgi-and-monitor-how-many-processes-are) dans la config Apache :+    apt-get install libapache2-mod-wsgi 
 +    ``` 
 +    On remplace la dernière ligne de l'application Bottle (`run(host='localhost', port=8080)`) par `application = bottle.default_app()`, puis on met cela dans la config Apache : 
 +    ``` 
 +    <VirtualHost *:80> 
 +      ServerName example.com 
 +      WSGIScriptAlias / /home/www/example/app.py 
 +      <Directory /
 +        AllowOverride All 
 +        Require all granted 
 +      </Directory> 
 +    </VirtualHost>      
 +    ```  
 +    L'avantage de cette méthode avec `mod_wsgi` est que **c'est Apache** qui va s'occuper tout seul de lancer le script Python et il n'y a **plus besoin** à la main de démarrer le script .py et de s'arranger pour qu'il tourne continuellement (donc plus besoin de `nohup` ou `screen` comme dans l'exemple précédent).
          
-        <VirtualHost *:80> +    Par contre, en testant `mod_wsgi` je suis tombé sur quelques problèmes`import module` qui ne marche pas alors qu'il est [dans le même répertoire](https://stackoverflow.com/questions/59088010/wsgi-importerror-no-module-named-hello-module-in-the-same-directory-of-the-ma), des [Fatal Python error: PyEval_AcquireThread: NULL new thread state](https://stackoverflow.com/questions/18013356/fatal-python-error-pyeval-acquirethreaddans les logs Apache, etc. Bref, à voir à l'usage.
-          ServerName example.com +
-          WSGIDaemonProcess yourapp user=www-data group=www-data processes=5 threads=5 +
-          WSGIScriptAlias / /var/www/wsgi_test/app.wsgi +
-          <Directory /var/www/wsgi_test> +
-            WSGIProcessGroup yourapp +
-            WSGIApplicationGroup %{GLOBAL} +
-            Require all granted +
-          </Directory> +
-        </VirtualHost> +
-      +
-     (à améliorer / retester)+
  
-# Javascript+## Javascript
  
 Oui on peut faire du Javascript côté serveur, c'est le cas notamment avec le framework très populaire [NodeJS](https://nodejs.org/). Oui on peut faire du Javascript côté serveur, c'est le cas notamment avec le framework très populaire [NodeJS](https://nodejs.org/).
  
-Le principe est le même que dans le paragraphe précédemment, on a un process qui tourne **en permanence**, avec une boucle d'événements (event loop) :+Le principe est le même que dans le paragraphe précédemment, on a un process qui tourne **en permanence**, avec une boucle d'événements (event loop). On le démarre avec :
  
-   node app.js+    node app.js
        
 Voici le code pour `app.js`: Voici le code pour `app.js`:
Ligne 151: Ligne 196:
  
  
-# C+## C
  
 Pour les puristes ! In progress... Pour les puristes ! In progress...
 +
 +## A voir (divers)
 +
 +### A lire...
 +
 +Pour bien voir la différence CGI / FastCGI / mod_wsgi / mod_python:
 +
 +https://www.electricmonk.nl/docs/apache_fastcgi_python/apache_fastcgi_python.html
 +
 +
 +### Comparaison de performance mod_php vs. mod_python
 +
 +Créons un fichier `test.py`:
 +
 +    import time
 +    def index(req):
 +        return(str(time.time()))
 +
 +un fichier `test.php`:
 +
 +    <?php
 +        echo time();
 +    ?>
 +
 +le `.htaccess` qui permet d'utiliser `mod_python`:
 +
 +    AddHandler mod_python .py
 +    PythonHandler mod_python.publisher
 +
 +et enfin `index.html` pour tester le temps que prend chaque requête:
 +
 +    <div id="a">Click test.py</div>
 +    <div id="b">Click test.php</div>
 +
 +    <script type="text/javascript">
 +    var start;
 +
 +    document.getElementById("a").onclick = function() { 
 +        start = Date.now()
 +        var xhr = new XMLHttpRequest();
 +        xhr.open("GET", "test.py");
 +        xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText); console.log(Date.now() - start); } }
 +        xhr.send("");
 +    }
 +
 +    document.getElementById("b").onclick = function() { 
 +        start = Date.now()
 +        var xhr = new XMLHttpRequest();
 +        xhr.open("GET", "test.php");
 +        xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText); console.log(Date.now() - start); } }
 +        xhr.send("");
 +    }
 +    </script>
 +    
 +Ouvrons `index.html` dans le navigateur et regardons le contenu de la console.
 +    
 +Bilan : 
 +
 +* la différence entre `mod_php` et `mod_python` est imperceptible, j'ai environ 35 millisecondes dans les deux cas, ce qui correspond à mon ping avec le serveur
 +
 +* je craignais que `mod_python` relance un interpréteur Python *pour chaque requête* (ce qui aurait été loooonnnnng... probablement au moins 100 ou 200 ms?) mais fort heureusement, non, donc c'est parfait! Voir aussi [ici](#methode_simple_mais_bizarrement_assez_peu_mise_en_avant) à ce sujet.
  
 </markdown> </markdown>
  
 {{tag>web php python nodejs cgi wsgi joseph}} {{tag>web php python nodejs cgi wsgi joseph}}
programmation_serveur_php_python_nodejs.1574892204.txt.gz · Dernière modification : 2019/11/27 22:03 de joseph