Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1183 connectés 

  FORUM HardWare.fr
  Programmation
  PHP

  Démon PHP avec du fork :)

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

Démon PHP avec du fork :)

n°2095368
the_bigboo
Posté le 16-08-2011 à 10:02:55  profilanswer
 

Hello à tous :)
 
Je sais que peu de gens utilisent PHP pour faire ce genre de chose... Mais je tente quand même :)
 
Je suis en train de faire un système de démon multi processus en PHP. A Dire vrai j'en avais déjà fait un pour ma boite, mais vu que je désire en faire une version plus clean, et plus générique, c'est le moment de peut être revoir certains choix techniques.
 
Pour info je créé mes processus avec un bon vieux fork (et non un process_open).
Cependant, après avoir créé les processus, il faut pouvoir dialoguer avec :)
 
Dans la première version que j'ai faite, j'avais utilisé les queues de message System V pour remplir ce rôle, mais en même temps je le dit qu'il y a peut être d'autres possibilité moins délicates à implémenter, ou plus efficaces. Je sais que c'est aussi possible d'utiliser les pipes.
 
Des idées ?

mood
Publicité
Posté le 16-08-2011 à 10:02:55  profilanswer
 

n°2095378
rufo
Pas me confondre avec Lycos!
Posté le 16-08-2011 à 10:22:12  profilanswer
 

Assez étonnant d'utiliser PHP pour faire ce genre de chose :/ Du C/C++ aurait été plus adapté je trouve...


---------------
Astres, outil de help-desk GPL : http://sourceforge.net/projects/astres, ICARE, gestion de conf : http://sourceforge.net/projects/icare, Outil Planeta Calandreta : https://framalibre.org/content/planeta-calandreta
n°2095381
the_bigboo
Posté le 16-08-2011 à 10:26:58  profilanswer
 

rufo a écrit :

Assez étonnant d'utiliser PHP pour faire ce genre de chose :/ Du C/C++ aurait été plus adapté je trouve...


Oui, c'est ce qu'on me dit souvent :) Néanmoins, le premier démon a été utilisé dans ma boite, qui avait tous ses applicatifs en PHP. Donc...
De plus ce qui va tourner derrière sera en PHP aussi donc :)
 
je ne maitrise pas le C et encore moins le C++... Mais je suis d'accord avec toi.

n°2095399
the_bigboo
Posté le 16-08-2011 à 11:23:19  profilanswer
 

Ben en fait, que ce soit du C ou du PHP, (bien que le langage soit différent)
tu peux faire communiquer les processus, après c'est le comment.
 
D'un côté j'ai les queues de messages, il y a aussi les pipes, et peut être encore d'autres solutions... J'ai aussi vu qu'on pouvait faire ça avec des sémaphores, mais ça n'a plus l'air vraiment "à la mode"...

n°2095909
CyberDenix
Posté le 18-08-2011 à 11:23:50  profilanswer
 

Je te conseille de faire un shell_exec si tu es sous unix.
Le principe est d'avoir un père et des fils, mais dutiliser le même fichier.php pour cela, que le père va relancer en ligne de commande en passant en argument une marque de sa paternité (très utile dans le cas de plusieurs pères, pour savoir à quel papa tel ou tel gosse appartient)
 
Voici, en speed, une partie du code que j'utilise pour un crawler de site en PHP :
 

Code :
  1. /**
  2.  *
  3.  */
  4.     public function crawl($options = null) {
  5.         if (!isset($options['--url']) || !$options['--url']) {
  6.   return false;
  7.  }
  8.         $time = microtime(true);
  9.  // Master-url initialization
  10.  // ---------------------------------------------------------------------
  11.  if (isset($options['--master-url'])) {
  12.   $master_url = $options['--master-url'];
  13.  } else {
  14.             $master_url = $options['--url'];
  15.  }
  16.  // Master-id initialization
  17.  // ---------------------------------------------------------------------
  18.  if (isset($options['--master-id'])) {
  19.   $master_id = $options['--master-id'];
  20.  } else {
  21.   $command_line = '';
  22.   if (isset($options) && $options) {
  23.                 $command_line = PHP_CLI.' '.WSM_CRAWLER_PATH.'crawler.php';
  24.    foreach ($options as $k => $v) {
  25.     $command_line .= ' '.$k.' ';
  26.     if (is_bool($v)) {
  27.      $command_line .= (int)$v;
  28.     } else if (is_numeric($v)) {
  29.      $command_line .= (int)$v;
  30.     } else {
  31.      $command_line .= '\''.$v.'\'';
  32.     }
  33.    }
  34.   }
  35.             $master_id = $this->getDatabase()->exec('INSERT INTO `crawl` (`url`, `command_line`, `start_date`) VALUES (?, ?, ?)', array($options['--url'], $command_line, date('c')), null, false);
  36.       $options['--id'] = $this->insertUrls(array($options['--url']), $master_id);
  37.  }
  38.         // Parse the url
  39.  // ---------------------------------------------------------------------
  40.  $parse_result = $this->parse($options);
  41.  if (isset($parse_result['error'])) {
  42.       file_put_contents('./curlerror.txt', $parse_result['error']['id'].' - '.$parse_result['error']['message'], FILE_APPEND | LOCK_EX);
  43.  }
  44.  // Retry (X times maximum) if a 500 error is catched
  45.  // ---------------------------------------------------------------------
  46.         $max_retries = (isset($options['--500-max-retries']) && $options['--500-max-retries']) ? $options['--500-max-retries'] : 3;
  47.  if ($parse_result['http_code'] == 500) {
  48.   $wait_between_retries = (isset($options['--500-wait-between-retries']) && $options['--500-wait-between-retries']) ? $options['--500-wait-between-retries'] : 10000;
  49.   for ($i = 0; $i < $max_retries; ++$i) {
  50.    usleep($wait_between_retries);
  51.                 $parse_result = $this->parse($options);
  52.    if ($parse_result['http_code'] != 500) {
  53.     break;
  54.    }
  55.   }
  56.  }
  57.  // Add new urls to the master crawler queue
  58.  // ---------------------------------------------------------------------
  59.  if (isset($parse_result['links'])) {
  60.             $white_list_filters = array();
  61.   if (isset($options['--white-list']) && $options['--white-list']) {
  62.    $in_white_list = false;
  63.    if (strpos($options['--white-list'], '|') !== false) {
  64.     $white_list_filters = explode('|', $options['--white-list']);
  65.    } else {
  66.                       $white_list_filters = array($options['--white-list']);
  67.    }
  68.   }
  69.             $black_list_filters = array();
  70.   if (isset($options['--black-list']) && $options['--black-list']) {
  71.    $in_black_list = false;
  72.    if (strpos($options['--black-list'], '|') !== false) {
  73.     $black_list_filters = explode('|', $options['--black-list']);
  74.    } else {
  75.                       $black_list_filters = array($options['--black-list']);
  76.    }
  77.   }
  78.   $urls = array();
  79.   foreach ($parse_result['links'] as $link) {
  80.    if (isset($link['href']) && $link['href']) {
  81.     $url = $link['href'];
  82.     // Auto-prepend domain to domain-less urls
  83.     // ---------------------------------------------------------
  84.                 $url = preg_replace('`^/`Usiu', $this->getDomain($master_url).'/', $url);
  85.     // Delete anchors
  86.     // ---------------------------------------------------------
  87.                 $url = preg_replace('`#[^/]`Usiu', '', $url);
  88.     // If url is on the same level than the master url
  89.                 // ---------------------------------------------------------
  90.                 if ($this->getDomain($master_url) == $this->getDomain($url)) {
  91.                         $in_white_list = true;
  92.                         $in_black_list = false;
  93.                         // If url satisfies at least one of the url filters
  94.                  // ---------------------------------------------------------
  95.                     if ($white_list_filters) {
  96.        $in_white_list = false;
  97.        foreach ($white_list_filters as $filter) {
  98.         if (preg_match(trim($filter), $url)) {
  99.          $in_white_list = true;
  100.          break;
  101.         }
  102.        }
  103.       }
  104.       // If url do not satisfies any of the url filters
  105.                   // ---------------------------------------------------------
  106.                   if ($black_list_filters) {
  107.        foreach ($black_list_filters as $filter) {
  108.         if (preg_match(trim($filter), $url)) {
  109.          $in_black_list = true;
  110.          break;
  111.         }
  112.        }
  113.       }
  114.      if ($in_white_list && !$in_black_list) {
  115.       // If there is no preexisting url in the web page
  116.             // -------------------------------------------------
  117.                   if (!isset($urls[$url])) {
  118.        // If there is no preexisting url in database
  119.                 // ---------------------------------------------
  120.                    $res = $this->getDatabase()->req('SELECT 1
  121.                                                              FROM `url` `U`
  122.                                                             WHERE `U`.`fk_crawl` = ?
  123.                                                               AND `U`.`url` = ?', array($master_id, $url), null, false);
  124.        if (!$res) {
  125.         $urls[$url] = $url;
  126.        }
  127.       }
  128.      }
  129.     } else {
  130.      //echo 'The following url was excluded : '.$url."\n".'because '.$this->getDomain($master_url).' != '.$this->getDomain($url).''."\n";
  131.     }
  132.    }
  133.   }
  134.      if ($urls) {
  135.    $this->insertUrls($urls, $master_id);
  136.   }
  137.  }
  138.         // Update the url information
  139.  // ---------------------------------------------------------------------
  140.  $this->updateUrl($parse_result, $options['--id']);
  141.  // Master's  management of crawling processes
  142.  // ---------------------------------------------------------------------
  143.  if (!isset($options['--master-id'])) {
  144.             $nb_processes = $master_id ? $this->getNbProcesses($master_id) : 0;
  145.   $max_processes = (isset($options['--max-processes']) && $options['--max-processes'] > 0) ? $options['--max-processes'] : 10;
  146.   $start = true;
  147.   $nb_parsed_urls = 0;
  148.   $wait_for_url_label = '[Crawler '.$master_id.'] Waiting for urls ... ';
  149.             $wait_for_url_label_length = mb_strlen($wait_for_url_label);
  150.   $wait_for_url_token = array('-', '/', '|', '\\');
  151.             $wait_for_url_token_count = count($wait_for_url_token);
  152.             $max_processes_label = '[Crawler '.$master_id.'] Waiting for free processes ... ';
  153.             $max_processes_label_length = mb_strlen($max_processes_label);
  154.   $max_processes_token = array('-', '/', '|', '\\');
  155.             $max_processes_token_count = count($max_processes_token);
  156.             $urls = $this->getUnparsedUrls($master_id, $max_processes);
  157.   while ($urls || $start) {
  158.    $start = false;
  159.    foreach ($urls as $url) {
  160.          ++$nb_parsed_urls;
  161.     // Do not exceed the --max-urls limit
  162.     // ---------------------------------------------------------
  163.     if (isset($options['--max-urls']) && $options['--max-urls'] > 0) {
  164.      if ($nb_parsed_urls >= $options['--max-urls']) {
  165.       echo '[Crawler '.$master_id.'] --max-urls '.$options['--max-urls'].' reached, stop crawl'."\n";
  166.       break(2);
  167.      }
  168.     }
  169.     // Do not exceed the --max-processes limit
  170.     // ---------------------------------------------------------
  171.     $w = 0;
  172.                  while ($nb_processes >= $max_processes) {
  173.      if (!$w) {
  174.       echo $max_processes_label;
  175.      } else {
  176.                         $w2_max = mb_strlen($max_processes_token[($w % $max_processes_token_count)]);
  177.       for ($w2 = 0; $w2 < $w2_max; ++$w2) {
  178.        echo chr(8);
  179.       }
  180.      }
  181.      echo $max_processes_token[(++$w % $max_processes_token_count)];
  182.      //echo '[Crawler '.$master_id.'] --max-processes '.$nb_processes.' reached, wait '.$usleep.' ms'."\n";
  183.      usleep(1000);
  184.                         $nb_processes = $this->getNbProcesses($master_id);
  185.     }
  186.     if ($w) {
  187.      $w2_max = $max_processes_label_length;
  188.      for ($w2 = 0; $w2 < $w2_max + 1; ++$w2) {
  189.       echo chr(8);
  190.      }
  191.     }
  192.     // Lock the url, to not parse it twice
  193.     // ---------------------------------------------------------
  194.     $this->getDatabase()->exec('UPDATE `url` SET `fk_parsing_status` = ? WHERE `id` = ?', array(2, $url['id']), null, false);
  195.     // Instanciate a crawling process in background
  196.     // ---------------------------------------------------------
  197.     $command_line = PHP_CLI.' '.WSM_CRAWLER_PATH.'crawler.php';
  198.     $command_line .= ' --id \''.$url['id'].'\'';
  199.     $command_line .= ' --url \''.$url['url'].'\'';
  200.     $command_line .= ' --master-id \''.$url['masterId'].'\'';
  201.     $command_line .= ' --master-url \''.$url['masterUrl'].'\'';
  202.     foreach ($options as $option_name => $option_value) {
  203.      if ($option_name !== '--id'    &&
  204.       $option_name !== '--url'   &&
  205.       $option_name !== '--master-id'  &&
  206.       $option_name !== '--master-url'  &&
  207.       $option_name !== '--max-processes' &&
  208.       $option_name !== '--max-urls'  )
  209.                      $command_line .= ' '.$option_name.' \''.$option_value.'\'';
  210.     }
  211.     // Comment this line can help to see error messages related to wrong paths
  212.     $command_line .= ' > /dev/null'.' 2>/dev/null'.' &';
  213.     //echo 'Executing '.$command_line."\n";
  214.     shell_exec($command_line);
  215.                 $nb_processes = $this->getNbProcesses($master_id);
  216.     echo '[Crawler '.$master_id.'] Process '.$url['url'].' ('.$nb_processes.' process'.(($nb_processes < 2) ? '' : 'es').')'."\n";
  217.    }
  218.    // As threads are running in background, the program could finish
  219.    // because no url has been parsed (and no new url has been added
  220.    // to the list) before the end of the launch of the X firsts threads.
  221.    // That's why it is required to wait for some new urls, or kill
  222.    // the wait loop if all urls have been crawled without finding
  223.    // no new url.
  224.    $w = 0;
  225.                 while (!($urls = $this->getUnparsedUrls($master_id, $max_processes))) {
  226.     if (!$w) {
  227.      echo $wait_for_url_label;
  228.     } else {
  229.                         $w2_max = mb_strlen($wait_for_url_token[($w % $wait_for_url_token_count)]);
  230.      for ($w2 = 0; $w2 < $w2_max; ++$w2) {
  231.       echo chr(8);
  232.      }
  233.     }
  234.     echo $wait_for_url_token[(++$w % $wait_for_url_token_count)];
  235.     usleep(1000);
  236.                     $rs = $this->getDatabase()->req(' SELECT 1
  237.              FROM `url` `U`
  238.                WHERE `U`.`fk_crawl` = ?
  239.               AND `U`.`fk_parsing_status` != ?', array($master_id, 3), null, false);
  240.     if (!$rs) {
  241.      break;
  242.     }
  243.    }
  244.    if ($w) {
  245.     $w2_max = $wait_for_url_label_length;
  246.     for ($w2 = 0; $w2 < $w2_max + 1; ++$w2) {
  247.      echo chr(8);
  248.     }
  249.    }
  250.   }
  251.             // Master waits for its crawling processes to finish
  252.   // -----------------------------------------------------------------
  253.   $previous_nb_processes = $nb_processes;
  254.   while (($nb_processes = $this->getNbProcesses($master_id))) {
  255.    usleep(2000);
  256.    if ($nb_processes != $previous_nb_processes) {
  257.                  echo '[Crawler '.$master_id.'] No new url detected, waiting after '.$nb_processes.' process'.(($nb_processes < 2) ? '' : 'es').' to finish'."\n";
  258.    }
  259.                 $previous_nb_processes = $nb_processes;
  260.   }
  261.             $this->getDatabase()->exec('UPDATE `crawl` SET `end_date` = ?', array(date('c')), null, false);
  262.             echo '[Crawler '.$master_id.'] Crawl finished : '.$nb_parsed_urls.' urls parsed in '.(microtime(true) - $time).' s'."\n";
  263.  }
  264. }


 
 
La partie qui t'intéresse tout particulièrement est :
 

Code :
  1. // Instanciate a crawling process in background
  2.     // ---------------------------------------------------------
  3.     $command_line = PHP_CLI.' '.WSM_CRAWLER_PATH.'crawler.php';
  4.     $command_line .= ' --id \''.$url['id'].'\'';
  5.     $command_line .= ' --url \''.$url['url'].'\'';
  6.     $command_line .= ' --master-id \''.$url['masterId'].'\'';
  7.     $command_line .= ' --master-url \''.$url['masterUrl'].'\'';
  8.     foreach ($options as $option_name => $option_value) {
  9.      if ($option_name !== '--id'    &&
  10.       $option_name !== '--url'   &&
  11.       $option_name !== '--master-id'  &&
  12.       $option_name !== '--master-url'  &&
  13.       $option_name !== '--max-processes' &&
  14.       $option_name !== '--max-urls'  )
  15.                      $command_line .= ' '.$option_name.' \''.$option_value.'\'';
  16.     }
  17.     // Comment this line can help to see error messages related to wrong paths
  18.     $command_line .= ' > /dev/null'.' 2>/dev/null'.' &';
  19.     //echo 'Executing '.$command_line."\n";
  20.     shell_exec($command_line);
  21.                 $nb_processes = $this->getNbProcesses($master_id);


Message édité par CyberDenix le 18-08-2011 à 11:28:25

---------------
Directeur Technique (CTO)
n°2096514
CyberDenix
Posté le 20-08-2011 à 13:41:51  profilanswer
 

Pour le getNbProcess, il suffit d'executer en shell_exec un
 

Code :
  1. ps aux | grep lenomdufichierphp | grep empreintedupere | grep -v grep | wc -l


 
Le wc -l te retourne le nombre de lignes, le grep -v grep te filtre la commande grep elle-même (qui parasite le résultat)
 
Fais un trim() sur le résultat et tu obtiens le nombre de fils qui sont actuellement en cours d'execution


Message édité par CyberDenix le 20-08-2011 à 13:42:38

---------------
Directeur Technique (CTO)
n°2096518
the_bigboo
Posté le 20-08-2011 à 14:02:16  profilanswer
 

Merci pour ce bout de code, cependant, ton démon diffère quelques peu du mien...
 
Pour commencer, mes processus ont une durée de vie non déterminée. Ils vivent à vitam éternam :)
 
Voilà pourquoi ils doivent pouvoir communiquer après avoir été créés.
Dans ton cas, tu leur donne le boulot à leur création, dans le mien, ils doivent pouvoir recevoir du boulot à n'importe quel moment de leur exécution.


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  PHP

  Démon PHP avec du fork :)

 

Sujets relatifs
[PHP, MYSQL] Réaliser un classement avec données de plusieurs tablesFlèches du clavier et jeu flash dans site PHP
Choix d'un framework PHP en 2011[PHP] Retrouver à la page précédente les données renseignées
[PHP] Question de n00b : comment ça coexiste le PHP et le JavaScript ?Ajax et session PHP erreur Incomplete Object
Communication PHP / C++ via un socket UNIX => blocage read/writeValeur d'un textbox en Html Php
Lister les fichiers d'un répertoire distant (sur un NAS) en PHP[SQL] Requète SQL complexe
Plus de sujets relatifs à : Démon PHP avec du fork :)


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR