Performances Zend framework

Eléphant du PHP | 245 Messages

22 févr. 2013, 10:59

j'aimerais optimisé au maximum les performances du ZF dans son architecture MVC.

j'ai sur un même serveur, fait des tests de montée en charge avec JMeter de 2 applications PHP
App 1 : développée avec un framework maison
App 2 : developpée avec ZF (v1.11)

La batterie de test consiste à charger 3 pages de l'app 1 et 3 pages de l'app 2
les pages choisies sont similaires en terme d'interactions avec les autre processus (pas de requêtes SQL ultra lourde , utilisation de Memcached ...) .
En fait, sachant ZF non forcément optimisé pour les performances, j'ai volontairement, charger des pages sur l'app1 (framework maison) avec plus d'accès aux bases de données pour laisser une petite chance au ZF d'être plus rapide (j'ai fait de la discrimination positive en quelque sorte)

Malheureusement, les tests sont clairs, mon app ZF pète beaucoup plus rapidement sous le fait d'une montée en charge.
j'ai fait les tests sur un serveur de dev, ce qui est intéressant sont plus les comparatifs que les chiffres brutes
Sur des montées en charges sur 1 minutes, je compte les erreurs de type "PHP Fatal error: Maximum execution time of 30 seconds exceeded"
60 utilisateurs app1 : 0, app 2 (zf) :0
120 utilisateurs app1 : 0, app 2 (zf): 30
180 utilisateurs app1 : 0, app 2 (zf) : 350
300 utilisateurs app1 : 50, app 2 (zf): >1000

Pour le ZF, ces erreurs ne se produisent pas au niveau des modèles, mais au niveau des librairies Zend, ce qui montre en engorgement de l'application au niveau de bootstrap

Plusieurs choses sont possiles pour booster les performances du ZF


1. Au niveau des accès base de données (serveur MySQL dans notre exemple) ,qui sont les causes de baisse de performance les plus courantes
Une petite analyse des logs de MySQL avec par exemple un outil comme jetprofiler montre que dans le top 10 des requêtes apparaissent des "DESCRIBE TABLE", mais pas de panique, les développeurs du ZF y ont pensé et on peut mettre en cache les données des tables en ajoutant une méthode d'initialisation dans la classe du bootstrap, comme ceci :
protected function _initMetadataCache() {
		$frontendOptions = array(
		    'lifetime' =>500,
		    'automatic_serialization' => true,
		    //'debug_header' => true,
		);
		$backendOptions  = array(
		    'lifetime' => 500,
		    'cache_dir' => APPLICATION_PATH .'/../cache/metadata'
		);
		$cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);
		//Zend_Registry::set('MetadataCache', $cache);
		Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);
		//return $cache;
	}

2. Au niveau du chargement des classes, c'est bien pratique d'utiliser l'autoloader de Zend, mais on peut l'optimiser :

2.a. En précisant dans le .htaccess de l'application l'include_path PHP pour celle ci

Code : Tout sélectionner

php_value include_path "/dir/library/Zend"
Dans mon exemple, je préfère utiliser le .htaccess que le php.ini car, l'app Zend n'est as la seule à tourner.

2.b On charge l'autoloader de Zend le pus tôt possible dans l'application, par exemple comme ceci :
//redéfinition de l'autoloader par défaut afin d'optimiser le chargement :
include 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setDefaultAutoloader(create_function('$class',	"include str_replace('_', '/', \$class) . '.php';"	));
Et on met en commentaires tous les appels "require_once" de la librairie Zend.



Après ces optimisations, le gain de performances est notable :
60 utilisateurs app1 : 0, app 2 (zf) :0
120 utilisateurs app1 : 0, app 2 (zf): 2
180 utilisateurs app1 : 0, app 2 (zf) : 175
300 utilisateurs app1 : 50, app 2 (zf): >1000

Mais l'appli du framework maison résiste quand même bien mieux à la montée en charge.
C'est normal étant plus simple, elle moins scalable , mais gère moins d'exception, charge moins de ressources,...
Au niveau du développement, évidemment, je préfère me replonger dans du code Zend que dans le code de l'appli maison, après tout c'est pour cela qu'on utilise un framework tel que Zend.
Cependant, je me demandait si quelqu'un aurait encore quelques conseils éclairés pour optimiser une application Zend.
Donc si quelqu'un a des pistes ...
merci :D

Mammouth du PHP | 1029 Messages

04 mars 2013, 19:57

Je dirais en se séparant des deux librairies les plus gourmandes :

Zend_DB et Zend_Date.

Tu peux facilement les remplacer.

Tu peux aussi précharger les classes les plus utiliser.

http://framework.zend.com/manual/1.12/f ... ce.example
L'expérience est la somme de toutes nos erreurs.

Mammouth du PHP | 571 Messages

05 mars 2013, 01:45

le point le plus important dans l'optimisation d'un projet zend framework(valables aussi pour d'autre framework PHP) passe indéniablement par la mise en place d'un cache d'OPCodes(APC) en compilant toutes les sources de Zend framework et si besoin les sources de son application. Ce qui implique qu'à chaque requête http d'un internaute, les sources de zend sont directement interprétées et directement accessibles non plus depuis le disque mais depuis la mémoire.Le serveur se trouve débarrasser des tâches rébarbatives( compilation, inclusion des fichiers) ce qui constitue quand même un gain énorme en terme de performance.

Eléphant du PHP | 245 Messages

05 mars 2013, 17:24

merci pour vos réponses.
je n'utilise pas Zend_date, par contre j'utilise fortement Zend_Db, mais à priori, l'outil de performance base de données que j'utilise n'indique pas de requête ou de connexions lentes depuis l'application Zend. Les erreurs sont dispersées sur l'ensemble des classes de la librairie Zend, il n'y en n'a pas une à priori qui est plus coûteuse que les autres.
Je vais tout de même regarder la doc que Maitrepylos m'a montré.
Mais pour ce qui est du chargement de classes de librairies externes à Zend ou de modèles, j'ai déjà une function d'initialisation dans le bootstrap :
protected function _initAutoload()
	{
		$moduleLoader = new Zend_Application_Module_Autoloader(array(
			'namespace' => '',
			'basePath' => APPLICATION_PATH.'')
		);
		//echo 'app path = '.APPLICATION_PATH;exit;



		return $moduleLoader;
	}
Du coup, cela prend déjà en charge les classes dont j'ai besoin, hors librairie Zend.

je vais regarder la compilation des ressources avec APC proposé par yann18 qui m'a l'air aussi très intéressante.

Sinon, j'utilise dans le bootstrap, Zend_Config_Ini, peut être gagnerais-je en utilisant Zend_Config_Php
plutôt? Surtout que j'ai une dizaine de fichier INI dont un contenant des réécriture d'URL qui est un peu gros (82 ko contre <1 ko pour les autres fichiers).
Qu'en pensez vous ?
peut être même peut-on utiliser Zend_Config_Php avec des fichiers de config compilés dans le cache APC ?