Forum Programmation.php [Solved] [Optimisation] PHP/Bash tester le plus rapidement si un process est actif

Posté par  . Licence CC By‑SA.
Étiquettes :
1
17
mar.
2019

Dans un script de HealthCheck l'utilisateur peut tester si un ou plusieurs processus sont actifs sur le serveur.

Voici un exemple adapté du code :

Cette version scan /proc/*/cmdline à la recherche des logiciels demandés par l'user.

<?php
$softwareName = array( "syncthing", "top"  ); /* add name for check if these softwares names is active running */

    $chkSoftIsLunch=true;
    if(is_array($softwareName)) {
        foreach($softwareName as $name){
                $pids=null;
                exec("pidof ".$name, $pids);
                if(empty($pids)) {
                    $chkSoftIsLunch=false;
                }
        }
    }

if($chkSoftIsLunch){
    echo "processus actif";
}else{
    echo "processus PAS actif";
}
?>

Néanmoins l'activation de ce block de code double voir triple la durée d’exécution du script PHP. (ce qui me parait beaucoup)
Auriez-vous d'autre solution plus rapide et moins énergivore que d’exécuter pidof via exec() ? 😀
Dans le cas ci-présent, chaque itération de la boucle foreach semble prendre a elle seule entre 20ms et 30ms (sur ARM, sur X64 c'est divisé par 5).

EDIT : nouvelle édition du code :

Cette version parse /proc///cmdline une fois par nom de logiciel. La fonction est optimisée au max.

$chkSoftwareName = array( "software1", "software2"  ); /* uncomment and edit for check if these softwares names are running */


    if(is_array($chkSoftwareName)) {
        if(!checkIfSoftwareIsRunning ($chkSoftwareName)){
            echo "un des logiciels ne fonctionne pas !";
        }
    }

function checkIfSoftwareIsRunning ($chkSoftwareName){
    $path="/proc/";
    $subfile="cmdline";
        foreach($chkSoftwareName as $softName){
            $check=false;
            $listFiles = scandir($path, 1);
            $lenght = count($listFiles);
            foreach ($listFiles as $filename) {
                if(!$check && is_dir($path.$filename)){
                    if(is_file($path.$filename.'/'.$subfile)){
                        if ($myFile = @fopen($path.$filename."/".$subfile, "r")) {
                            while (($buffer = fgets($myFile)) !== false) {
                                if(preg_match("/".$softName."/i", $buffer)){
                                    $check=true;
                                }
                            }
                            fclose($myFile);
                        }
                    }
                }
                $lenght--;
                if ($lenght<=0 && $check==false) {  return false;  } // last tab
            }
        }
    return true;
}

Cette deuxième implémentation parse une seule fois /proc/*/cmdline mais est quand même plus lente que la version juste ci-haut. La fonction peut encore être un peu optimisée.

function checkIfSoftwareIsRunning2($chkSoftwareName){
    $path="/proc/";
    $subfile="cmdline";
    foreach($chkSoftwareName as $softName){ 
        $check[$softName]=false;
    }
    $listFiles = scandir($path, 1);
    foreach ($listFiles as $filename) {
        if(is_dir($path.$filename)){
            if(is_file($path.$filename.'/'.$subfile)){
                if ($myFile = @fopen($path.$filename."/".$subfile, "r")) {
                    while (($buffer = fgets($myFile)) !== false) {
                        foreach($chkSoftwareName as $softName){
                            if(preg_match("/".$softName."/i", $buffer, $match[0])){
                                $check[$softName]=true;
                            }
                        }
                    }
                    fclose($myFile);
                }
            }
        }
    }
    foreach($check as $k=>$v){
        if(!$v){    return false;   }
    }
    return true;
}
  • # man pidof

    Posté par  (site web personnel) . Évalué à 3.

    Il semble que tu puisses ne l'appeler qu'une seule fois avec tous les programmes que tu veux tester.

    SYNOPSIS
           pidof [-s] [-c] [-n] [-x] [-o omitpid[,omitpid..]]  [-o omitpid[,omitpid..]..]  program [program..]
    
    DESCRIPTION
           Pidof finds the process id's (pids) of the named programs. It prints those id's on the standard output. 
    …
    

    Python 3 - Apprendre à programmer dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

    • [^] # Re: man pidof

      Posté par  . Évalué à 1. Dernière modification le 17 mars 2019 à 20:24.

      Il semble que tu puisses ne l'appeler qu'une seule fois avec tous les programmes que tu veux tester.

      Merci, update faites 😉

      Le type de commande :

      pidof -s nomSoft1 nomSoft2 | wc -w

      Puis comparé la taille du nombre qui sort de la commande avec le nombre de logiciels recherché.

      <?php
      $chkSoftwareName = array( "soft1", "soft2"  );
      
          if(is_array($chkSoftwareName)) {
              foreach($chkSoftwareName as $name){
                      $fullName = $fullName." ".$name;
              }
              $pids=null;
              exec("pidof -s ".$fullName." | wc -w", $pids);
              if(empty($pids) || intval($pids[0])<count($chkSoftwareName) ) {
                  echo "un des loulous n'est pas lancé !";
              }
          }
      ?>

      Par contre même en ne recherchant qu'un seul software, l'utilisation de pidof fait tripler le temps de chargement du script sur x64 (passe de 5ms a 17-25ms) et entre 3x et 4x sur ARM (de 8-20ms à 45-65ms).

      • [^] # Re: man pidof

        Posté par  (site web personnel) . Évalué à 3.

        Si tu en es à compter les millisecondes, pourquoi forker ? S'il n'y a pas de wrapper PHP pour libproc, consulter /proc directement depuis PHP pourrait améliorer les choses, non ?

        Debian Consultant @ DEBAMAX

        • [^] # Re: man pidof

          Posté par  . Évalué à 1.

          Si tu en es à compter les millisecondes, pourquoi forker ?

          Ce n'est pas un fork.

          S'il n'y a pas de wrapper PHP pour libproc, consulter /proc directement depuis PHP pourrait améliorer les choses, non ?

          J'avoue ne pas voir comment utiliser le contenu de /proc en partant de noms de processus.

      • [^] # Re: man pidof

        Posté par  . Évalué à 1. Dernière modification le 18 mars 2019 à 13:20.

        l'utilisation de pidof fait tripler le temps de chargement du script sur x64 (passe de 5ms a 17-25ms) et entre 3x et 4x sur ARM (de 8-20ms à 45-65ms).

        Sur ma machine, qui n'est pas à genoux, un simple pidof firefox prend en effet à peu près 10ms. Il faut quand même lancer un shell qui charge un exécutable (deux avec wc) et faire plein d'appels système, puis matcher une chaine de caractères sur plusieurs centaines de candidats. Du coup, ça prend le temps que ça prend, on parle quand même de moins de 1/50e de seconde pour une tâche que tu ne vas pas réaliser toutes les secondes…

        Si tu as le contrôle de soft1 et soft2, pourquoi ne pas leur faire créer un fichier vide quelque part dans /tmp, ou un truc comme ça? Ça devrait aller beaucoup plus vite de vérifier l'existence d'un fichier plutôt que d'appeler pidof…

        J'avoue ne pas voir comment utiliser le contenu de /proc en partant de noms de processus.

        Tu peux parser le nom du soft, par exemple dans /proc/*/cmdline sur tous les pid. Est-ce que pidoff ne ferait pas un truc comme ça?

        • [^] # Re: man pidof

          Posté par  . Évalué à 1. Dernière modification le 18 mars 2019 à 13:33.

          Du coup, ça prend le temps que ça prend, on parle quand même de moins de 1/50e de seconde pour une tâche que tu ne vas pas réaliser toutes les secondes…

          Bah justement si, le but de ce script est d'être appelé le plus souvent possible (plus d'une fois par seconde si l'on considère le nombre de machines). Par conséquent, diminuer au maximum son impacte est primordiale.

          Avec une moyenne de 55ms de réponse et un appel tout les 5 secondes ça fait une durée d’exécution de 950,4 secondes par jours (soit 15,84 minutes/jours) ou encore 96,36 heures par an, le tout a multiplié par le nombre de serveurs backends.

          Je vais regarder pour tenter de convertir ce calcul en consommation énergétique.

          Si tu as le contrôle de soft1 et soft2, pourquoi ne pas leur faire créer un fichier vide quelque part dans /tmp

          Parce que ce fichier permettrait de savoir si les softwares ont été lancé, pas s'ils sont toujours en cours de fonctionnement au moment du check.

          • [^] # Re: man pidof

            Posté par  (site web personnel) . Évalué à 3.

            Si tu leur fais écrire leur pid dans un fichier, tu devrais pouvoir le lire et regarder ensuite directement dans /proc/lepid/status l'état du processus, sans avoir besoin d'exec.

            Python 3 - Apprendre à programmer dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

        • [^] # Re: man pidof

          Posté par  . Évalué à 1. Dernière modification le 18 mars 2019 à 16:30.

          Tu peux parser le nom du soft, par exemple dans /proc/*/cmdline sur tous les pid. Est-ce que pidoff ne ferait pas un truc comme ça?

          Hmmm, l'idée est très bonne mais il semble que l'utilisateur www-data ne puisse accéder à ces fichiers :

          [Mon Mar 18 16:22:50.620046 2019] [php7:error] [pid 14756] [client 192.168.42.42:43000] PHP Fatal error:  Uncaught UnexpectedValueException: RecursiveDirectoryIterator::__construct(/proc/tty/driver): failed to open dir: Permission denied in /var/www/html/test/test.php:14\nStack trace:\n#0 [internal function]: RecursiveDirectoryIterator->__construct('/proc/tty/drive...', 0)\n#1 /var/www/html/test/test.php(14): RecursiveDirectoryIterator->getChildren()\n#2 {main}\n  thrown in /var/www/html/test/test.php on line 14
          

          Testé avec le code suivant :

          <?php
          
          $chkSoftwareName = array( "syncthing", "apache2"  );
          $path="/proc/";
          $subfile="cmdline";
          
              if(is_array($chkSoftwareName)) {
                  $checkOfAllSoft=true;
                  foreach($chkSoftwareName as $softwareName){
                          $checkOfThisSoft=false;
                          $di = new RecursiveDirectoryIterator($path);
                          $cpt = 0;
                          $lenght = count(new RecursiveIteratorIterator($di));
                          foreach (new RecursiveIteratorIterator($di) as $filename => $file) {
                              if(is_dir($filename.'/'.$subfile)){
                                  if(preg_match($softwareName, fgets($filename.'/'.$subfile))){
                                      echo $filename . '/' . $subfile . ' <br/>';
                                      $checkOfThisSoft=true;
                                  }
                              }
                              if ($cpt == $lenght - 1 && $checkOfThisSoft==false) {
                                      // last tab
                                  $checkOfAllSoft=false;
                              }
                              $cpt++;
                          }
                  }
                  if(!$checkOfAllSoft) {
                      echo "un des loulous n'est pas lancé !";
                  }
              }
          ?>
          • [^] # Re: man pidof

            Posté par  . Évalué à 1. Dernière modification le 18 mars 2019 à 17:20.

            Rectification de ma part, c'est mon code qui était pourris. Cette version fonctionne et est beaucoup plus rapide que via pidof.

            <?php
            
            $chkSoftwareName = array( "apache2", "mysql"  );
            
            function checkIfSoftwareIsRunning(){
            
            }
                if(is_array($chkSoftwareName)) {
            function checkIfSoftwareIsRunning ($chkSoftwareName){
                $path="/proc/";
                $subfile="cmdline";
                    $checkOfAllSoft=true;
                    foreach($chkSoftwareName as $softwareName){
                            $checkOfThisSoft=false;
                            $di = new RecursiveDirectoryIterator($path);
                            $cpt = 0;
                            $listFiles = scandir($path, 1);
                            $lenght = count($listFiles);
                            foreach ($listFiles as $filename) {
                                if(is_dir($path.$filename)){
                                    if(is_file($path.$filename.'/'.$subfile)){
                                        $handle = @fopen($path.$filename."/".$subfile, "r");
                                        if ($handle) {
                                            while (($buffer = fgets($handle)) !== false) {
                                                if(preg_match("/".$softwareName."/i", $buffer)){
                                                    $checkOfThisSoft=true;
                                                }
                                            }
                                            if (!feof($handle)) {
                                                echo "Error: unexpected fgets() fail\n";
                                            }
                                            fclose($handle);
                                        }
                                    }
                                }
                                if ($cpt == $lenght - 1 && $checkOfThisSoft==false) {
                                        // last tab
                                    $checkOfAllSoft=false;
                                }
                                $cpt++;
                            }
                    }
                    if(!$checkOfAllSoft) {
                        header('HTTP/1.1 502'); exit;
                    }else{
                        return true;
                    }
            }
            
            
                }
            ?>
            • [^] # Re: man pidof

              Posté par  . Évalué à 1. Dernière modification le 18 mars 2019 à 17:36.

              Excusez pour le copié-collé foireux, j'ai mis le code dans le message du thread.

              Tu peux parser le nom du soft, par exemple dans /proc/*/cmdline sur tous les pid. Est-ce que pidoff ne ferait pas un truc comme ça?

              Par contre il y a risque de faux positifs avec cette méthode je pense.

              Si je lance un script qui à comme paramètre "syncthing" et que justement l'utilisateur recherche "syncthing", je pense que ça va grep.

  • # utiliser ce qui est prevu pour ca

    Posté par  . Évalué à 4.

    le SNMP permet de lister les processus en cours
    donc tu n'as plus qu'à parser la sortie ou demander une branche speciale pour ne sortir que les process listés

    ca devrait etre plus rapide qu'ouvrir une page php, appeler un shell, executer une commande, parser le resultat, redonner le resultat à php, etc

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.