Статьи

Настройка производительности PHP-приложений в Windows / IIS с выходным кэшированием

В этой статье я покажу вам, как повысить производительность приложений PHP в Windows / IIS, рассказав, что, когда и как использовать модуль IIS Output Caching. В качестве справочного чтения (или для сжатой версии моего поста) я предлагаю прочитать это: Настроить кэширование вывода IIS 7 (и я настоятельно рекомендую прочитать введение в эту статью).

Что такое кеширование вывода?

Модуль кэширования вывода IIS позволяет настроить IIS для кэширования динамических страниц, генерируемых PHP. Когда страница PHP становится «горячей», содержимое страницы кэшируется, так что она обслуживается без выполнения сценария, который ее генерирует. Очевидно, что это может оказать существенное влияние на производительность приложения, но это также означает, что вам необходимо убедиться, что кэширование вывода является правильным инструментом кэширования для вашего приложения (см. « Когда следует использовать кэширование вывода?» Ниже).

Примечание . Статическое содержимое (например, обычные HTML-страницы, изображения, таблицы стилей и т. Д.) По умолчанию автоматически кэшируется IIS.

С помощью модуля IIS Output Caching вы можете кэшировать все страницы, сгенерированные PHP, изменять то, что кэшируется значением параметра строки запроса, или то, что кэшируется значением заголовка. Какой из этих вариантов вы выберете, зависит от вашего приложения, что поднимает следующий вопрос …

Когда я должен использовать кеширование вывода?

При определении того, когда использовать кэширование вывода, важно помнить одну вещь: модуль кэширования вывода кэширует целые страницы, и когда страница обслуживается из кэша, ни один из его PHP-кода не выполняется . Вы можете контролировать, какие страницы кэшируются сначала по расширению страницы (например, .php), затем по параметру строки запроса и типу заголовка. Имея это в виду, вот мое высокоуровневое руководство о том, когда использовать кэширование вывода: используйте кэширование вывода, когда у вас есть целые страницы , содержание которых меняется не очень часто . (То, что «очень часто», будет зависеть от вашего приложения. Для некоторых приложений «10 минут» может быть «очень часто», а для других — 1 секунда.) Обратите внимание, что это включает страницы, которые запрашиваются в вызовах AJAX.

Если у вас есть страница с несколькими источниками данных, эти источники данных могут различаться по скорости их обновления, поэтому использование кэширования вывода может оказаться не лучшим решением. Однако если на странице много динамического контента, который обновляется отдельными вызовами AJAX, то некоторые из сценариев, запрашиваемых вызовами AJAX, могут быть отличными кандидатами для кэширования вывода. Если страницы, запрошенные вызовами AJAX, имеют разные параметры запроса, то вы, безусловно, можете повысить производительность своего приложения с помощью кэширования вывода. Вот почему я ограничусь этим сценарием: если вы настроите кэширование вывода для кэширования всех страниц с расширением .php, то все (или почти все) страницы вашего приложения могут быть кэшированы (что, вероятно, не то, что вы хотите). Если затем добавить, что страницы должны кэшироваться в соответствии с параметром строки запроса или значением заголовка,тогда все страницы PHP с указанным параметром строки запроса или значением заголовка могут быть кэшированы (опять же, вероятно, не то, что вы хотите). Но если параметр строки запроса является уникальным для данной страницы, то с помощью этой конфигурации вы можете быть уверены, какие страницы будут кэшироваться. Надеюсь, это станет понятнее, когда я проведу вас по настройке кэширования вывода (представьте мой тестовый скрипт ниже как скрипт, запрошенный вызовом AJAX).

Как настроить кэширование вывода?

Настроить IIS для использования кэширования вывода довольно просто — вы можете внести некоторые незначительные изменения в файл web.config вашего приложения вручную (что я покажу вам, как это сделать здесь) или использовать пользовательский интерфейс в IIS Manager, чтобы внести изменения , Однако, чтобы по-настоящему понять, как работает кэширование вывода, я посчитал полезным запустить некоторые тесты, когда внес изменения в свою конфигурацию. В следующих разделах я расскажу вам об изменениях конфигурации, которые я сделал, и (в большинстве случаев) покажу вам результаты тестов, которые я провел, чтобы вы могли понять, как работает кэширование вывода. Во всех моих тестах я использовал следующий очень простой PHP-скрипт для представления страницы, которая генерирует «полудинамический» контент.

<?php

$upperbound = isset($_GET['upperbound']) ? $_GET['upperbound'] : 100000000;

 

for($i = 0; $i < $upperbound; $i++)

{

    // loop to represent generation of "semi-dynamic" content

}

echo $upperbound; 

?>

Очевидно, что «настоящая» страница PHP будет генерировать некоторый интересный контент, но этого сценария будет достаточно для демонстрации кэширования вывода с помощью IIS. (Думайте об этом как о сценарии, вызываемом вызовом AJAX.) Если вы работаете вместе со мной в этой статье, поместите приведенный выше код в файл в каталоге wwwroot с именем dynamic_page.php .

Примечание . Все значения времени загрузки страницы, которые я показываю в этой статье, следует рассматривать только как примеры. Я сгенерировал время загрузки страницы на своем ноутбуке, который работает под управлением Windows 7 (x64), IIS 7.5 и PHP 5.3.4. Я не делал пользовательских оптимизаций. YMMV.

Я организовал вещи в соответствии с основными вопросами, которые возникли у меня …

Когда страница кэшируется?

По умолчанию при создании правила кэширования страница кэшируется, когда она запрашивается 2 раза в течение 10 секунд. Эти значения по умолчанию могут быть изменены. Однако я объясню, как изменить эти настройки в контексте примера. (Вы можете перейти к разделу « Изменение времени кэширования страницы » ниже, если не можете ждать.)

Когда страница из кэша просрочена?

После того, как страница кэширована, у вас есть три варианта определения, когда срок действия страницы истекает из кэша:

  1. Срок действия кэшированной страницы истекает, когда вносятся изменения в исходный код страницы (с помощью уведомлений об изменениях файлов ).
  2. Срок действия кэшированной страницы через предварительно определенный интервал времени (через интервалы времени (чч: мм: сс) ).
  3. Никогда не кэшируйте страницу в первую очередь (Запретите все кэширование).

Я буду использовать кэширование в пользовательском режиме и сосредоточусь на истечении срока хранения кэшированной страницы на основе уведомлений об изменениях файлов и временных интервалов. Хотя я думаю, что срок действия кэшированной страницы на основе изменений файла или временных интервалов довольно прост, я рассмотрю один пример, чтобы конкретизировать идею. Для этого я загружу свою динамическую страницу PHP ( dynamic_page.php ), выполнив следующий тестовый скрипт из командной строки:

<?php

function microtime_float() 

{ 

    list($usec, $sec) = explode(" ", microtime()); 

    return ((float)$usec + (float)$sec); 

} 

 

for($i = 1; $i <= 5; $i++)

{

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, "http://localhost/dynamic_page.php");

    $start_time = microtime_float();

    curl_exec($ch);

    $end_time = microtime_float();

    $time = round($end_time - $start_time, 4); 

    echo " ".$time."\n";

    curl_close($ch);

}

?>

Когда я запускаю приведенный выше скрипт без предварительного создания правила кэширования, вот что я получаю:

образ

Примечание . 100000000 — это выходной файл страницы dynamic_page.php, а время (время загрузки для dynamic_page.php ) — выходной файл cache_test.php .

Теперь давайте создадим правило кэширования и повторно запустим тест.

Создание правила кэширования уведомлений об изменении файла

Чтобы создать правило кэширования уведомлений об изменении файла, откройте (или создайте) файл web.config в корневом каталоге вашего сайта. Затем добавьте элемент <caching> в качестве дочернего элемента <system.webServer>, как показано здесь:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <system.webServer>

        <caching>

            <profiles>

                <add extension=".php" 

                     policy="CacheUntilChange" />

            </profiles>

        </caching>

    </system.webServer>

</configuration>

Примечание . Вы можете создавать правила кэширования с помощью дружественного пользовательского интерфейса в IIS Manager. Дополнительные сведения см. В разделе « Добавление правила кэширования вывода» (IIS 7).

Убедитесь, что вы сохранили файл web.config после того, как отредактировали его.

Теперь, когда я запускаю тест, вот что я получаю:

образ

Обратите внимание, что «динамическое содержимое» страницы было сгенерировано при первых двух загрузках страницы, но после этого страница обслуживалась из кэша.

Примечание . По умолчанию страница кэшируется, только если она запрашивается не менее 2 раз в течение 10 секунд. Чтобы узнать, как изменить эти значения по умолчанию, см. Раздел « Изменение при кэшировании страницы» ниже. Поскольку правило кэширования истекает из кэшированной страницы только после изменения исходного файла, последующие запросы на страницу будут обслуживаться из кэша. Чтобы увидеть это, перезапустите тест (все времена загрузки будут аналогичны последним трем выше).

Примечание : время уведомления об изменении файла также ограничено примерно 4 минутами. Другими словами, даже если вы не измените исходный код страницы и страница не будет запрошена в течение приблизительно 4 минут, она будет удалена из кэша (а затем снова кэширована при повторном запросе в течение 10 минут). Итак, если вы хотите, чтобы страница кэшировалась в течение более длительного периода времени (и вы не ожидаете изменения исходного кода), я бы предложил использовать правило кэширования временных интервалов (см. Ниже).

Чтобы увидеть, что страница не обслуживается из кэша после изменения исходного файла, добавьте комментарий к dynamic_page.php , сохраните его и перезапустите тест.

Создание правила кэша временного интервала

Если вы предпочитаете, чтобы срок действия страницы из кэша истек через определенный промежуток времени (вместо ожидания изменения исходного файла, как указано выше), добавьте элемент <caching> в файл web.config, как показано здесь:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <system.webServer>

        <caching>

            <profiles>

                <add extension=".php" 

                     policy="CacheForTimePeriod" 

                     duration="00:00:30" />

            </profiles>

        </caching>

    </system.webServer>

</configuration>

Эта политика кэширования истечет срок действия страницы из кэша через 30 секунд (измените значение атрибута duration по своему усмотрению). С этим правилом вы увидите результаты теста, аналогичные приведенным выше (с правилом изменения файла), но если вы перезапустите тест через 30 секунд, вы увидите, что страница не обслуживается из кэша. И, как с любым правилом кэширования, страница кэшируется только тогда, когда она запрашивается 2 раза в течение 10-секундного периода времени по умолчанию.

Изменение, когда страница кэшируется

По умолчанию при создании правила кэширования страница кэшируется, когда она запрашивается 2 раза в течение 10 секунд. Вы можете изменить эти значения по умолчанию, сначала внеся изменения в файл applicationHost.config, а затем обновив файл web.config . Ваш файл applicationHost.config обычно находится в каталоге c: \ Windows \ System32 \ inetsrv \ config и наиболее легко открывается с помощью Блокнота из командной строки с правами администратора. В этом файле найдите следующую запись (дочерний элемент элемента <sectionGroup name = «system.webServer»> ): <section name = «serverRuntime» overrideModeDefault = » Deny » /> .Изменить Deny наРазрешить и сохранить файл.

<sectionGroup name="system.webServer">

 

    <!-- other elements here -->

 

    <section name="serverRuntime" overrideModeDefault="Allow" />

 

    <!-- more elements here -->

 

</sectionGroup>

Теперь вы можете добавить следующий элемент в качестве дочернего элемента элемента <system.webServer> в вашем файле web.config : <serverRuntime частыйHitThreshold = «3» частыйHitTimePeriod = «00:00:20» /> .

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <system.webServer>

        <serverRuntime frequentHitThreshold="3" frequentHitTimePeriod="00:00:20" />

        <caching>

            <profiles>

                <add extension=".php" 

                     policy="CacheUntilChange"  

                     duration="00:00:30" />

            </profiles>

        </caching>

    </system.webServer>

</configuration>

Это будет кэшировать страницы после того, как они были запрошены 3 раза в течение 20 секунд. Очевидно, измените эти значения по своему усмотрению. (Элемент применяется как для изменения файла, так и для кэширования временных интервалов.)

Если вы хотите быть уверены, что страница никогда не кэшируется, добавьте следующий элемент <caching> в ваш файл web.config :

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <system.webServer>

        <caching>

            <profiles>

                <add extension=".php" 

                     policy="DisableCache" />

            </profiles>

        </caching>

    </system.webServer>

</configuration>

Кэширование на основе строковых переменных запроса и заголовков

Часто вам может потребоваться кэшировать страницу в зависимости от того, какие переменные или заголовки строки запроса отправляются в запросе. Например, при создании правила кэширования для моей страницы dynamic_page.php я хочу, чтобы запрос на http: //localhost/dynamic_page.php? Upperbound = 25000000 кэшировался отдельно от запроса на http: //localhost/dynamic_page.php? 50000000 . В приведенных выше примерах эти запросы обрабатываются как один и тот же запрос (потому что я не указал, что хочу кэширование на основе значений строки запроса), поэтому неверная страница может обслуживаться из кэша. Мы можем увидеть это, изменив наш тест ( cache_test.php) для отправки запросов с разными значениями строки запроса (обратите внимание, что запросы страниц теперь имеют разные значения строки запроса каждый раз в цикле):

    list($usec, $sec) = explode(" ", microtime()); 

    return ((float)$usec + (float)$sec); 

} 

 

for($i = 1; $i <= 5; $i++)

{

    $upperbound = 5000000*$i;

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, "http://localhost/dynamic_page.php?upperbound=$upperbound");

    $start_time = microtime_float();

    curl_exec($ch);

    $end_time = microtime_float();

    $time = round($end_time - $start_time, 4); 

    echo " ".$time."\n";

    curl_close($ch);

}

?>

Когда я запускаю этот тест, вот что я получаю:

образ

Как видите, переменные строки запроса игнорируются и неверная страница подается из кеша. К счастью, это легко исправить.

Создание правила кэширования на основе строковых переменных запроса

Чтобы убедиться, что страницы кэшируются на основе переменных строки запроса, выполните те же шаги, которые описаны выше для создания правила кэширования (либо правила уведомления об изменении файла, либо правила временного интервала), и добавьте одну пару атрибут / значение в элемент <add> : varByQueryString. = » переменная_имя « . Итак, в моем случае у меня есть файл web.config, например:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <system.webServer>

        <caching>

            <profiles>

                <add extension=".php" 

                     policy="CacheForTimePeriod"  

                     duration="00:00:30" 

                     varyByQueryString="upperbound" />

            </profiles>

        </caching>

    </system.webServer>

</configuration>

Теперь, когда я запускаю тест, я вижу это:

образ

Каждый запрос обрабатывался как отдельный запрос, поэтому ни одна из страниц не обслуживалась из кэша. Чтобы увидеть, что страницы на самом деле кэшируются, снова запустите тест в течение 30 секунд (так как это временной интервал для моего правила кэширования):

образ

Обратите внимание, что каждая страница обслуживалась из кэша.

Создание правила кэширования на основе заголовков

Точно так же, как вы хотите, чтобы страницы кэшировались в соответствии со значениями строки запроса, вы можете захотеть, чтобы они кэшировались в соответствии со значениями заголовка. И так же, как с переменными строки запроса, это не происходит по умолчанию. Если в правиле кэширования не указано, что значения страницы должны кэшироваться в соответствии со значениями заголовка, то значения заголовка не будут учитываться при кэшировании страницы. Опять же, исправить легко …

Чтобы убедиться, что страницы кэшируются по значению заголовка, выполните те же шаги, которые описаны выше для создания правила кэширования (либо правила уведомления об изменении файла, либо правила временного интервала), и добавьте одну пару атрибут / значение в элемент <add> : varByHeaders = » заголовок _name « . Итак, если вы хотите изменить кэширование в зависимости от значения заголовка Accept-Charset , ваш файл web.config должен выглядеть следующим образом:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <system.webServer>

        <caching>

            <profiles>

                <add extension=".php" 

                     policy="CacheForTimePeriod" 

                     duration="00:00:30" 

                     varyByHeaders="Accept-Charset" />

            </profiles>

        </caching>

    </system.webServer>

</configuration>

Чтобы продемонстрировать это правило в действии, я изменю свой тест, чтобы запросы страниц отправлялись с разными значениями для заголовка Accept-Charset для каждого запроса (обратите внимание, что я сохраняю значение строки запроса постоянным для каждого запроса):

<?php

function microtime_float() 

{ 

    list($usec, $sec) = explode(" ", microtime()); 

    return ((float)$usec + (float)$sec); 

} 

 

$charset = array('Accept-Charset: ISO-8859-1', 

                 "Accept-Charset: US-ASCII", 

                 "Accept-Charset: q=0.8", 

                 "Accept-Charset: UTF-8", 

                 "Accept-Charset: ISO-10646-UCS-2");

 

for($i = 1; $i <= 5; $i++)

{

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, "http://localhost/dynamic_page.php?upperbound=25000000");

    curl_setopt($ch, CURLOPT_HTTPHEADER, array($charset[$i - 1]));

    $start_time = microtime_float();

    curl_exec($ch);

    $end_time = microtime_float();

    $time = round($end_time - $start_time, 4); 

    echo " ".$time."\n";

    curl_close($ch);

}

?>

Выполнение вышеуказанного теста показывает, что страницы изначально не кэшируются, поскольку значения заголовков отличаются:

образ

Каждый запрос обрабатывался как отдельный запрос, поэтому ни одна из страниц не обслуживалась из кэша. Чтобы увидеть, что страницы на самом деле кэшируются, снова запустите тест в течение 30 секунд (так как это временной интервал для моего правила кэширования):

образ

Обратите внимание, что каждая страница обслуживается из кэша.

Кэширование на основе строковых переменных и заголовков запроса

Конечно, страницы могут быть кэшированы на основе значений строки запроса и значений заголовка. Например (используя мой скрипт dynamic_page.php ), если вы хотите, чтобы комбинация значения строки запроса ( upperbound ) и значения заголовка ( Accept-Charset ) определяла, кэшируется ли страница, ваш файл web.config будет выглядеть примерно так: этот:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

    <system.webServer>

        <caching>

            <profiles>

                <add extension=".php" 

                     policy="CacheForTimePeriod"  

                     duration="00:00:30" 

                     varyByHeaders="Accept-Charset" 

                     varyByQueryString="upperbound" />

            </profiles>

        </caching>

    </system.webServer>

</configuration>

Теперь страница будет кэшироваться только в том случае, если ее запрашивают с одним и тем же значением строки запроса и значением заголовка дважды в течение 10 секунд (если только вы не изменили значения ЧастотаHitThreshold и ЧастотаHitTimePeriod, как описано в разделе « Изменение, когда страница кэшируется » выше).

Резюме

Как вы можете видеть, использование кэширования вывода может значительно улучшить быстродействие PHP в IIS и, следовательно, повысить производительность вашего приложения PHP в Windows / IIS. Конечно, как я уже упоминал ранее, вы должны быть уверены, что кэширование вывода подходит для вашего приложения. Если это не так, вам понадобится инструмент кэширования, который даст вам более точный контроль над тем, что кэшируется (например, WinCache, о котором я расскажу в ближайшее время).

Для полной ссылки на элемент IIS <caching> см. Http://www.iis.net/ConfigReference/system.webServer/caching .

Спасибо.

-Брайан