2. Алексей Качаев
Senior/Lead PHP Developer at
Cogniance
4+ года опыта c PHP
3+ года использования Zend
Framework
Активный участник
zendframework.ru/forum
ZF2 contributor
4. О чем мы будем говорить
• о ресурсах и о том, почему их не хватает
• о времени и о том, куда оно девается
• о пользователях, современных веб-приложениях и о
проблемах, с ними связанными
• ...
5. Как мы будем говорить
1. 2 проблемы в использовании ресурсов на реальных
примерах из широкой практики
2. как эти пробемы можно решить быстро и практически
безболезнено (как в новом, так и в функционирующем
проекте)
3. контректный zf app и все шаги имплементации того, что
описано в п. 2
Понимание проблем и путей их решения важно
9. Проблема 1. Запросы и
инициализация
AJAX + Пользователь => Много запросов к серверу
=
(nginx + apache2 + php + Zend + application) * много
Примеры: autosuggest-поле, валидация формы, загрузка
виджетов и т.д.
11. Проблема 1. Запросы и
инициализация
Rich application = AJAX * много
=
(nginx + apache2 + php + Zend + application) * много (в
квадрате)
12. Куда уходит время
- создание объекта приложения
- чтение и сборка конфигурации (тут даже кеш не спасает)
- загрузка и инициализация ресурсов
Затраты: до 40-60% времени выполнения.
13. Как хотелось бы? (псевдо код)
Framework::initialize();
while($request = Somewhere::request()) {
// Тут мы что-то делаем
// Отдаем ответ
}
Framework::shutdown();
14. Особенности PHP реализации
1. Проблемы:
- корявая реализация поддержки Fast-CGI на PHP
2. Существующие решения:
- php-fpm (FastCGI Process Manager, http://php-fpm.org/)
- phpDaemon
15. phpDaemon в студии
Основные возможности
- WebSocket, HTTP, FastCGI, flash
- асинхронные клиенты для MySQL, Memcache, MongoDB и
других
- динамическая перегрузка кода в демон при изменении
файлов
- статистика по количеству запросам, памяти и т.д.
- гибкая настройка worker'ов
...
- многое другое
17. Проблема 2. Кеширование HTML
1. Это заманчиво!
- обслуживание большого количества подключений
одновременно
- возможность не инициализировать приложение вообще
2. Это проблематично!
- "размазывание кеша" (для авторизированных
пользователей, по cookies и т.д.)
- точечная инвалидация = "микро-инфаркт" (например,
блок последних комментариев на хабре)
20. Проблема 2. Возьмем хитростью
1. Varnish, как промежуточный кеширующий сервер
(caching HTTP reverse proxy)
http://www.varnish-cache.org/
2. ESI, как markup language связи логических кусков HTML
http://en.wikipedia.org/wiki/Edge_Side_Includes
<esi:include src="..." onerror="continue"/>
3. Nginx, как front web server
http://nginx.org/
21. Проблема 2. Расплата за хитрость
ESI = Увеличение количества запросов к backend
Большинство из этих запросов однотипно и нетребует
мощностей всего приложения
ВЕРНУЛИСЬ К ПРОБЛЕМЕ 1
Слава богу, мы уже знаем как ее
решать.
25. Демо приложение: "Конференция"
1. Список докладов из базы данных
2. Добавление докладов в избранное без регистрации
3. Форма поиска по докладам с "подсказыванием" тегов
4. ~ Вход/выход для зарегистрированных пользователей
Sources
https://github.com/kachayev/zfconf-speech
29. phpDaemon: установка
1. Официальная документация
https://github.com/kakserpom/phpdaemon/wiki/Installation-
(common)
2. Используется PECL расширения libevent, proctitle
(возможно потребуется пересборка PHP из исходников)
http://tokarchuk.ru/2010/09/%D1%83%D1%81%D1%82%D0%
B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-
phpdaemon-libevent-%D0%BD%D0%B0-ubuntu/
РУКОВОДСТВА БЫСТРО УСТАРЕВАЮТ
30. phpDaemon: установка на Ubuntu
0. Нам понадобиться: php-dev, build-essential, git
1. Удаляем старую libevent1
2. Скачиваем новую из ветки 1.4 (2я пока в альфе). Далее
наше любимое .configure && make && make install
3. Устанавливаем pecl extension libevent-0.0.4, добавляем
его подгрузку в /etc/php5/cli/php.ini
4. Аналочиные действия с proctitle
5. Устанавливаем phpDaemon... ->
31. phpDaemon: установка на Ubuntu
cd /usr/local/lib
git clone git://github.com/kakserpom/phpdaemon.git $path
cd ./phpdaemon
chmod +x ./bin/phpdaemon
ln /usr/bin/phpdaemon /usr/local/lib/phpdaemon/bin/phpdaemon
cp init-scripts/phpd /etc/init.d/
mkdir /etc/phpd/ && cp conf/phpd.conf.example /etc/phpd/phpd.conf
/usr/local/lib/phpdaemon/bin/phpdaemon start
В файл конфигурации добавляем следующее:
HTTP {
enable 1;
listen 127.0.0.1;
listen-port 8020;
responder "Example"
}
32. phpDaemon:
default application
1. Принимает HTTP
запросы на порт, без р
веб-сервера
2. Siege показал 370
обработанных реквестов
в секунду при 100
одновременных
подключениях.
33. Прикручиваем к своему проекту
1. Выделяем консистентные блоки (autocomplete,
autosuggest, рендеринг отдельных блоков, виджеты и т.д.)
Важно! Операция на демоне должна затрагивать минимум
различных частей приложения. Подходят: ajax-проверка
полей формы, inline-редактирование данных,
autosuggest/complete и т.д.
2. Отделяем выполнение (Ajax, SSI, ESI etc).
3. Поднимаем демон, реализованный как HTTP App (при
желании можно и Fast CGI).
(следующий слайд)
34. Прикручиваем к своему проекту
(прд.)
Начинаем с Nginx-подобного конфиг-файла.
/etc/phpd/phpd.conf
Примечание:
(Можно через AppResolver.php, но для
простых случаев это overkill)
35. phpDaemon + Zend Framework
Responder application - класс, наследник AppInstance
Должен реализовать следующие функции:
init() - инициализация фреймворка (создаем Zend
Application, бутстрапимся, получаем Front Controller)
beginRequest() - вызывается, когда приложение получает
новый Request для обработки (возвращаем специальный
объект - наследник HTTPRequest)
onReady() - вызывается после отработки инициализации
onShutdown() - завершаем работу фрейморка
38. phpDaemon -> Request
HTTPRequest содержит все нужные нам данные GET,
POST, COOKIES, SESSION и т.д.
В наследнике должны реализовать функции
run() - передать в приложение новые суперглобальные,
подготовить Response, должны вернуть Request::DONE
(сбрасываем Request, Response объекты Front Controller'а и
выполняем run())
onFinish() - возвращаем подготовленный ранее Response
40. Nginx -> phpDaemon
4. Nginx "ловит" адрес и перенаправляет его на backend
(HTTP, или FastCGI)
Полный разбор nginx кофигурации не делаем.
Только момент "перехвата" URL.
41. Несколько замечаний (номер 1)
Важно! При реализации данного подхода критически
важно, чтобы инициализация (Bootstrap, Application
Resources) не делала ничего request-зависимого.
Например.
Присваивание текущего языка (current language) в Bootstrap
- это очень большая ошибка. Для этого есть плагины.
(вспоминаем цикл диспечирезации).
42. Несколько замечаний (номер 2)
Важно! Внимательно следите, чтобы никто "не посмел"
отправить клиенту хоть какие-то данные, до тех пор, пока
это явно не сделает phpDaemon.
Например.
В коде Action'а:
$this->_helper->json($data);
Приведет к:
(примеров много)
44. Known issues
1. Debian, Ubuntu - проблемы с рестартом при "зависших"
подключениях (аналогично Mongo). "Спасает":
2. Если работа ведется без веб-сервера, то нужно помнить
о статике. Например, попытка обработать запрос
/favicon.ico с помощью Zend отправляет phpDaemon в
глубокий down.
Т.е. без веб-сервера фильтруем статику в ручную.
45. Что дальше?
Неблокирующий MySQL драйвер
Неблокирующий Memcache клиент
И т.д.
Как это работает? (на примере MySQL)
1. Запрос (обработчик) делает request на MySQL и вешает
callback
2. Вызывается $this->sleep(30) - диспечер выбрасывает
процесс из обработки, чтобы вернуться через 30 сек
3. В callback делается $this->wakeup()
46. Что дальше?
В последних версиях появилась возможность использовать
Sandbox
Fatal error воркера не приводит к перезапуску демона.
47. Оценим полученный результат
+ Плюсы
1. Снижение нагрузки на сервер и потребляемых ресурсов
2. Уменьшение времени ожидания ответа
- Минусы
1. Усложнение деплоя
2. Усложнение тестирования
3. Усложнение управления приложением
48. Полезные материалы по теме
0. "Офф. сайт"
http://phpdaemon.net/
1. phpDaemon - фреймворк асинхронных приложений
http://habrahabr.ru/blogs/php/79377/
2. Wiki документация
https://github.com/kakserpom/phpdaemon/wiki
50. Varnish: сильные стороны
1. Мощный язык конфигурации VCL (Varnish Configuration
Language)
2. Легкость горизонтального масштабирования
3. Возможность использования механизмов балансировки
нагрзуки с учетом времени ответов и загруженности бекенд
серверов
4. Поддержка ESI разметки
5. Большое количество отличных утилит для
профайлирования и мониторинга
52. Varhish: установка на Ubuntu
cd /tmp
sudo bash
curl http://repo.varnish-cache.org/debian/GPG-key.txt | apt-key add -
echo "deb http://repo.varnish-cache.org/ubuntu/ $(lsb_release -s -c) varnish-2.1" >> /etc/apt/source.list
apt-get update && apt-get install varnish
# Далее вы можете использовать init-скрипт
* Usage: /etc/init.d/varnish {start|stop|restart|force-reload}
Установка на других платформах:
http://www.varnish-cache.org/docs
Установка web-based admin console:
http://www.varnish-cache.org/trac/wiki/WebGui
60. PhpDaemon workers
В указанном выше примере использовался один и тот же
ZfconfApp на порту 8091.
Но, часто есть смысл разграничивать обработчики:
- отдельный worker на autosuggest
- отдельный worker на рендеринг AJAX-видета
В таких случаях используется AppResolver.php (можно
прописывать правила "раздачи" запросов на worker-ы).
61. Использование ESI
Основное
- Формат: <esi:include src="" />
- Директивы alt и onerror на данный момент не
поддерживаются
- для Zend-a самописный View Helper (рендер include и
отправление заголовка esi-enabled)
62. Использование ESI
Обратить внимание
- отключаем сжатие (nginx: gzip off, apache2: no deflate)
- проблемы с ETag, If-Non-Match
- отслеживание поведения кеша по URL (nginx rewrite,
apache2 rewrite, Zend routing, URL-mark etc)
- готовьтесь к сюрпризам :)
63. А можно ли по другому?
По аналогу со схемой Nginx + Varnish + ESI можно
использовать схему
Nginx + SSI + Memcache
Минусы
- nginx не умеет писать контент в memcache (нужно писать
со стороны backend-a)
- слабые возможности конфигурирования кеш-ключа
- сложно управлять кешированием
64. Подводные камни
1. Правильность разграничения контента
Например, если HTML различается по языку, хранящемуся в cookies, то
это нужно учитывать.
65. Подводные камни
2. Очистка выполняется через CLI или отправкой запроса
HTTP PURGE
Не самая простая процедура, поэтому для dev-режима нужно
предусмотреть возможность работы с TTL=0.
Очистка varnish-кеша по объекто-зависимым событиям.
Пример: очистить кеш списка докладов после внесения
изменений пользователем.
- переопределить CacheManager
- обернуть необходимые cache backend в специальные
враппер
67. Подводные камни
3. Управление кешем со стороны клиента (Cache-Control,
etc)
В demo app есть пример работы заголовками клиента
4. Документация во многом не соответствует правде.
68. Нагрузочное тестирование
результата
Не очень информативно и предсказуемо, но по факту:
ab -n 100 -c 5 http://zfconf.loc/
Преимущество: response time практически не
увеличивается при повышении concurrency level.
69. Ссылки на полезные материалы
Varnish docs and tutorials:
• http://www.varnish-cache.org/docs/2.1/index.html
• http://www.ibm.com/developerworks/opensource/library/os-php-
varnish/?ca=dgr-lnxw06Varnish-PHP
• http://www.mediawiki.org/wiki/Manual:Varnish_caching
ESI:
• http://www.varnish-cache.org/trac/wiki/ESIfeatures
• http://cd34.com/blog/infrastructure/no-esi-processing-first-char-not/
VCL:
• http://www.varnish-cache.org/docs/2.1/tutorial/vcl.html