Кешування браузера Nginx. Налаштування кешування статики за допомогою nginx у Debian Nginx вимкнути кешування для сесії

Головна / Корисна інформація
|

Чим швидше завантажується сайт, тим більше шансів утримати відвідувачів. Сайти з великою кількістюзображень та інтерактивного контенту, який запускається сценаріями у фоновому режимі, дуже складно швидко завантажити. Процес завантаження такого сайту складається з безлічі запитів до різних файлів. Що менше таких запитів надсилає сервер, то швидше завантажиться сайт.

Прискорити завантаження сайту можна різними способами, і кешування браузера – один із найважливіших. Це дозволяє браузеру повторно використовувати локальні копії раніше завантажених файлів. Для цього потрібно ввести нові заголовки відповіді HTTP.

У цьому допоможе модуль header веб-сервера Nginx. Цей модуль може додавати довільні заголовки у відповідь, але його основна роль полягає у визначенні заголовків кешування. У цьому посібнику показано, як використовувати модуль header для налаштування кешування браузера.

Вимоги

  • Сервер Ubuntu 16.04 (про налаштування сервера можна дізнатися).
  • Користувач із доступом до команди sudo.
  • Попередньо встановлений веб-сервер Nginx (посібник із встановлення – ).
  • Модуль map (інструкції з налаштування цього модуля – ).

1: Створення тестових файлів

Для початку створіть кілька тестових файлів у стандартному каталозі Nginx. Надалі ці файли можна використовувати для перевірки кешування браузера.

Щоб визначити тип файлу, що передається по мережі, Nginx не аналізує його контент (це було б занадто повільно), натомість він дивиться на розширення файлу, щоб визначити його тип MIME, який визначає ціль файлу.

Тому зовсім неважливо, що будуть містити тестові файли. Просто виберіть для них відповідні імена та розширення, і Nginx сприйматиме порожній файл як зображення або таблицю стилів.

У каталозі Nginx за замовчуванням створіть файл test.html за допомогою truncate. Як видно з розширення це буде HTML-файл.

sudo truncate -s 1k /var/www/html/test.html

Таким же чином створіть ще кілька тестових файлів з розширеннями jpg(зображення), css (таблиця стилів) та js (JavaScript):

sudo truncate -s 1k /var/www/html/test.jpg
sudo truncate -s 1k /var/www/html/test.css
sudo truncate -s 1k /var/www/html/test.js

2: Перевірка стандартної поведінки Nginx

За промовчанням всі файли кешуються однаково. Щоб перевірити це, використовуйте тестовий HTML-файл.

Надішліть запит до test.html з локального сервера Nginx і перегляньте заголовки відповіді:

Ця команда поверне такий заголовок відповіді:

HTTP/1.1 200 OK

Дата: Sat, 10 Sep 2016 13:12:26 GMT
Content-Type: text/html
Content-Length: 1024

Connection: keep-alive
ETag: "57d40685-400"
Accept-Ranges: bytes

У виділеному червоному рядку ви бачите заголовок ETag, який містить унікальний ідентифікатор цього перегляду запитуваного файлу. Якщо ви повторно запустите команду curl, ви побачите таке саме значення ETag.

Браузер зберігає значення ETag і відправляє його назад на сервер у заголовку відповіді If-None-Match за необхідності повторно запитати файл (наприклад, при оновленні сторінки).

Ви можете симулювати цю поведінку за допомогою наступної команди:

curl -I -H "If-None-Match: "57d40685-400"" http://localhost/test.html

Примітка: Вкажіть значення ETag замість 57f6257c-400.

Тепер команда поверне:

HTTP/1.1 304 Not Modified
Server: nginx/1.10.0 (Ubuntu)
Дата: Sat, 10 Sep 2016 13:20:31 GMT
Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT
Connection: keep-alive
ETag: "57d40685-400"

На цей раз Nginx поверне 304 Not Modified. Веб-сервер не пересилатиме файл знову, він просто повідомить браузеру про те, що він може повторно використовувати завантажений раніше файл.

Це зменшує мережевий трафік, але цього недостатньо для досягнення високої продуктивності кешування. Проблема ETag полягає в тому, що браузер завжди повинен надсилати серверу запит, щоб повторно використати свій кешований файл. У відповідь сервер замість файлу відправляє 304, але ця процедура все ще триває багато часу.

3: Налаштування заголовків Cache-Control та Expires

Крім ETag існує ще два заголовки відповіді для керування кешування: Cache-Control і Expires. Cache-Control - новий заголовок, він має більше функцій, ніж Expires, і в цілому більш корисний в налаштуванні кешування.

Ці заголовки повідомляють браузеру, що запитуваний файл можна зберігати локально протягом певного періоду(В тому числі і завжди) і при цьому не вимагати його знову. Якщо ці заголовки не налаштовані, браузер буде змушений постійно запитувати файли сервера і чекати відповіді 200 OK або 304 Not Modified.

Ці HTTP-заголовки можна настроїти за допомогою модуля header. Модуль Header вбудований в Nginx, отже, його не потрібно встановлювати.

Щоб додати цей модуль, відкрийте файл віртуального хоста Nginx за промовчанням у текстовому редакторі:

sudo nano /etc/nginx/sites-available/default

Знайдіть блок server:

. . .

#
server (
listen 80 default_server;

. . .

Помістіть у файл два нові розділи: один перед блоком server (налаштування тривалості кешування файлів різних типів), а другий усередині цього блоку (налаштування заголовків кешування).

. . .
# Default server configuration
#
# Expires map
map $sent_http_content_type $expires (
default off;
text/html epoch;
text/css max;
application/javascript max;
~image/max;
}
server (
listen 80 default_server;
listen [::]:80 default_server;
expires $ expiras;
. . .

Розділ перед блоком server – це новий блок map, який визначає відповідності між типом файлу та періодом зберігання його в кеші.

  • off — значення за замовчуванням, яке не додає заголовків для керування кешуванням. Це запобіжний захід для контенту, до кешування якого немає певних вимог.
  • text/html має значення epoch. Це спеціальне значення, яке відключає кешування, внаслідок чого браузер завжди буде вимагати актуальний стан сайту.
  • text/css (таблиці стилів) та application/javascript (файли Javascript) мають значення max. Це означає, що браузер кешуватиме ці файли протягом максимально можливого періоду часу, значно зменшуючи кількість запитів (враховуючи, що таких файлів зазвичай багато).
  • ~image/ — регулярний вираз, який шукає всі файли з MIME-типом image/ (наприклад, image/jpg та image/png). Воно також має значення max, оскільки зображень, як таблиць стилів, на сайтах багато. Кешуючи їх, браузер зменшить кількість запитів.

Директива expires (включена в модуль headers) налаштовує заголовки керування кешуванням. Вона використовує значення змінної $expires, вказаної в блоці map, завдяки чому заголовки відповіді відрізняються залежно від типу файлу.

Збережіть та закрийте файл.

Щоб оновити налаштування, перезапустіть Nginx:

sudo systemctl restart nginx

4: Тестування кешування браузера

Виконайте той самий запит, що й на початку керівництва:

curl -I http://localhost/test.html

Час відповіді відрізнятиметься. У висновку ви побачите два нові заголовки відповіді

HTTP/1.1 200 OK
Server: nginx/1.10.0 (Ubuntu)
Date: Sat, 10 Sep 2016 13:48:53 GMT
Content-Type: text/html
Content-Length: 1024
Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT
Connection: keep-alive
ETag: "57d40685-400"
Expires: Thu, 01 Jan 1970 00:00:01 GMT
Cache-Control: no-cache
Accept-Ranges: bytes

Заголовок Expires показує дату в минулому, а Cache-Control має значення no-cache, що означає, що браузер повинен постійно запитувати актуальну версіюфайлу (за допомогою заголовка ETag).

Запросіть інший файл:

curl -I http://localhost/test.jpg
HTTP/1.1 200 OK
Server: nginx/1.10.0 (Ubuntu)
Date: Sat, 10 Sep 2016 13:50:41 GMT
Content-Type: image/jpeg
Content-Length: 1024
Last-Modified: Sat, 10 Sep 2016 13:11:36 GMT
Connection: keep-alive
ETag: "57d40688-400"
Expires: Thu, 31 Dec 2037 23:55:55 GMT
Cache-Control: max-age=315360000
Accept-Ranges: bytes

Як бачите, результат відрізняється. Expires містить дату в далекому майбутньому, а Cache-Control має значення max-age, яке повідомляє браузеру, як довго він може кешувати файл (у секундах). В даному випадкубраузер кешуватиме завантажений файл максимально довго, так що надалі для завантаження цього зображення браузер використовуватиме локальний кеш.

Спробуйте надіслати запити до файлів test.js та test.css, ви повинні отримати схожий результат.

З виводу команди curl видно, що кешування браузера налаштовано успішно. Кешування сторінок збільшить продуктивність сайту та зменшить кількість запитів.

Примітка: Даний посібник пропонує зручні налаштуваннякешування, які підійдуть до середньостатистичного сайту. Щоб збільшити продуктивність свого сайту, проаналізуйте його контент та налаштуйте параметри кешування, виходячи з того, яких файлів більше на вашому сайті.

Wordpress далеко не найпродуктивніша платформа для ведення блогів, і великі сайти зазвичай використовують кешування для прискорення його роботи. Для wordpress, є багато популярних доповнень, що реалізують кешування, але всі вони на мій погляд досить ускладнені, і, як правило, вимагають або установки додаткового програмного забезпечення, Такого як, наприклад, Varnish або memcached, або перекладають кешування на плечі PHP який теж продуктивним не назвеш. У цьому пості я розповім як налаштувати кешування wordpress засобами nginx, без встановлення додаткового ПЗ.

У nginx є FastCGI модуль, який надає директиви, що дозволяють кешувати відповідь від fastcgi. Використання цього модуля позбавляє нас необхідності використовувати сторонні засоби кешування. Модуль також дозволяє нам не кешувати частину ресурсів спираючись на різні параметри запиту, такі як, наприклад: тип (GET, POST), куки, адреса сторінки та інші. Сам модуль вміє лише додавати в кеш, але не вміє його очищати або видаляти окремі записи з нього. Без очищення кешу при додаванні, редагуванні та додаванні коментаря до посту кеш не буде оновлюватися, і зроблені зміни будуть видні тільки з великою затримкою, тому для очищення кешу ми будемо використовувати сторонній модуль nginx - nginx_cache_purge.

Налаштування nginx

У більшості сучасних дистрибутивів nginx вже зібраний з модулем ngx_cache_purge, але про всяк випадок перевіримо, що він присутній. У консолі виконаємо:

nginx -V 2>& 1 | grep -o nginx-cache-purge

Якщо після виконання команди ви бачите nginx-cache-purge, то можна продовжувати. Якщо після виконання команди нічого не з'явилося, то у вас певно якийсь із старих дистрибутивів ubuntu, в якому nginx зібраний без підтримки цього модуля. В даному випадку необхідно перевстановити nginx із стороннього ppa:

sudo add-apt-repository ppa:rtcamp/nginx sudo apt-get update sudo apt-get remove nginx* sudo apt-get install nginx-custom

Налаштуємо nginx. Відкриємо файл з налаштуваннями віртуального хоста, і приведемо його до такого вмісту:

fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:100m inactive=60m; fastcgi_cache_key " $scheme$request_method$host$request_uri"; fastcgi_cache_use_stale error timeout invalid_header http_500; fastcgi_ignore_headers Cache-Control Expires Set-Cookie; # Upstream to abstract backend connection(s) for php upstream php ( server unix:/var/run/php5-fpm.sock fail_timeout=0 ; ) server ( listen 80 ; server_name .example.com ; root /var/www/example.com/html ; index index.php ; error_log /var/www/example.com/log/error.log; access_log /var/www/example.com/log/access.log; set $skip_cache 0; # POST requests and urls with query string should always go to PHP if ($request_method = POST) ( set $skip_cache 1 ; ) if ($query_string != "") ( set $skip_cache 1 ; ) # Don't cache uris containing the following segments if ($request_uri ~ * "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml")(set $skip_cache 1;) # Don't use the cache for logged in users or recent commenters if ($http_cookie~* "comment_author|wordpress_+|wp-postpass|wordpress_no_cache|wordpress_logged_in")( set $skip_cache 1 ; ) location / ( try_files $uri $uri/ /index.php? $args ; ) location ~ \.php$ ( try_files $uri = 404 ; include fastcgi_params ; fastcgi_pass php ; fastcgi_cache skip_cache ; fastcgi_cache WORDPRESS ; fastcgi_cache_valid 60m ; ) location ~ /purge(/.*) ( allow 127 .0.0.1 ; $scheme$request_method$host$1"; ) location ~ * ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff | xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ (access_log off; log_not_found off; expires max;) location ~ /\. ( deny all ; access_log off ; log_not_found off ; ) location = / favicon.ico ( log_not_found off ; access_log off ; ) location = / robots.txt ( allow all ; log_not_found off ; access_log off ; # Deny access to uploads that aren’t images, videos, music, etc. location ~ * ^/wp-content/uploads/.*.(html|htm|shtml|php|js|swf)$ ( deny all ; ) # Deny public access to wp-config.php location ~ * wp-config.php ( deny all ; ) )

Зрозуміло, параметри root, server_name, access_log, error_log необхідно виправити відповідно до того, як у вас все налаштовано. У рядку fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:100m inactive=60m; ми говоримо nginx, що зберігати кеш потрібно в директорії /var/run/nginx-cache/, зону пам'яті називаємо WORDPRESS, максимальний розмір кешу встановлюємо в 100 мегабайт і таймер скидання через неактивність встановлюємо в 60 хвилин. Приємним бонусом подібної конфігурації є те, що якщо з якихось причин наш PHP бекенд припиняє працювати, nginx продовжить віддавати закешовані сторінки.

Налаштування Wordpress

Сам nginx не знає, коли потрібно очищати кеш, тому необхідно встановити додаток для wordpress, який автоматично очищатиме кеш після змін. Ставимо доповнення nginx -t nginx: configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful

І якщо все добре – перезавантажуємо його:

systemctl reload nginx

Заходимо на будь-яку сторінку, і перевіряємо, що nginx додав її в кеш:

ls -la /var/run/nginx-cache

Додатково: поміщаємо кеш nginx у ramdisk (tmpfs)

Зараз у нас налаштовано кешування, але кеш зберігається на жорсткому диску, що не є гарним рішення. Оптимальніше буде змонтувати директорію з кешем nginx на згадку. Для цього відкриємо /etc/fstab і додамо туди:

tmpfs /var/run/nginx-cache tmpfs nodev,nosuid,size= 110M 0 0

Якщо в налаштуваннях nginx ви вказали більший розмір кешу, то параметр size необхідно змінити відповідно до вказаного розміру кешу плюс невеликий запас.
Відразу змонтуємо директорію на згадку:

Тепер при кожному завантаженні системи директорія /var/run/nginx-cache буде поміщатися в пам'ять, що має зменшити час віддачі сторінки.

Висновок

Не варто розраховувати на даний спосібяк на панацею. Інтерпретатор PHP, як я вже вище писав, не можна назвати швидким, та й сам Wordpress досить великий і "важкий". Якщо сторінки немає в кеші, або вона рідко запитується, то nginx все одно спочатку буде звертатися до продуктивного бекенду. Проте в цілому, кешування дасть можливість вашому серверу трохи розслабитися на генерації популярних постів і в моменти публікації нового посту.

Це далеко не єдиний спосіб прискорити ваш wordpress блог. Існує ще безліч інших технік, про які я напишу пізніше.

У web-сервер і reverse-proxy nginx вбудовані дуже потужні можливості для кешування HTTP-відповідей. Однак у ряді випадків документації та прикладів не вистачає, в результаті не все виходить так легко та просто, як хотілося б. Наприклад, мої конфіги nginx-а місцями написані кров'ю. Цією статтею я спробую трохи покращити ситуацію.

У цій статті: а) підводне каміння при повносторінковому кешуванні; б) кешування із ротацією; в) створення динамічного вікна в закешованій сторінці.

Я припускатиму, що ви використовуєте зв'язку nginx+fastcgi_php. Якщо ви застосовуєте nginx+apache+mod_php, просто замініть імена директив із fastcgi_cache* на proxy_cache*

Якщо вибрати, чи кешувати сторінку на стороні PHP або на стороні nginx, я вибираю nginx. По-перше, це дозволяє віддавати 5-10 тис. запитів за секунду без жодних складнощів і без розумних розмов про «високе навантаження». По-друге, nginx самостійно стежить за розміром кеша і чистить його як при старінні, так і при витісненні даних, що не часто використовуються.

Кешування всієї сторінки повністю

Якщо на вашому сайті головна сторінка хоч і генерується динамічно, але змінюється досить рідко, можна сильно знизити навантаження на сервер, закешувавши її в nginx. За високої відвідуваності навіть кешування на короткий термін (5 хвилин і менше) вже дає величезний приріст у продуктивності, адже кеш працює дуже швидко. Навіть закешувавши сторінку всього на 30 секунд, ви все одно досягнете значного розвантаження сервера, зберігши при цьому динамічність оновлення даних (у багатьох випадках оновлення раз на 30 секунд цілком достатньо).

Наприклад, закешувати головну сторінку можна так:

Fastcgi_cache_path /var/cache/nginx levels=keys_zone=wholepage:50m; ... server ( ... location / ( ... fastcgi_pass 127.0.0.1:9000; ... # Включаємо кешування і ретельно вибираємо ключ кеша. fastcgi_cache wholepage; fastcgi_cache_valid 200 301 302 304 5m;_mod |$http_if_none_match|$host|$request_uri"; # Гарантуємо, що різні користувачі не отримають одну і ту ж сесійну Cookie. fastcgi_hide_header "Set-Cookie"; PHP.fastcgi_ignore_headers "Cache-Control" "Expires";))

Я не сильно перебільшу, якщо скажу, що кожен рядок у цьому конфізі написано кров'ю. Тут багато підводного каміння, давайте їх все розглянемо.

fastcgi_cache_path: простота налагодження теж важлива

fastcgi_cache_path /var/cache/nginx levels=keys_zone=wholepage:50m;

У директиві fastcgi_cache_path я виставляю порожнє значення для levels. Хоча це трохи знижує продуктивність (файли будуть створюватися безпосередньо в /var/cache/nginx, без розбиття по директоріям), зате на порядок полегшує налагодження та діагностику проблем з кешем. Повірте, вам ще не раз доведеться залазити руками в /var/cache/nginx і дивитися, що там зберігається.

fastcgi_cache_valid: кешуємо код відповіді 304 теж

fastcgi_cache_valid 200301302304 5m;

У директиві fastcgi_cache_valid ми змушуємо кешувати як стандартні коди 200 ОК, 301 Moved Permanently і 302 Found, але й 304 Not Modified. Чому? Згадаймо, що означає 304. Він видається з порожнім тілом відповіді у двох випадках:

  • Якщо браузер надіслав заголовок "If-Modified-Since: date", в якому date більше або дорівнює значенню заголовка відповіді "Last-Modified: date". Тобто. клієнт запитує: «Чи є Нова версіяз моменту date? Якщо ні, поверни мені 304 і заощади трафік. Якщо є, дай мені тіло сторінки».
  • Якщо браузер надіслав заголовок "If-None-Match: hash", де hash збігається зі значенням заголовка відповіді "ETag: hash". Тобто. клієнт запитує: «Чи відрізняється поточна версіясторінки від тієї, що я запросив минулого разу? Якщо ні, поверни мені 304 і заощади трафік. Якщо так, то віддай тіло сторінки».

В обох випадках Last-Modified або ETag буде взято, швидше за все, з кешу nginx, і перевірка пройде дуже швидко. Нам нема чого «смикати» PHP тільки для того, щоб скрипт видав ці заголовки, особливо у світлі того, що клієнтам, яким піде відповідь 200, він буде відданий з кешу.

fastcgi_cache_key: уважно працюємо із залежностями

fastcgi_cache_key "$request_method|$http_if_modified_since|$http_if_none_match|$host|$request_uri";

На особливу увагу заслуговує значення в директиві fastcgi_cache_key. Я навів мінімальне робоче значення цієї директиви. Крок праворуч, крок ліворуч, і ви почнете часом отримувати «неправильні» дані з кеша. Отже:

  • Залежність від $request_method нам необхідна, т.к. HEAD-запити в Інтернеті досить часті. Відповідь на HEAD-запит ніколи не містить тіла. Якщо усунути залежність від $request_method, то може так збігтися, що хтось до вас запросив головну сторінку HEAD-методом, а вам потім по GET віддасться порожній контент.
  • Залежність від $http_if_modified_since потрібна для того, щоб кеш з відповіддю 304 Not Modified не був випадково відданий клієнту, який робить звичайний GET-запит. Інакше клієнт може отримати порожню відповідь із кешу.
  • Те саме і з $http_if_none_match. Ми маємо бути застраховані від видачі порожніх сторінок клієнтам!
  • Нарешті, залежність від $host та $request_uri не вимагає коментарів.
fastcgi_hide_header: вирішуємо проблеми з безпекою

fastcgi_hide_header "Set-Cookie";

Директива fastcgi_hide_header є дуже важливою. Без неї ви серйозно ризикуєте безпекою: користувачі можуть отримати чужі сесії через сесійну куку в кеші. (Щоправда, в останніх версіях nginx щось було зроблено у бік автоматичного обліку даного фактора.) Розумієте, як це відбувається? На сайт зайшов Вася Пупкін, йому видалася сесія та сесійна Cookie. Нехай кеш на той момент виявився порожнім, і записалася Васина Cookie. Потім прийшов інший користувач, отримав відповідь з кешу, а в ньому – і Cookie Васі. Отже, і його сесію теж.

Можна, звичайно, сказати: давайте не викликатимемо session_start() на головній сторінці, тоді і з Cookies проблем не буде. Теоретично це, але практично цей спосіб дуже нестійкий. Сесії часто стартують "відкладено", і достатньо будь-якої частини коду "випадково" викликати функцію, що вимагає доступу до сесії, як ми отримаємо дірку в безпеці. А безпека - така штука, що якщо в тій чи іншій методиці може виникнути дірка з необережності, то ця методика вважається "дірявою" за визначенням. До того ж є інші Cookies, крім сесійної; їх також не треба записувати в кеш.

fastcgi_ignore_headers: не даємо сайту «лягти» від навантаження під час друкарської помилки

fastcgi_ignore_headers "Cache-Control" "Expires";

Сервер nginx звертає увагу на заголовки Cache-Control, Expires та Pragma, які видає PHP. Якщо в них сказано, що сторінку не потрібно кешувати (або що вона вже застаріла), nginx не записує її в кеш-файл. Ця поведінка, хоч і здається логічною, на практиці породжує масу складнощів. Тому його блокуємо: завдяки fastcgi_ignore_headers в кеш-файли потрапить вміст будь-якої сторінки, незалежно від її заголовків.

Що ж це за складнощі? Вони знову пов'язані з сесіями та функцією session_start(), яка в PHP за замовчуванням виставляє заголовки Cache-Control: no-cache і Pragma: no-cache. Тут існує три вирішення проблеми:

  • Не використовувати session_start () на сторінці, де передбачається кешування. Один з мінусів цього способу ми вже розглянули вище: досить одного необережного руху, і ваш сайт, що приймає тисячі запитів за секунду на закешовану головну сторінку, миттєво «ляже», коли кеш відключиться. Другий мінус - нам доведеться керувати логікою кешування у двох місцях: у конфізі nginx та у PHP-коді. Тобто. ця логіка виявиться «розмазаною» по різних частинах системи.
  • Виставити ini_set ("session.cache_limiter", ""). Це змусить PHP заборонити виведення будь-яких заголовків, що обмежують кешування під час роботи з сесіями. Проблема тут та сама: «розмазаність» логіки кешування, адже в ідеалі ми хотіли б, щоб усе кешування керувалося з єдиного місця.
  • Ігнорувати заголовки заборони кешування під час запису в кеш-файли за допомогою fastcgi_ignore_headers. Здається, це безпрограшне рішення, тому я його раджу.

Кешування з ротацією

Статична головна сторінка – це не так уже й цікаво. Що робити, якщо на сайті багато матеріалів, а Головна виступає у ролі своєрідної «вітрини» для них? На такій "вітрині" зручно відображати "випадкові" матеріали, щоб різні користувачі бачили різне (і навіть один користувач отримував новий контент, перезавантаживши сторінку в браузері).

Розв'язання задачі – кешування з ротацією:

  1. Ми змушуємо скрипт чесно видавати елементи головній сторінці у випадковому порядку, виконуючи необхідні запити до бази даних (нехай і повільно).
  2. Потім ми зберігаємо в кеші не одну, а скажімо, 10 варіантів сторінки.
  3. Коли користувач заходить на сайт, ми показуємо йому один із цих варіантів. При цьому якщо кеш порожній, то запускається скрипт, а якщо ні, то результат повертається з кеша.
  4. Встановлюємо час старіння кешу малим (наприклад, 1 хвилина), щоб за день різні користувачі "віддивилися" всі матеріали сайту.

У результаті перші 10 запитів до скрипта-генератора виконаються «чесно» та «навантажать» сервер. Натомість потім вони «осядуть» у кеші і протягом хвилини видаватимуться вже швидко. Приріст продуктивності тим більший, чим більше відвідувачів на сайті.

Ось шматочок конфіга nginx, що реалізує кешування з ротацією:

Fastcgi_cache_path /var/cache/nginx levels=keys_zone=wholepage:50m; perl_set $rand "sub (return int rand 10)"; ... server ( ... location / ( ... fastcgi_pass 127.0.0.1:9000; ... # Включаємо кешування і ретельно вибираємо ключ кеша. fastcgi_cache wholepage; fastcgi_cache_valid 200 301 302 304 1m; |$http_if_modified_since|$http_if_none_match|$host|$request_uri"; # Гарантуємо, що різні користувачі не отримають одну і ту ж сесійну Cookie. fastcgi_hide_header "Set-Cookie"; # Примушуємо nginx , що виставляються в PHP fastcgi_ignore_headers "Cache-Control" "Expires"; # Заставляємо браузер щоразу перезавантажувати сторінку (для ротації). , post-check=0, pre-check=0"; fastcgi_hide_header "Pragma"; add_header Pragma "no-cache"; # Видаємо завжди свіжий Last-Modified. expires -1; # Увага!!! Last-Modified $sent_http_Expires; ))

Ви можете помітити, що порівняно з попереднім прикладом мені довелося додати ще 6 директив до location. Вони всі дуже важливі! Але не забігатимемо вперед, розглянемо все по порядку.

perl_set: залежність-рандомізатор

perl_set $rand "sub (return int rand 10)";

З директивою perl_set просто. Ми створюємо змінну, при використанні якої nginx викликатиме функцію вбудованого в нього Perl-інтерпретатора. За словами автора nginx, це досить швидка операція, тому ми не заощаджуватимемо на сірниках. Змінна приймає випадкове значеннявід 0 до 9 у кожному з HTTP-запитів.

fastcgi_cache_key: залежність від рандомізатора

fastcgi_cache_key "$rand|$request_method|...";

Тепер ми замішуємо змінну-рандомізатор у ключ кешу. У результаті виходить 10 різних кешів на той самий URL, що нам і потрібно. Завдяки тому, що скрипт, який викликається при кеш-промаху, видає елементи головної сторінкиу випадковому порядку ми отримуємо 10 різновидів головної сторінки, кожна з якої «живе» 1 хвилину (див. fastcgi_cache_valid).

add_header: примусово вимикаємо браузерний кеш
fastcgi_hide_header "Cache-Control"; add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"; fastcgi_hide_header "Pragma"; add_header Pragma "no-cache";

Вище ми говорили, що nginx чутливий до кеш-заголовків, що видаються PHP-скриптом. Якщо PHP-скрипт повертає заголовки «Pragma: no-cache» або «Cache-Control: no-store» (а також деякі, наприклад, «Cache-Control: не-зберігати, не-видавати, мене-тут-не- було, я-цього-не-говорив, чия-це-капелюх»), то nginx не буде зберігати результат у кеш-файлах. Для того, щоб придушити таку його поведінку, ми використовуємо fastcgi_ignore_headers (див. вище).

Чим відрізняється "Pragma: no-cache" від "Cache-Control: no-cache"? Тільки тим, що Pragma – спадщина HTTP/1.0 і зараз підтримується для сумісності зі старими браузерами. У HTTP/1.1 використовується Cache-Control.

Проте є ще кеш у браузері. І в деяких випадках браузер може навіть не намагатись робити запит на сервер, щоб відобразити сторінку; натомість він дістане її з власного кешу. Т.к. у нас ротація, нам така поведінка незручна: адже щоразу, заходячи на сторінку, користувач має бачити нові дані. (Насправді, якщо ви все ж таки хочете закешувати якийсь один варіант, то можна поекспериментувати із заголовком Cache-Control.)

Директива add_header таки передає в браузер заголовок заборони кешування. А щоб цей заголовок випадково не розмножився, ми спочатку прибираємо з HTTP-відповіді те, що записав туди PHP-скрипт (і те, що записалося в nginx-кеш): директива fastcgi_hide_header. Адже ви, коли пишете конфіг nginx-а, не знаєте, що надумає виводити PHP (а якщо використовується session_start(), то він точно надумає). Раптом він виставить власний заголовок Cache-Control? Тоді їх буде два: PHP-шний та доданий нами через add_header.

expires та Last-Modified: гарантуємо перезавантаження сторінки
expires -1; # Увага!!! Цей рядок expires необхідний! add_header Last-Modified $sent_http_Expires;

Ще один трюк: ми повинні виставити Last-Modified рівним поточному часу. На жаль, у nginx немає змінної, що зберігає поточний час, проте вона магічним чином з'являється, якщо вказати директиву expires -1.

Хоча це зараз (жовтень 2009 р.) не задокументовано, nginx створює змінні види $sent_http_XXX для кожного заголовка відповіді XXX, відданого клієнту. Однією з них ми користуємося.

Чому ж так важливо виставляти поточним часомцей заголовок? Все дуже просто.

  1. Давайте уявимо, що PHP видав заголовок "Last-Modified: деяка_дата".
  2. Цей заголовок буде записаний у кеш-файл nginx (можете перевірити: у нашому прикладі файли зберігаються в /var/cache/nginx), а потім відданий у браузер клієнту.
  3. Браузер запам'ятає сторінку та дату її модифікації.
  4. … тому при наступному заході користувача на сайт у HTTP-запиті буде заголовок-питання "If-Modified-Since: деяка_дата".
  5. Що ж зробить nginx? Він дістане сторінку зі свого кеша, розбере її заголовки та порівняє Last-Modified з If-Modified-Since. Якщо значення співпадуть (або перше виявиться меншим за друге), то nginx поверне відповідь «304 Not Modified» з порожнім тілом. І користувач не побачить жодної ротації: він отримає те, що бачив раніше.

Насправді, велике питання, як поведеться браузер за наявності одночасно Last-Modified та Cache-Control no-cache. Чи буде він запит If-Modified-Since? Здається, що різні браузериповодяться тут по-різному. Експериментуйте.

Є ще один привід виставляти Last-Modified вручну. Справа в тому, що PHP-функція session_start () примусово видає заголовок Last-Modified, але вказує в ньому час зміни PHP-файлу, який перший отримав управління. Отже, якщо у вас на сайті всі запити йдуть на той самий скрипт (Front Controller), то ваша Last-Modified буде майже завжди дорівнює часу зміни цього єдиного скрипту, що абсолютно не вірно.

Динамічне вікно в закешованій сторінці

Ну і насамкінець згадаю одну техніку, яка може бути корисна у світлі кешування. Якщо вам хочеться закешувати головну (або будь-яку іншу) сторінку сайту, проте заважає один маленький блок, який обов'язково має бути динамічним, скористайтесь модулем для роботи з SSI.

В ту частину сторінки, яка повинна бути динамічною, вставте такий «HTML-коментар»:

З погляду кешу nginx цей коментар - звичайний текст. Він буде збережений у кеш-файлі у вигляді коментаря. Однак пізніше, при прочитанні кешу, спрацює модуль SSI nginx, який звернеться до динамічного URL-адреси. Звичайно, за адресою /get_user_info/ має бути PHP-обробник, який видає вміст цього блоку.

Ну і, звичайно, не забудьте увімкнути SSI для цієї сторінки або навіть для всього сервера:

Директива SSI include має ще одну, дуже важливу властивість. Коли на сторінці зустрічаються кілька таких директив, всі вони починають оброблятися одночасно, в паралельному режимі. Так що, якщо у вас на сторінці 4 блоки, кожен із яких завантажується 200мс, у сумі сторінка буде отримана користувачем через 200мс, а не через 800мс.

|

Nginx включає модуль FastCGI, який дозволяє використовувати директиви для кешування динамічного контенту в інтерфейсі PHP. FastCGI усуває необхідність шукати додаткові рішеннядля кешування сторінок (наприклад, зворотні проксі або спеціальні плагіни додатків). Контент також може бути виключений з кешування на основі методу запиту, URL-адреси, cookies або будь-якої іншої змінної сервера.

Активація кешування FastCGI

Щоб дотримуватися цього посібника, потрібно заздалегідь . Також потрібно відредагувати конфігураційний файлвіртуального хоста:

nano /etc/nginx/sites-enabled/vhost

Внесіть наступні рядки на початок файлу поза директивою server ( ):

Директива fastcgi_cache_path визначає шлях до кешу (/etc/nginx/cache), вказує його розмір (100m), ім'я зони пам'яті (MYAPP), рівні підкаталогів та таймер inactive.

Кеш можна розміщувати в будь-якій зручній точці жорсткого диска. Максимальний розмір кешу не повинен перевищувати RAM сервера + розмір swap-файлу; інакше буде виведено помилку Cannot allocate memory. Якщо кеш не був використаний протягом конкретного періоду часу, вказаного за допомогою опції inactive (в даному випадку це 60 хвилин), то Nginx видаляє його.

Директива fastcgi_cache_key вказує спосіб хешування імен файлів. За даними налаштувань, Nginx шифруватиме файли за допомогою MD5.

Тепер можна перейти до директиви location, яка передає PHP запити модулю php5-fpm. У location ~ .php$ ( ) внесіть такі рядки:

fastcgi_cache MYAPP;
fastcgi_cache_valid 200 60м;

Директива fastcgi_cache посилається на зону пам'яті, яка вже була вказана у директиві fastcgi_cache_path.

За замовчуванням Nginx зберігає кешовані об'єкти протягом часу, вказаного за допомогою одного з цих заголовків:

X-Accel-Expires
Expires
Cache-Control.

Директива fastcgi_cache_valid вказує термін зберігання кешу за замовчуванням, якщо жодного із цих заголовків немає. Відповідно до встановленого значення кешуються тільки відповіді з кодом стану 200 (звісно, ​​можна вказати інші коди стану).

Перевірте налаштування FastCGI

service nginx configtest

Потім перезапустіть Nginx, якщо з налаштуваннями все гаразд.

service nginx reload

На даному етапі файл vhost повинен мати такий вигляд:

fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=MYAPP:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
server (
listen 80;
root /usr/share/nginx/html;
index index.php index.html index.htm;
server_name example.com;
location / (
try_files $uri $uri//index.html;
}
location ~ \.php$ (
try_files $uri = 404;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_cache MYAPP;
fastcgi_cache_valid 200 60м;
}
}

Тепер потрібно перевірити, чи кешування працює.

Перевірка кешування FastCGI

Створіть PHP-файл, який відображає позначку часу UNIX.

/usr/share/nginx/html/time.php

Внесіть у файл:

echo time();
?>

Потім кілька разів запитайте даний файлчерез curl або веб-браузер.

[email protected]:~# curl http://localhost/time.php;echo
1382986152

1382986152
[email protected]:~# curl http://localhost/time.php;echo
1382986152

Якщо кешування виконується належним чином, тимчасова позначка всіх запитів буде співпадати (оскільки відповідь була кешована).

Щоб знайти кеш цього запиту, потрібно виконати зворотний запис кешу

[email protected]:~# ls -lR /etc/nginx/cache/
/etc/nginx/cache/:
total 0
drwx------ 3 www-data www-data 60 Oct 28 18:53 e
/etc/nginx/cache/e:
total 0
drwx------ 2 www-data www-data 60 Oct 28 18:53 18
/etc/nginx/cache/e/18:
total 4
-rw------- 1 www-data www-data 117 Oct 28 18:53

Також можна додати заголовок X-Cache, який вкаже, що даний запитбув оброблений з кеша (X-Cache HIT) або безпосередньо (X-Cache MISS).

Над директивою server ( ) внесіть:

add_header X-Cache $upstream_cache_status;

Перезапустіть сервіс Nginx та виконайте докладний запитза допомогою curl, щоб побачити новий заголовок.

[email protected]:~# curl -v http://localhost/time.php
* About to connect() to localhost port 80 (#0)
* Trying 127.0.0.1...
* connected
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /time.php HTTP/1.1
> User-Agent: curl/7.26.0
> Host: localhost
> Accept: */*
>
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 200 OK
< Server: nginx
< Date: Tue, 29 Oct 2013 11:24:04 GMT
< Content-Type: text/html
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Cache: HIT
<
* Connection #0 to host localhost left intact
1383045828* Closing connection #0

Винятки кешування

Деякий динамічний контент (наприклад, сторінки запиту аутентифікації) не потрібно кешувати. Такий контент можна виключити з кешування за допомогою змінних request_uri, request_method та http_cookie.

Нижче наведено приклад налаштувань, які можна використовувати у контексті server( ).

#Cache everything by default
set $no_cache 0;
#Don't cache POST requests
if ($request_method = POST)
{
set $no_cache 1;
}
#Don't cache if the URL contains a query string
if ($query_string != "")
{
set $no_cache 1;
}
#Don't cache the following URLs
if ($request_uri ~* "/(administrator/|login.php)")
{
set $no_cache 1;
}
#Don't cache if there is a cookie називається PHPSESSID
if ($http_cookie = "PHPSESSID")
{
set $no_cache 1;
}

Щоб застосувати змінну $no_cache у відповідні директиви, помістіть наступні рядки у location ~ .php$ ( )

fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;

Директива fastcgi_cache_bypass ігнорує існуючий кеш для запитів, пов'язаних із встановленими нами умовами. Директива fastcgi_no_cache взагалі не кешуватиме такі запити.

Очищення кешу

Угода про імена кешу ґрунтується на змінних, які були застосовані у директиві fastcgi_cache_key.

fastcgi_cache_key "$scheme$request_method$host$request_uri";

Згідно з цими змінними, при запиті http://localhost/time.php будуть виведені наступні значення:

fastcgi_cache_key "httpGETlocalhost/time.php";

Після хешування цього рядка в MD5 вийшло б таке:

b777c8adab3ec92cd43756226caf618e

Це сформує ім'я файлу кеша у відповідності до підкаталогів, вказаних у levels=1:2. Таким чином, перший рівень каталогу в цьому рядку MD5 буде позначений останнім символом рядка (у даному випадку символ е). Другому рівню належать наступні після першого рівня 2 символи (18). Таким чином, вся структура каталогів цієї кеш-зони виглядатиме так:

/etc/nginx/cache/e/18/b777c8adab3ec92cd43756226caf618e

На основі цього формату кешування, можна створити скрипт очищення кешу в будь-якій зручній мові. У цьому посібнику для того використовується PHP. Створіть файл:

/usr/share/nginx/html/purge.php

Внесіть до нього:

$cache_path = "/etc/nginx/cache/";
$url = parse_url($_POST["url"]);
if(!$url)
{
echo "Invalid URL entered";
die();
}
$scheme = $url["scheme"];
$host = $url["host"];
$requesturi = $url["path"];
$hash = md5($scheme."GET".$host.$requesturi);
var_dump(unlink($cache_path . substr($hash, -1) . "/" . substr($hash,-3,2) . "/" . $hash));
?>

Надішліть POST-запит на цей файл із URL, який потрібно очистити.

curl -d "url=http://www.example.com/time.php" http://localhost/purge.php

Скрипт видасть true чи false залежно від того, чи був очищений кш чи ні. Обов'язково виключіть цей скрипт із кешування, а також не забудьте обмежити доступ до нього.

Tags: ,

© 2022 androidas.ru - Все про Android