Zend FR

Consultez la FAQ sur le ZF avant de poster une question

Vous n'êtes pas identifié.

#1 05-05-2007 09:44:50

Killing Joke
Membre
Date d'inscription: 05-05-2007
Messages: 11

[ZF][0.9.2] Impossible d'avoir plusieurs visiteurs en simultanée

Bonjour,

Je ne sais pas si quelqu'un aurait une idée sur le problème suivant, ni même si ce problème est propre au Zend Framework.

J'ai un petit site entièrement développé en MVC avec ZF (passé aujourd'hui en 0.9.2 pour voir si c'était pareil ou non), et je rencontre ce comportement particulièrement curieux (que je n'ai jamais vu ailleurs) : certaines pages ont un traitement "long" de plusieurs secondes, et pendant la génération de ces pages "longues", plus aucune autre page ne peut être rendue à l'utilisateur ! Autrement dit, si un visiteur 1 déclenche une de ces pages qui n'est pas instantannée, plus aucun autre visiteur ne pourra dans l'intervalle avoir une page aussi basique que la page d'accueil - ce qui est plutôt curieux et gênant !

Je précise que :
- j'ai fait des tests hors Zend avec une page sleep.php contenant un sleep(60) pour tester : çà fonctionne bien, je peux avoir 3 pages sleep.php en attente de chargement, une autre page nosleep.php est générée automatiquement ;
- le problème se pose à l'identique en développement chez moi (Ubuntu Apache 2 à priori configuré plutôt "haut" (MaxRequestsPerChilds, MaxServers, etc.), et en production (chez Infomaniak, sur un serveur sur lequel je n'ai évidemment pas la main au niveau configuration) ;
- j'ai essayé de désactiver un certain nombre de choses qui pourraient être des "ressources mutuellement exclusives" mais jusqu'ici rien n'y a fait (virer les logs en bases de données, virer les logs fichiers, virer toutes les logs, virer les méthodes qui analysait l'espace mémoire occupé (avec des exec()), virer tous les accès bdd en écriture pour voir - toujours pareil) ;


J'ai d'autres pistes à explorer (le traitement long est une lecture à distance de pages sur d'autres sites via CURL, il faut que je vois si çà ne pourrait pas venir de là ...), mais si quelqu'un avait déjà rencontré un problème de ce genre çà m'intèresse (je précise que ce n'est peut être pas lié au ZF en fait, je n'en sais rien à ce stade !).

Merci.

A tout hasard je vous mets mon index.php actuel et ma classe Contexte qui gère les accès logs, etc.


index.php : (plusieurs choses commentées pour tests actuels genre "sans les logs")

Code:

<?php

  // ===== Chargement automatique des classes sans devoir faire 
  function __autoload($class) {
      Zend_Loader :: loadClass($class);
  }

  // ===== Includes standards : controller, routeur, configuration, utilitaires
  require_once 'Zend/Loader.php';       // Point d'entrée ZF
  require_once 'Contexte.php';          // Gestion des logs, des paramètres, etc. 
  require_once 'update/index.php';      // Le site est-il en cours de mise à jour 
  require_once 'configuration/configuration.inc.php';   // Lecture des paramètres => seront recopiées en contexte

  // ===== Chargement du comparateur
  Contexte :: loadConfiguration($config);               // Recopie des paramètres

  // ===== On interdit l'interruption de la page à l'utilisateur si cette fonction est disponible
  if ( SET_TIME_LIMIT_ALLOWED ) {
    // set_time_limit(300);
  }

  // ===== Création du/des logger
  /*
  $params = array(
    'host'     => Contexte :: $database['DB_SERVER'],
    'username' => Contexte :: $database['DB_USER'],
    'password' => Contexte :: $database['DB_PASSWORD'],
    'dbname'   => Contexte :: $database['DB_NAME']
  );
  $db_logs =& Zend_Db::factory(Contexte :: $database['DB_TYPE'], $params);
  Zend_Registry :: set('db', $db);
  */

  // $logger =& new Zend_Log_Adapter_Null();
  /*
  $logger =& new Zend_Log_Adapter_File(Contexte :: $paths['logs'] . Contexte :: $logs['main_log_file_name']);
  $logger->setOption('format', Contexte :: $logs['file_log_format']);
  Zend_Log :: registerLogger($logger, Contexte :: $logs['file_logger_tag']);
  */

  /*
  $logAdapter =& new Zend_Log_Adapter_Db($db_logs, Contexte :: $tables['logs']);
  $logAdapter->setOption('fieldMessage', 'LOG_MESSAGE');
  $logAdapter->setOption('fieldLevel', 'LOG_LEVEL');
  Zend_Log :: registerLogger($logAdapter, Contexte :: $logs['db_logger_tag']);
  */

  // ===== Gestion des routes par chargement depuis un fichier de configuration
  $router =& new Zend_Controller_Router_Rewrite();
  $router->addConfig(new Zend_Config_Ini(Contexte :: $paths['configuration'] . 'routes.ini', null), 'routes');

  // ===== Création du controleur Zend
  $controller =& Zend_Controller_Front :: getInstance();
  $controller->setRouter($router);
  $controller->setControllerDirectory(Contexte :: $paths['controllers']);
  $controller->setBaseUrl(ROOT_URL);
  $controller->returnResponse(true);
  // $controller->throwExceptions(true); // A désactiver en production !

  // ===== Création de la vue
  Zend_Loader :: loadClass('Zend_View');
  $view =& new Zend_View;
  $view->setScriptPath(Contexte :: $paths['views']);
  $view->addHelperPath(Contexte :: $paths['helpers']);

  // ===== Enregistrement dans la session courante des objets à propager
  Zend_Registry :: set('view', $view);
  Zend_Registry :: set('router', $router);

  // ===== Démarrage session $_SESSION via Zend_Session
  Zend_Session_Core::start();        

  // ==== Gestion des erreurs non gérées et des exceptions
  /*
  function exception_handler($exception) {
    echo "Exception non attrapée : " , $exception->getMessage(), "<br />";
  }

  function error_handler($severity, $message, $filename, $lineno) {
     echo "Erreur : $severity<br />";
     echo "Message : $message<br />";
     echo "file : $filename, $lineno<br />";
  }

  set_error_handler('error_handler');
  set_exception_handler('exception_handler');
  */

  Contexte :: debug("URI=" . $_SERVER["REQUEST_URI"]);
  Contexte :: loaded(); // Fin du chargement du Contexte

  // ===== Affichage de la page
  $response =& $controller->dispatch();
  if (isset($response) ) {
      if ( $response->isException() ) {
          $exceptions = $response->getException();
          foreach ($exceptions as $exception) {
              Contexte ::error($exception->getMessage());
          }
          if ( DEBUG ) {
              Zend :: dump($exceptions);
          }
      }
      echo $response;
  }
?>

Contexte.php :

Code:

<?php

final class Contexte
{
    // Définition des propriétés sous forme de variables statiques, accessibles depuis Contexte :: $paths, etc.
    static $phpExtension = '.php';
    static $paths = array();
    static $tables = array();
    static $database = array();
    static $logs = array();
    static $render = array();
    static $UTF8_decode = false;
    
    static $logger = null;

    static $errors = array();

    // Gestion des erreurs (pile d'erreurs générées par une page stockées en local pour accès ultérieur)
    public function addError($error) {
        self :: $errors[] = $error;
    }

    public function hasErrors() {
       return ( count(self :: $errors) > 0 );
    }

    // Lecture des paramètres et stockage en local 
    static public function loadConfiguration($config) {

        // On remonte une exception si une variable a été déclarée en configuration mais pas en tant que variable statique ici même
        // TODO voir comment mettre en place un ménisme générique (sans avoir à déclarer les variables ici)
        foreach ($config as $config_name => $config_value) {
            if ( $config_name == 'paths' ) {
                self:: $paths = $config_value;
            } else if ( $config_name == 'tables' ) {
                self:: $tables = $config_value;
            } else if ( $config_name == 'database' ) {
                self:: $database = $config_value;
            } else if ( $config_name == 'phpExtension' ) {
                self:: $phpExtension = $config_value;
            } else if ( $config_name == 'logs' ) {
                self:: $logs = $config_value;
            } else if ( $config_name == 'render' ) {
                self:: $render = $config_value;
            } else if ( $config_name == 'UTF8_decode' ) {
                self :: $UTF8_decode = $config_value;
            } else {
                throw new Exception("Erreur lecture d'une propriété non déclarée : '$config_name'");
            }
        }

        // ===== Gestion des includes : ajout du répertoire des modèles pour loadClass facilité
        $current_path = self:: $paths['models'].PATH_SEPARATOR.get_include_path();
        if ( ! ereg('.' . PATH_SEPARATOR, $current_path) ) {
          $current_path = '.'.PATH_SEPARATOR . $current_path;
        }
        set_include_path($current_path);
    }
    
    // Méthode retournant le logger courant
    static public function getLogger() {
        if ( self :: $logger == null ) {
            self :: $logger = new Zend_Log();
        }
        return self :: $logger;
    }

    // Decodage UTF8 si besoin est
    static public function utf8_decode($s) {
        if ( self :: $UTF8_decode === true ) {
            $s = utf8_decode($s);
        }
        return $s;
    }

    // Encodage UTF8 si besoin est
    static public function utf8_encode($s) {
        if ( self :: $UTF8_decode === true ) {
            $s = utf8_encode($s);
        }
        return $s;
    }

    // Chargement d'une interface
    static public function loadInterface($interface, $dirs = null) {

        // autodiscover the path from the class name
        $path = str_replace('_', DIRECTORY_SEPARATOR, $interface);
        if ($dirs === null && $path != $interface) {
            // use the autodiscovered path
            $dirs = dirname($path);
            $file = basename($path) . '.php';
        } else {
            $file = $interface . '.php';
        }

        Zend_Loader :: loadFile($file, $dirs, true);
    }

    // Chargement d'une librairie externe
    static public function loadLibrary($library, $dirs = null) {
      $file = $library . '.inc';
      Zend_Loader :: loadFile($file . self:: $phpExtension, 'lib/');
    }

    // Méthode à appeler lorsque la classe Comparateur a fini d'être chargée
    static function loaded() {
        self :: debug('Path de recherche = "' . get_include_path() . '"');
    }

    // Méthode de debug - à réécrire avec getLogger()
    static function debug($s) {
        /*
        if ( DEBUG && Contexte :: $logs['log_to_file'] ) {
            $params = array('date' => date(self:: $logs['log_date_format']), 'mylevel' => 'D');
            self :: getLogger()->debug($s, Zend_Log::LEVEL_DEBUG, $params, self :: $logs['file_logger_tag']);
        }
        if ( DEBUG && Contexte :: $logs['log_to_db'] ) {
            self :: getLogger()->debug($s, Zend_Log::LEVEL_DEBUG, self :: $logs['db_logger_tag']);
        }
        */
    }

    static function info($s) {
        /*
        if ( Contexte :: $logs['log_to_file'] ) {
            $params = array('date' => date(self:: $logs['log_date_format']), 'mylevel' => 'I');
            self :: getLogger()->debug($s, Zend_Log::LEVEL_INFO, $params, self:: $logs['file_logger_tag']);
        }
        if ( Contexte :: $logs['log_to_db'] ) {
            Zend_Log :: log($s, Zend_Log::LEVEL_INFO, self:: $logs['db_logger_tag']);
        }
        */
    }

    static function warn($s) {
        /*
        if ( Contexte :: $logs['log_to_file'] ) {
            $params = array('date' => date(self:: $logs['log_date_format']), 'mylevel' => 'W');
            Zend_Log :: log($s, Zend_Log::LEVEL_WARNING, $params, self:: $logs['file_logger_tag']);
        }
        if ( Contexte :: $logs['log_to_db'] ) {
            Zend_Log :: log($s, Zend_Log::LEVEL_WARNING, self:: $logs['db_logger_tag']);
        }
        */
    }

    static function error($s) {
        /*
        if ( Contexte :: $logs['log_to_file'] ) {
            $params = array('date' => date(self:: $logs['log_date_format']), 'mylevel' => 'E');
            Zend_Log :: log($s, Zend_Log::LEVEL_ERROR, $params, self:: $logs['file_logger_tag']);
        }
        if ( Contexte :: $logs['log_to_db'] ) {
            Zend_Log :: log($s, Zend_Log::LEVEL_ERROR, self:: $logs['db_logger_tag']);
        }
        */
    }
}

Dernière modification par Killing Joke (05-05-2007 09:47:36)

Hors ligne

 

#2 05-05-2007 10:37:33

Killing Joke
Membre
Date d'inscription: 05-05-2007
Messages: 11

Re: [ZF][0.9.2] Impossible d'avoir plusieurs visiteurs en simultanée

Bon ben effectivement après nouvelles investigations je me réponds à moi même, çà vient de cURL.
J'ai viré cURL pour voir en remettant des filegetcontents à la place, je n'ai plus le problème.
Du coup c'est curieux, cURL étant réputé pour être efficace et pour pouvoir gérer facilement des connexions en parallèle. Je vais creuser voir si je ne trouve pas la cause réelle du problème sur cURL (qui est quand même super pratique et puissant à utiliser).

Donc rien à voir avec le ZF !

hmm

Hors ligne

 

#3 05-05-2007 14:07:33

Julien
Membre
Date d'inscription: 16-03-2007
Messages: 501

Re: [ZF][0.9.2] Impossible d'avoir plusieurs visiteurs en simultanée

cURL possède des timeouts qu'il faut configurer, sinon il va utiliser le processus Apache à 100% sans laisser de place pour le reste, dans ses phases d'attente.
Petite question, pourquoi utilises-tu tes objets par référence avec un =& , alors qu'en PHP5, ce sont de toute façon des références qui sont retournées, au niveau objet ?

Hors ligne

 

#4 09-05-2007 23:37:06

Killing Joke
Membre
Date d'inscription: 05-05-2007
Messages: 11

Re: [ZF][0.9.2] Impossible d'avoir plusieurs visiteurs en simultanée

Julien a écrit:

cURL possède des timeouts qu'il faut configurer, sinon il va utiliser le processus Apache à 100% sans laisser de place pour le reste, dans ses phases d'attente.
Petite question, pourquoi utilises-tu tes objets par référence avec un =& , alors qu'en PHP5, ce sont de toute façon des références qui sont retournées, au niveau objet ?

Salut,
Oui effectivement je configurais déjà tous les TIMEOUT de CURL (timeout sur "connexion impossible au bout de x secondes" et timeout sur "page non chargée au bout de x secondes").
Par contre ce n'est toujours pas çà, je suis en train de tout réécrire en multi_curl (qui marche d'ailleurs vraiment bien en soi d'ailleurs), mais j'ai toujours mon problème de "figeage" du serveur (qui maintenant accepte 2 connections en //, mais la 3e n'est pas rendue tant qu'une des 2 précédentes n'est pas terminée, ce qui reste curieux).

Pour les =& ...
- depuis PHP4 / PHP5 je m'embrouille franchement un peu sur les passages par références en PHP (surtout que le comportement avait même me semble t'il évolué sur une version de PHP4) ; il va falloir que je me reclarifie tout çà ;
- en général je ne fais quasiment pas de passage par pointeur en PHP ; toutefois, sur ce petit site, j'ai eu de gros problèmes de consommation mémoire, qui m'ont obligé à me repencher sur le problème ; il y a sans doute quelques erreurs dans mon code (qu'il faudra que j'investigue), mais je n'ai plus aucun problème de débordements mémoires (et ma lecture de plusieurs pages de plusieurs centaines de ko avec création de parsers pour analyse des dites pages et extraction d'infos utiles ne me pose plus de problèmes).

Hors ligne

 

Pied de page des forums

Propulsé par PunBB
© Copyright 2002–2005 Rickard Andersson
Traduction par punbb.fr

Graphisme réalisé par l'agence Rodolphe Eveilleau
Développement par Kitpages