por qué symfony2 es tan rápido
DESCRIPTION
Mi charla en deSymfony 2013 explicando la cache de aplicaciones de Symfony que es uno de los elementos que más contribuyen a su velocidad.TRANSCRIPT
Por qué Symfony2 es tan rápido Carlos Granados
Carlos Granados
Por qué Symfony2 es tan rápido Carlos Granados
Por qué Symfony2 es tan rápido Carlos Granados
Por qué Symfony2 es tan rápido Carlos Granados
Por qué Symfony2 es tan rápido Carlos Granados
• Crear el Kernel y leer la petición• Leer configuración• Compilar y cargar el contenedor
de dependencias• Aplicar la configuración de
seguridad• Leer todas las rutas definidas• Decidir qué ruta aplicar• Ejecutar al controlador apropiado• Leer la configuración de las
entidades • Conectarse con la base de datos• Consultar la base de datos
• Convertir los resultados de la base de datos en entidades
• Construir las rutas que podamos necesitar
• Encontrar qué plantilla twig usar• Compilar la plantilla twig• Ejecutar la plantilla twig• Convertir el resultado de la
plantilla twig en una respuesta• Devolver esa respuesta al cliente• Autocargar todas las clases
implicadas
¿Qué hace Symfony cuando recibe una petición?
Por qué Symfony2 es tan rápido Carlos Granados
¿Para cuándo dice que lo quiere?
Por qué Symfony2 es tan rápido Carlos Granados
• Crear el Kernel y leer la petición• Leer configuración• Compilar y cargar el contenedor
de dependencias• Aplicar la configuración de
seguridad• Leer todas las rutas definidas• Decidir qué ruta aplicar• Ejecutar el controlador apropiado• Leer la configuración de las
entidades • Conectarse con la base de datos• Consultar la base de datos
• Convertir los resultados de la base de datos en entidades
• Construir las rutas que podamos necesitar
• Encontrar qué plantilla twig usar• Compilar la plantilla twig• Ejecutar la plantilla twig• Convertir el resultado de la
plantilla twig en una respuesta• Devolver esa respuesta al cliente• Autocargar todas las clases
implicadas
Muchos de estos procesos están cacheados
Por qué Symfony2 es tan rápido Carlos Granados
No hablamos del caché HTTP
“La mejor petición es aquella que nunca llega a nuestro servidor”
Por qué Symfony2 es tan rápido Carlos Granados
Hablamos del caché de aplicación
app/cache
Por qué Symfony2 es tan rápido Carlos Granados
app.php
Entornos y depuración
$kernel = new AppKernel('prod', false);
app_dev.php
$kernel = new AppKernel(‘dev', true);
Entorno debug
Por qué Symfony2 es tan rápido Carlos Granados
config_dev.ph
web_profiler: toolbar: true
El entorno se usa para saber qué fichero de configuración usar
config_prod.ph
web_profiler: toolbar: false
Y también qué subdirectorio de app/cache utilizar
app/cache/prodapp/cache/dev
Por qué Symfony2 es tan rápido Carlos Granados
El valor de debug es el que realmente distingue un entorno de depuración de uno de producción
%kernel.debug%
- Si se muestran los errores o no (en Symfony 2.3 está separado)
- Si se actualiza la caché en cada ejecución
Por qué Symfony2 es tan rápido Carlos Granados
Normalmente ‘prod’ = no debug y ‘dev’=debug
Pero NO tiene por qué ser así
Podemos, por ejemplo, crear un ‘app_staging.php’ con la configuración del entorno de producción pero con debug=true
$kernel = new AppKernel('prod', true);
Por qué Symfony2 es tan rápido Carlos Granados
APC
“Sin APC Symfony no sería posible”
Por qué Symfony2 es tan rápido Carlos Granados
APC: evitar que compruebe si tiene que recompilar ficheros
apc.stat=0 en php.ini
Al subir nuevo código hay que vaciar el caché APC:
• Reiniciar el servidor• Crear un script que borre esta caché y
ejecutarlo (con una llamada http)
Por qué Symfony2 es tan rápido Carlos Granados
Autoload: como encuentra las clases el autoloader
if (isset($this->prefixes[$first])) { foreach ($this->prefixes[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { if (file_exists($dir . DIRECTORY_SEPARATOR . $classPath)) { return $dir . DIRECTORY_SEPARATOR . $classPath; } } } }}
vendor/composer/ClassLoader.php
Por qué Symfony2 es tan rápido Carlos Granados
Autoload: crear un classmap con Composer
php composer.phar dump-autoload --optimize
vendor/composer/autoload_classmap.php
…'Acme\\DemoBundle\\AcmeDemoBundle' => $baseDir . '/src/Acme/DemoBundle/AcmeDemoBundle.php','Acme\\DemoBundle\\Controller\\DemoController' => $baseDir . '/src/Acme/DemoBundle/Controller/DemoController.php','Acme\\DemoBundle\\Controller\\SecuredController' => $baseDir . '/src/Acme/DemoBundle/Controller/SecuredController.php','Acme\\DemoBundle\\Controller\\WelcomeController' => $baseDir . '/src/Acme/DemoBundle/Controller/WelcomeController.php',…
Por qué Symfony2 es tan rápido Carlos Granados
Autoload: utilizar ApcClassLoader
web/app.php
// Use APC for autoloading to improve performance// Change 'sf2' by the prefix you want in order to prevent key conflict with another application $loader = new ApcClassLoader('sf2', $loader);$loader->register(true);
Por qué Symfony2 es tan rápido Carlos Granados
Bootstrap
app/bootstrap.php.cache
• Construido por Composer durante la instalación
• Se incluye en los front controllers• Contiene las clases básicas necesarias para
inicializar el entorno Symfony: ContainerAwareInterface, Container, Kernel, ClassCollectionLoader, ApcClassLoader, Bundle, ConfigCache, HttpKernel
Por qué Symfony2 es tan rápido Carlos Granados
Classes.php
app/cache/prod/classes.php
• Se construye a partir de un mapa de clases: classes.map
• En modo debug se comprueba cada clase del mapa para ver si ha cambiado y si es así se reconstruye el fichero classes.php
• En modo no debug sólo se construye classes.php si no existe
Por qué Symfony2 es tan rápido Carlos Granados
Classes.map
app/cache/prod/classes.map
Contiene las clases que cada bundle decida incluir para optimizar
$this->addClassesToCompile(array( 'Twig_Environment', 'Twig_Extension', 'Twig_Extension_Core', 'Twig_Extension_Escaper', 'Twig_Extension_Optimizer', 'Twig_LoaderInterface', 'Twig_Markup', 'Twig_Template', ));
Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php
Por qué Symfony2 es tan rápido Carlos Granados
Classes.map
app/cache/prod/classes.map
Podemos añadir clases de nuestros bundles
public function load(array $configs, ContainerBuilder $container){
…$this->addClassesToCompile(array(
'Acme\DemoBundle\Twig\Extension\DemoExtension', 'Acme\DemoBundle\Twig\EventListener\
ControllerListener', ));
}
Acme/DemoBundle/DependencyInjection/AcmeDemoExtension.php
Por qué Symfony2 es tan rápido Carlos Granados
Contenedor de dependencias
app/cache/prod/appProdProjectContainer.php
• Lo construye el compilador del contenedor de dependencias
• En modo debug se comprueba un fichero appDevDebugProjectContainer.php.meta generado al compilar. Si alguna de las clases incluidas ha cambiado, regeneramos el contenedor
• En modo no debug sólo se construye el contenedor si no existe
Por qué Symfony2 es tan rápido Carlos Granados
Contenedor de dependencias
…/** * Gets the 'doctrine' service. * * This service is shared. * This method always returns the same instance of the service. * * @return Doctrine\Bundle\DoctrineBundle\Registry A Doctrine\Bundle\DoctrineBundle\Registry instance. */protected function getDoctrineService(){ return $this->services['doctrine'] = new \Doctrine\Bundle\DoctrineBundle\Registry($this, array(
'default' => 'doctrine.dbal.default_connection'), array('default' => 'doctrine.orm.default_entity_manager'),'default', 'default');
}…
app/cache/prod/appProdProjectContainer.php
Por qué Symfony2 es tan rápido Carlos Granados
Enrutador
app/cache/prod/appProdUrlMatcher.php
• Lo construye el componente de enrutación• En modo debug se comprueba un fichero
appDevUrlMatcher.php.meta generado por el router. Si alguno de los recursos (ficheros yml, controladores, etc…) incluidos ha cambiado, regeneramos el enrutador
• En modo no debug sólo se construye el enrutador si no existe
Por qué Symfony2 es tan rápido Carlos Granados
if (0 === strpos($pathinfo, '/_')) { // _wdt if (0 === strpos($pathinfo, '/_wdt') && preg_match('#^/_wdt/(?P<token>[^/]++)$#s', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, array('_route' => '_wdt')), array ('_controller'
=>'web_profiler.controller.profiler:toolbarAction',)); }
app/cache/prod/appProdUrlMatcher.php
Enrutador
Es importante que las rutas más usadas estén al principio:
• Definirlas antes en los ficheros yml• Definirlas antes en los controladores si usamos anotaciones
Por qué Symfony2 es tan rápido Carlos Granados
# app/config/config_prod.ymlparameters: router.options.matcher.cache_class: ~ # disable router cache router.options.matcher_class: Symfony\Component\Routing\Matcher\ApacheUrlMatcher
Usar el Enrutador de Apache
$ php app/console router:dump-apache -e=prod --no-debug
# skip "real" requestsRewriteCond %{REQUEST_FILENAME} -fRewriteRule .* - [QSA,L] # helloRewriteCond %{REQUEST_URI} ^/hello/([^/]+?)$RewriteRule .* app.php [QSA,L,E=_ROUTING__route:hello,E=_ROUTING_name:%1,E=_ROUTING__controller:AcmeDemoBundle\:Demo\:hello]
// web/app.php…use Symfony\Component\HttpFoundation\ApacheRequest;…$kernel->handle(ApacheRequest::createFromGlobals())->send();
Por qué Symfony2 es tan rápido Carlos Granados
static private $declaredRoutes = array(
'_demo' => array ( 0 => array ( ), 1 => array ( '_controller' => 'Acme\\DemoBundle\\Controller\\DemoController::indexAction', ), 2 => array ( ), 3 => array ( 0 => array ( 0 => 'text', 1 => '/demo/', ), ), 4 => array ( ),),
'_demo_hello' => array ( 0 => array ( 0 => 'name', ), 1 => array ( '_controller' => 'Acme\\DemoBundle\\Controller\\DemoController::helloAction', ), 2 => array ( ), 3 => array ( 0 => array ( 0 => 'variable', 1 => '/', 2 => '[^/]++', 3 => 'name', ), 1 => array ( 0 => 'text', 1 => '/demo/hello', ), ), 4 => array ( ),),…
app/cache/prod/appProdUrlGenerator.php
Enrutador: generador de rutas
Por qué Symfony2 es tan rápido Carlos Granados
Plantillas
app/cache/prod/templates.php
• Lo construye un caché warmer del componente de plantillas
• En modo debug no se construye• Hay un directorio app/cache/prod/twig donde
están las plantillas compiladas. En modo no debug sólo se genera cada plantilla si no existe.
• La estructura del directorio Twig es un poco rara. Puede ser dificil encontrar la plantilla compilada. Usar una búsqueda global por ficheros con el nombre de la plantilla.
Por qué Symfony2 es tan rápido Carlos Granados
return array (… 'AcmeDemoBundle:Demo:index.html.twig' => '/Z:/stack/src/Acme/DemoBundle/Resources/views/Demo/index.html.twig', 'AcmeDemoBundle:Demo:hello.html.twig' => '/Z:/stack/src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig', '::base.html.twig' =>
'/Z:/stack/app/Resources/views/base.html.twig',);
app/cache/prod/templates.phpPlantillas
// line 5public function block_content($context, array $blocks = array()){ // line 6 echo " <h1>Hello "; echo twig_escape_filter($this->env, (isset($context["name"]) ?
$context["name"] : null), "html", null, true); echo "!</h1>";}
Por qué Symfony2 es tan rápido Carlos Granados
Otros elementos en el cache
• Anotaciones• Traducciones• Assetic• Doctrine• Etc…
Muchos de estos elementos se crean con un cache warmer.
Por qué Symfony2 es tan rápido Carlos Granados
Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface
Crear un cache warmer para tu bundle
public function warmUp($cacheDir){ // Creamos el contenido a guardar el el cache $content = ...; // Lo guardamos en el cache $this->writeCacheFile($cacheDir.'/filename.php', $content);}
public function warmUp($cacheDir); public function isOptional();
Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer
Por qué Symfony2 es tan rápido Carlos Granados
• Query cache• Metadata cache• Result cache
Cache de Doctrine
doctrine: orm: metadata_cache_driver: apc query_cache_driver: type: service id: my_doctrine_common_cache_service result_cache_driver: type: memcache host: localhost port: 11211 instance_class: Memcache…
Por qué Symfony2 es tan rápido Carlos Granados
• Hacer un cache:clear (con warmup) cada vez que instalemos en producción
• apc.stat=0 en php.ini
• Crear un classmap con Composer
• Activar ApcClassLoader
• Listar las clases que queremos añadir a classes.map en nuestro bundle
• Ordenar las rutas para que las más usadas aparezcan antes
• Usar el enrutador de Apache
• Crear cache warmers con ficheros inicializados por nuestro bundle
Conclusiones
Por qué Symfony2 es tan rápido Carlos Granados
Por qué Symfony2 es tan rápido Carlos Granados
¡¡Gracias!!
• [email protected]• @carlos_granados• http://es.linkedin.com/in/carlosgranados
¿Preguntas?
• https://joind.in/8852