Fermer

juin 26, 2018

Optimisation côté serveur avec Nginx et pm-static –


Cet article fait partie d'une série de sur la construction d'un exemple d'application – un blog de galerie multi-images – pour l'analyse comparative des performances et les optimisations. (Voir le repo ici.)


Continuons d'optimiser notre application . Nous commençons avec la génération de vignettes à la volée qui prend 28 secondes par requête, selon la plate-forme qui exécute votre application de démonstration (dans mon cas, il s'agissait d'une lente intégration du système de fichiers entre l'OS hôte et Vagrant). assez acceptable 0,7 secondes

 28 secondes pour le chargement de la page

Certes, ces 28 secondes ne devraient se produire que sur la charge initiale. Après la mise au point, nous avons été en mesure d'atteindre des délais de production:

 1,2 seconde par requête

Dépannage

On suppose que vous avez suivi le processus d'amorçage et que l'application en cours d'exécution sur votre machine – virtuelle ou réelle.

Remarque: si vous hébergez la boîte Homestead Improved sur une machine Windows, il peut y avoir un problème avec les dossiers partagés. Cela peut être résolu en ajoutant type: "nfs" au dossier dans Homestead.yaml :

 nfs setting

Vous devrait aussi exécuter errant vers le haut à partir d'une interface shell / powershell qui a des privilèges d'administration si les problèmes persistent (clic droit, exécuter en tant qu'administrateur).

Dans un exemple, nous avons 20 à 30 secondes temps de chargement sur chaque requête, et n'a pas pu obtenir un taux plus rapide qu'une requête par seconde (il était plus proche de 0,5 par seconde):

 0.9 Locust rps

Le processus

Passons par le processus de test. Nous avons installé Locust sur notre hôte, et créé un locustfile.py :

 très simple à partir de l'import de locust HttpLocust, TaskSet, task

class UserBehavior (TaskSet):
    @tache 1)
    def indice (auto):
        self.client.get ("/")

class WebsiteUser (HttpLocust):
    task_set = UserBehavior
    min_wait = 300
    max_wait = 1000

Ensuite, nous avons téléchargé ngrok sur notre ordinateur invité et y avons passé toutes les connexions HTTP, afin de pouvoir tester notre application sur une URL statique.

Puis nous avons démarré Locust et grimpé notre application avec 100 utilisateurs parallèles:

 Locust

Notre pile de serveurs se composait de PHP 7.1.10, Nginx 1.13.3 et MySQL 5.7.19, sur Ubuntu 16.04

PHP-FPM et son paramétrage du gestionnaire de processus

] php-fpm génère ses propres processus, indépendamment du processus du serveur Web. La gestion du nombre de ces processus est configurée dans /etc/php/7.1/fpm/pool.d/www.conf (7.1 peut être échangé contre le numéro de version de PHP actuellement utilisé). 19659008] Dans ce fichier, nous trouvons le réglage pm . Ce paramètre peut être défini sur dynamique ondemand et statique . Dynamique est peut-être la sagesse la plus commune; cela permet au serveur de jongler entre plusieurs paramètres du nombre de processus PHP générés:

 pm = dynamic
; Le nombre de processus enfants à créer lorsque pm est défini sur "static" et le
; nombre maximal de processus fils lorsque pm est défini sur "dynamique" ou "ondemand".
; Cette valeur définit la limite du nombre de requêtes simultanées qui seront
; servi.
pm.max_children = 6
; Le nombre de processus enfants créés au démarrage.
; Remarque: Utilisé uniquement lorsque pm est défini sur "dynamique"
; Valeur par défaut: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.start_servers = 3
; Le nombre minimal souhaité de processus de serveur inactif
; Remarque: Utilisé uniquement lorsque pm est défini sur "dynamique"
; Remarque: Obligatoire lorsque pm est défini sur "dynamique"
pm.min_spare_servers = 2
; Le nombre maximal souhaité de processus de serveur inactif
; Remarque: Utilisé uniquement lorsque pm est défini sur "dynamique"
; Remarque: Obligatoire lorsque pm est défini sur "dynamique"
pm.max_spare_servers = 4

Les significations de ces valeurs s'expliquent d'elles-mêmes et la génération de processus est effectuée à la demande, mais contrainte par ces valeurs minimales et maximales.

Après avoir corrigé le problème des dossiers partagés Windows avec nfs et en testant avec Locust, nous avons pu obtenir environ cinq requêtes par seconde, avec environ 17-19% d'échecs, avec 100 utilisateurs simultanés. Une fois qu'il a été envahi par les requêtes, le serveur a ralenti et chaque requête a pris plus de dix secondes pour finir

Puis nous avons changé le réglage pm en ondemand

Ondemand signifie qu'il n'y a pas de processus minimum: une fois que les requêtes s'arrêtent, tous les processus s'arrêtent. Certains préconisent ce paramètre, car cela signifie que le serveur ne dépensera aucune ressource dans son état inactif, mais pour les instances de serveur dédiées (non partagées), ce n'est pas forcément le meilleur. La génération d'un processus comprend un surcoût, et ce qui est gagné en mémoire est perdu dans le temps nécessaire pour engendrer des processus à la demande. Les paramètres pertinents ici sont les suivants:

 pm.max_children = 6
; et
pm.process_idle_timeout = 20s;
; Le nombre de secondes après lequel un processus inactif sera tué.
; Remarque: Utilisé uniquement lorsque pm est défini sur "ondemand"
; Valeur par défaut: 10s

Lors des tests, nous avons un peu augmenté ces paramètres, en nous souciant moins des ressources.

Il y a aussi pm.max_requests qui peut être changé, et qui désigne le nombre de requêtes chaque processus enfant devrait s'exécuter avant de réapparaître.

Ce réglage est un compromis entre la vitesse et la stabilité, où 0 signifie illimité.

ondemand n'a pas apporté beaucoup changement, sauf que nous avons remarqué plus de temps d'attente initiale lorsque nous avons commencé à essaimer notre demande avec des demandes, et plus d'échecs initiaux. En d'autres termes, il n'y avait pas de gros changements: l'application était capable de servir environ quatre à six requêtes maximum par seconde. Le temps d'attente et le taux d'échecs étaient similaires à ceux de la configuration dynamique

Puis nous avons essayé le paramètre pm = static permettant à nos processus PHP de prendre en charge le maximum des ressources du serveur , à court d'échanger, ou de conduire le processeur à l'arrêt. Ce paramètre signifie que nous forçons le maximum de notre système à tout moment. Cela signifie également que, dans le cadre des contraintes de notre serveur, il n'y aura pas de frais de temps de calcul.

Nous avons constaté une amélioration de 20%. Le taux de demandes échouées était encore significatif, et le temps de réponse n'était toujours pas très bon. Le système était loin d'être prêt pour la production

 Locust - pm static

Cependant, sur Pingdom Tools, nous avons eu un support de 3,48 secondes quand le système n'était pas sous pression:

 pm static - Pingdom

Cela signifiait que pm static était une amélioration, mais dans le cas d'une charge plus importante, elle descendrait encore.

Dans l'un des articles précédents, nous expliquions comment Nginx peut lui-même servir de système de mise en cache, à la fois pour le contenu statique et dynamique. Nous avons donc cherché la magie de Nginx et essayé d'amener notre application à un tout autre niveau de performance.

Et nous avons réussi. Voyons comment.

Nginx et fastcgi Caching

 proxy_cache_path / home / errant / Code / ng-cache niveaux = 1: 2 keys_zone = ng_cache: 10m max_size = 10g inactif = 60m;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
fastcgi_cache_path / home / vagabulente / Code / ngd-cache niveaux = 1: 2 keys_zone = ngd_cache: 10m inactif = 60m;
fastcgi_cache_key "$ scheme $ request_method $ hôte $ request_uri";
fastcgi_cache_use_stale erreur timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control expire Set-Cookie;
add_header NGINX_FASTCGI_CACHE $ upstream_cache_status;

serveur {
    écoute 80;
    écoute 443 ssl http2;
    nom_serveur nginx-performance.app;
    root "/ home / vagabond / Code / projet-nginx / public";

    index index.html index.htm index.php;

    jeu de caractères utf-8;

    proxy_cache ng_cache;

    emplacement / {
        try_files $ uri $ uri / /index.php?$query_string;
    }

    emplacement = https: //dab1nmslvvntp.cloudfront.net/favicon.ico {access_log off; log_not_found off; }
    location = /robots.txt {access_log désactivé; log_not_found off; }

    access_log désactivé;
    Erreur_log /var/log/nginx/nginx-performance.app-error.log erreur;

    sendfile désactivé;

    client_max_body_size 100m;

    emplacement ~  .php $ {
        fastcgi_split_path_info ^ (. + . php) (/.+) $;
        fastcgi_pass unix: /var/run/php/php7.1-fpm.sock;
        fastcgi_index index.php;
        inclure fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $ racine_document $ fastcgi_script_name;

        fastcgi_intercept_errors désactivé;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 4 16k;
        fastcgi_connect_timeout 300;
        fastcgi_send_timeout 300;
        fastcgi_read_timeout 300;


      fastcgi_cache ngd_cache;
      fastcgi_cache_valid 60m;
    }

    emplacement ~ /.ht {
        Nier tous;
    }

    ssl_certificate /etc/nginx/ssl/nginx-performance.app.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx-performance.app.key;
}

Nous avons ouvert notre fichier hôte virtuel Nginx et ajouté les paramètres ci-dessus. Expliquons-les.

 proxy_cache_path / home / vagabond / Code / ng-cache niveaux = 1: 2 keys_zone = ng_cache: 10m max_size = 10g inactif = 60m;

Comme expliqué dans Apache vs Nginx Performance: techniques d'optimisation proxy_cache_path est utilisé pour la mise en cache de actifs statiques – comme des images, des feuilles de style, des fichiers JavaScript. Le chemin lui-même doit exister; nous devons créer ces répertoires. niveaux désigne la profondeur des répertoires dans ce chemin / dossier. Traverser peut être coûteux pour le temps de la demande, il est donc bon de le garder petit. La zone Clés est un nom. chaque hôte virtuel peut (et devrait) en utiliser un distinct. La taille maximale signifie la taille maximale du cache et inactive signifie que les éléments de temps seront conservés dans le cache même s'ils ne sont pas demandés.

Après cette période d'inactivité, le cache pour une ressource sera repeuplé.

proxy_cache_use_stale et fastcgi_cache_use_stale sont intéressants, car ils peuvent fournir la fonction "toujours en ligne" que nous pouvons voir avec les fournisseurs de CDN comme Cloudflare: si le back-end se déconnecte, Nginx servira ces ressources du cache .

Tous les paramètres fastcgi_cache _ * sont pour le contenu (dynamique) généré par PHP, et les paramètres proxy_cache _ * sont pour les fichiers statiques

fastcgi_cache_key définit une clé pour la mise en cache.

fastcgi_ignore_headers désactive le traitement de certains champs d'en-tête de réponse du backend FastCGI .

ont utilisé:

 fastcgi_cache_purge

Ceci définit les demandes qui seront capables de purger le cache. Nginx (son ngx_http_fastcgi_module ) nous donne tout à fait le jeu d'outils complet pour la mise en cache. Un exemple d'utilisation de la directive ci-dessus serait:

 fastcgi_cache_path / data / nginx / cache keys_zone = cache_zone: 10m;

map $ request_method $ purge_method {
    PURGE 1;
    par défaut 0;
}

serveur {
    ...
    emplacement / {
        le backend fastcgi_pass;
        fastcgi_cache cache_zone;
        fastcgi_cache_key $ uri;
        fastcgi_cache_purge $ purge_method;
    }
}

Ici, la requête PURGE REST serait capable de supprimer des éléments du cache.

Il est également possible de revalider le cache sous certaines conditions.

Dans notre configuration, nous n'avons pas utilisé toutes les subtilités et fonctionnalités de Nginx , mais il est bon de savoir qu'ils sont là si nous en avons besoin.

Nous avons ajouté des en-têtes Nginx à nos réponses, pour savoir si la ressource était servie depuis le cache:

 add_header NGINX_FASTCGI_CACHE $ upstream_cache_status;

Ensuite, nous pouvons inspecter et disséquer notre temps de chargement de la page pour voir ce qui fonctionne et ce qui ne fonctionne pas:

 En-têtes du cache Nginx

Pour réchauffer le cache, nous devrons passer par le

fastcgi_cache_methods peut être utile pour mettre en cache des méthodes de requête spécifiques, comme POST. GET et HEAD sont mis en cache par défaut.

Il y a aussi la mise en cache des octets, qui peut être utilisée pour l'optimisation du streaming vidéo, comme décrit ici

Avec toute la configurabilité offerte par Nginx.

Après avoir activé la configuration ci-dessus – à la fois pour le contenu statique et dynamique de notre site Web – nous avons démarré Locust, et avons grillé notre système avec 100 utilisateurs parallèles. La différence dans les résultats était tout simplement incroyable. La contrainte sur laquelle le serveur était auparavant ne pouvait pas être ressentie maintenant.

 Nginx cache swarming

Nous pouvons voir que le temps médian par demande était de 170 millisecondes. C'est environ une amélioration centuple. Les demandes par seconde étaient supérieures à 100.

Nous pouvons également voir, dans le graphique de temps de réponse moyen, que les demandes initiales ont connu des pics de temps de réponse, et que le temps de réponse a diminué de plus en plus vers 130 ms. ] La mise en cache de Nginx nous a apporté de grandes améliorations. Le principal goulot d'étranglement avec cette application ne sera pas les ressources matérielles, même si elles sont modestes.

 Caching Locust terminal

On constate également que le pourcentage de demandes échouées est passé de 17% à 0,53%. 19659008] Nous sommes ensuite allés au test de page de Pingdom et avons testé notre site Web:

 GTmetrix

Nous pouvons voir que nous avons réussi à amener le temps de chargement de la page bien en dessous d'une seconde! , qui a un "bagage" supplémentaire de galeries apparentées et récentes:

 Test de page de galerie simple

Nous sommes attachant un fichier HAR rapport de ce test pour analyse.

Conclusion

Dans cet article, certains des points abordés dans mes discussions précédentes sur la performance Nginx ont été testés, et d'autres paramètres comme la gestion des processus, et la mesure de son impact sur le temps de chargement des pages.

Avons-nous oublié quelque chose qui mérite d'être mentionné? Pouvez-vous penser à d'autres paramètres Nginx que nous pourrions appliquer à cette application pour améliorer les performances?




Source link