Сегодня мы поговорим о кэшировании. У Symfony есть много встроенных стратегий кэширования. Например, конфигурационные YAML-файлы сначала конвертируются в PHP и затем кэшируются в файловой системе. Мы так же видели, что модули, созданные с помощью генератора админки, кэшируются для улучшения производительности.
Но сегодня мы будем говорить о другом кэше: кэш HTML. Для улучшения производительности Вашего веб-сайта, вы можете кэшировать целые страницы HTML или только их части.
Создание нового окружения
По умолчанию функциональность кэширования шаблонов Symfony включена в
конфигурационном файле settings.yml
для окружения prod
, но не для
test
и dev
:
prod: .settings: cache: true dev: .settings: cache: false test: .settings: cache: false
Поскольку нам нужно протестировать функциональность кэша перед выпуском, мы можем
активировать кэш для окружения dev
или создать новое окружение.
Напомним, что окружение определяется его именем (строка), связанным фронт-контроллером
и опционально набором специальных значений конфигурации.
Для того, чтобы попробовать систему кэширования в Jobeet, мы создадим окружение cache
,
подобное окружению prod
, но с журналированием и информацией для отладки, доступной
в окружении dev
.
Создайте контроллер, связанный с новым окружением cache
скопировав dev
контроллер
web/frontend_dev.php
в web/frontend_cache.php
:
// web/frontend_cache.php if (!in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) { die('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.'); } require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php'); $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'cache', true); sfContext::createInstance($configuration)->dispatch();
Это все, что нужно сделать. Теперь мы можем использовать новое окружение cache
. Единственное
отличие - это второй аргумент для метода getApplicationConfiguration()
,
где указывается имя окружения - cache
.
Вы можете протестировать окружение cache
в вашем браузере с помощью вызова его контроллера:
http://www.jobeet.com.localhost/frontend_cache.php/
note
Скрипт контроллера начинается с кода, который проверяет, что он вызван с локального IP-адреса. Это меры безопасности для защиты от вызова контроллера на промышленных серверах. Мы поговорим об этом более детально в завтрашнем уроке.
Сейчас окружение cache
наследует конфигурацию по умолчаюнию. Измените
конфигурационный файл settings.yml
для добавления специфичной конфигурации для окружения cache
:
# apps/frontend/config/settings.yml cache: .settings: error_reporting: <?php echo (E_ALL | E_STRICT)."\n" ?> web_debug: true cache: true etag: false
В этих настройках, была включена функциональность кэша шаблонов с помощью
настройки cache
и веб-панель отладки - с помощью настройки web_debug
.
Поскольку конфигурация по умолчанию сохраняет все настройки в кэше, Вам нужно очистить его перед тем, как Вы увидите изменения в Вашем браузере:
$ php symfony cc
Теперь, если Вы обновите страницу в Вашем браузере, должна появиться веб-панель отладки в
верхнем-правом углу страницы, как это было для окружения dev
.
Конфигурация кэша
Кэш шаблонов Symfony может быть настроен в конфигурационном файле cache.yml
.
Конфигурацию по умолчанию для приложения можно найти в apps/frontend/config/cache.yml
:
default: enabled: false with_layout: false lifetime: 86400
По умолчанию, поскольку все страницы могут содержать динамическую информацию,
кэш глобально отключен (enabled: false
). Нам не нужно менять эту настройку,
поскольку мы будем включать кэш на постраничной базе.
Настройка lifetime
определяет время жизни кэша в секундах
(86400
соответствует одним суткам) на стороне сервера.
tip
Вы можете это сделать другим способом: включить кэш глобально и затем отключить его на специальных страницах, которые не могут быть кэшированы. Это зависит от того где меньше работы для Вашего приложения.
Кэш страницы
Поскольку домашняя страница Jobeet возможно будет наиболее посещаемой страницей веб-сайта, вместо запроса данных из базы данных каждый раз, когда пользователь приходит на нее, она будет кэширована.
Создайте файл cache.yml
для модуля sfJobeetJob
:
# plugins/sfJobeetPlugin/modules/sfJobeetJob/config/cache.yml index: enabled: true with_layout: true
tip
Конфигурационный файл cache.yml
имеет те же свойства что и любой другой
файл конфигурации как например view.yml
. Это означает что вы например
можете включить кэш для всех действий модуля используя специальный ключ all
.
Если Вы обновите страницу в браузере, Вы увидите что Symfony украсил страницу рамкой показывая что содержимое было кэшировано:
Рамка дает некоторую ценную информацию о кэше для отладки, как например время жизни кэша и его возраст.
Если Вы обновите страницу еще раз, цвет рами будет изменен с зеленого на желтый, указывая на то что страница была получена из кэша:
Также заметьте, что не было сделано запросов к базе данных во втором случае, как показано на отладочной веб-панели.
tip
Даже если язык будет изменен пользователем, кэш все еще работает, так как язык включен в URL.
Когда страница может быть кэширована, и если кэш еще не существует, Symfony сохраняет объект ответа в кэше в конце запроса. Для всех будущих запросов, Symfony будет отсылать кэшированый ответ без вызова контроллера:
Это сильно влияет на производительность и Вы можете самостоятельно измерить это, используя такие инструменты, как например JMeter.
note
Входящий запрос с параметрами GET
или отосланный с помощью методов POST
,
PUT
, или DELETE
не кэшируется Symfony, независимо от конфигурации.
Страница создания работы также может быть кэширована:
# plugins/sfJobeetPlugin/modules/sfJobeetJob/config/cache.yml new: enabled: true index: enabled: true all: with_layout: true
Поскольку две страницы могут быть кэшированы с шаблоном, мы создали секцию all
,
которая определяет конфигурацию по умолчанию для всех действий модуля sfJobeetJob
.
Очистка кэша
Если вы хотите очистить кэш страницы, вы можете использовать задачу cache:clear
:
$ php symfony cc
Задача cache:clear
очищает все кэши Symfony, сохраненные в главной папке
cache/
. Она также принимает опции для выборочной очистки некоторых частей кэша.
Для очистки только кэша шаблонов для окружения cache
, используйте опции
--type
и --env
:
$ php symfony cc --type=template --env=cache
Вместо очистки кэша каждый раз, когда вы делаете изменения, вы можете так же отключить кэш с помощью добавления любой строки запроса в URL, или используя кнопку "Игнорировать кэш" из отладочной веб-панели:
Кэширование действия
Иногда Вы не можете сохранить всю страницу в кэш, но шаблон действия сам по себе может быть кэширован. Попробуйте другой вариант, Вы можете кэшировать все, кроме шаблона.
Для приложения Jobeet, мы не можем кэшировать всю страницу из-за фрагмента "история вакансий".
Измените конфигурацию кэша для модуля job
следующим образом:
# plugins/sfJobeetPlugin/modules/sfJobeetJob/config/cache.yml new: enabled: true index: enabled: true all: with_layout: false
Изменив настройку with_layout
на false
, Вы отключили кэширование шаблона.
Очистите кэш:
$ php symfony cc
Обновите страницу в Вашем браузере, чтобы увидеть отличия:
Несмотря на то, что поток выполнения запроса выглядит очень похоже на упрощенной диаграмме, кэширование без шаблона намного более ресурсоемко.
Кэширование фрагментов и компонентов
Для очень динамичных веб-сайтов иногда невозможно кэшировать весь шаблон действия. В этом случае, Вам нужно сконфигурировать кэш на более низком уровне. Фрагменты и компоненты также могут быть кэшированы.
Давайте кэшируем компонент language
, создав файл cache.yml
для модуля sfJobeetLanguage
:
# plugins/sfJobeetPlugin/modules/sfJobeetLanguage/config/cache.yml _language: enabled: true
Конфигурирование кэша для фрагмента или компонента достигается простым добавлением записи
с его именем в файл конфигурации. Опция with_layout
не берется в расчет для этого типа кэша, поскольку в
этом нет смысла:
Формы в кэше
Сохранение страницы создания вакансии в кэш проблематично, поскольку она содержит форму. Для лучшего понимания проблемы, сходите на страницу "Post a Job" в Вашем браузере для создания кэша. Затем очистите куки сессии, и попробуйте сохранить вакансию. Вы должны увидеть сообщение об ошибке - "CSRF атака":
Почему? Поскольку мы настроили секретный CSRF, когда создавали приложение frontend, Symfony вкладывает ключ CSRF во все формы. Для защиты Вашего приложения от CSRF атак, этот ключ уникален для данного пользователя и для данной формы.
Первый раз, когда страница отображается, сгенерированная HTML-форма сохраняется в кэш с ключом текущего пользователя. Когда следом заходит другой пользователь, будет отображена страница из кэша с CSRF-ключом первого пользователя. Когда форма будет отослана, ключ не совпадет и появится ошибка.
Как мы можем это исправить, поскольку кэширование формы выглядит разумным? Форма создания вакансии не зависит от пользователя, и она ничего не меняет для текущего пользователя. В этом случае CSRF-защита не нужна, и мы можем совсем удалить ключ CSRF:
// plugins/sfJobeetPlugin/lib/form/doctrine/PluginJobeetJobForm.class.php abstract PluginJobeetJobForm extends BaseJobeetJobForm { public function configure() { $this->disableLocalCSRFProtection(); } }
После этого изменения, очистите кэш и попробуйте тот же самый сценарий, чтобы увидеть, что он теперь работает, как ожидалось.
Та же конфигурация должна быть применена для формы смены языка сайта, которая содержится в шаблоне
и будет сохранена в кэше. Поскольку использована sfLanguageForm
по умолчанию,
вместо создания нового класса можно просто удалить CSRF-ключ, давайте сделаем это для
действия и компонента модуля sfJobeetLanguage
:
// plugins/sfJobeetPlugin/modules/sfJobeetLanguage/actions/components.class.php class sfJobeetLanguageComponents extends sfComponents { public function executeLanguage(sfWebRequest $request) { $this->form = new sfFormLanguage($this->getUser(), array('languages' => array('en', 'fr'))); $this->form->disableLocalCSRFProtection(); } } // plugins/sfJobeetPlugin/modules/sfJobeetLanguage/actions/actions.class.php class sfJobeetLanguageActions extends sfActions { public function executeChangeLanguage(sfWebRequest $request) { $form = new sfFormLanguage($this->getUser(), array('languages' => array('en', 'fr'))); $form->disableLocalCSRFProtection(); // ... } }
The disableLocalCSRFProtection()
method disables the CSRF token for this form.
Удаление кэша
Каждый раз, когда пользователь добавляет и активирует вакансию, должен быть обновлен список новых вакансий на домашней странице.
Поскольку нам не нужно, чтобы вакансии появлялись на домашней странице в реальном времени, лучшая стратегия - это уменьшить время жизни кэша:
# plugins/sfJobeetPlugin/modules/sfJobeetJob/config/cache.yml index: enabled: true lifetime: 600
В конфигурации по умолчанию вместо одного дня кэш для домашней страницы будет автоматически удаляться каждые десять минут.
Но если Вы хотите обновлять домашнюю страницу сразу после того, как пользователь активирует
новую вакансию, измените метод executePublish()
модуля sfJobeetJob
и добавьте ручную очистку кэша:
// plugins/sfJobeetPlugin/modules/sfJobeetJob/actions/actions.class.php public function executePublish(sfWebRequest $request) { $request->checkCSRFProtection(); $job = $this->getRoute()->getObject(); $job->publish(); if ($cache = $this->getContext()->getViewCacheManager()) { $cache->remove('sfJobeetJob/index?sf_culture=*'); $cache->remove('sfJobeetCategory/show?id='.$job->getJobeetCategory()->getId()); } $this->getUser()->setFlash('notice', sprintf('Your job is now online for %s days.', sfConfig::get('app_active_days'))); $this->redirect($this->generateUrl('job_show_user', $job)); }
Кэш управляется классом sfViewCacheManager
. Метод remove()
удаляет кэш,
связанный в внутренним URI. Для удаления кэша для всех возможных параметров
переменной, используйте *
как значение. Мы использовали sf_culture=*
в коде выше, что означает, что Symfony будет удалять кэш для домашней
страницы и на английском, и на французском.
Поскольку менеджер кэша равен null
, когда кэш отключен, мы заключили удаление кэша в
блок if
.
Тестирование кэша
Перед тем, как мы начнем, нам нужно изменить конфигурацию для окружения test
,
чтобы включить слой кэширования:
# apps/frontend/config/settings.yml test: .settings: error_reporting: <?php echo ((E_ALL | E_STRICT) ^ E_NOTICE)."\n" ?> cache: true web_debug: false etag: false
Давайте протестируем страницу создания вакансии:
// test/functional/frontend/jobActionsTest.php $browser-> info(' 7 - Job creation page')-> get('/fr/')-> with('view_cache')->isCached(true, false)-> createJob(array('category_id' => Doctrine_Core::getTable('CategoryTranslation')->findOneBySlug('programming')->getId()), true)-> get('/fr/')-> with('view_cache')->isCached(true, false)-> with('response')->checkElement('.category_programming .more_jobs', '/23/') ;
Тестер view_cache
использован для тестирования кэша. Метод isCached()
принимает
два булевых значения:
- Должна ли страница быть в кэше
- Должен ли кэш быть с шаблоном
tip
Даже со всеми инструментами, предоставленными фреймворком функционального тестирования,
иногда легче найти проблемы с помощью браузера. Это очень легко сделать.
Просто создайте контроллер для окружения test
. Журнал который хранится в
log/frontend_test.log
может также оказаться полезным.
Увидимся завтра!
Как и большинство другой функциональности Symfony, подфреймворк кэширования очень гибкий и позволяет разработчику настроить кэш на очень низком уровне.
Завтра мы поговорим о последнем шаге в жизненном цикле приложения: развертывании на промышленных серверах.
This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.