Николай Ланец
17 авг. 2013 г., 2:17

Разработка интернет-магазина 13000+ товаров на MODX Revolution.

Я уже писал про свой компонент shopModx. И хотя мало кто его оценил, так как многие ждут именно готовых решений с одной большой кнопкой «Установить и работать», тем не менее этот компонент разрабатывается с учетом тех минусов, которые есть в MODX, и в которые часто упираются MODX-разработчики, и с учетом тех плюсов, которые в MODX есть, но о которых разработчики не знают, или просто не используют.
Так же хочу сказать, что этот модуль не просто так разрабатывается. Он разрабатывается под два не маленьких магазина (для начала), и на выходе получится обкатанная платформа для реализации крупных интернет-магазинов.
Сегодня я хотел бы начать цикл статей о разработке крупных интернет-магазинов на MODX Revolution, с рассказами о том, с какими сложностями приходится сталкиваться, и какие варианты решения этих проблем используются. А так же о том, что для решения таких проблем shopModx уже будет нести на борту, и какие приемы позволят получить 100% контроль над разработкой своего уникального магазина, не влезая в код shopModx.
Итак, немного о магазине, над которым ведется работа: это интернет-магазин мебели. Вчера импортнул базу. Получилось 13000+ документов, 43000+ ТВшек и почти 13000 записей в modx_shopmodx_products.
Сразу скажу, что я рассчитываю получать код страницы даже не из кеша и с поиском по параметрам менее чем за 1 секунду, а средняя загрузка не должна превышать 0.3-0.4 сек.
Итак, коротко о первых проблемах и их решениях.
Проблема 1. Большой файл кеша и много используемой памяти
Для начала входные данные для чистой Ревы. Специально скачал чистую 2.3.0 и посмотрел использование памяти. Код воткнул в плагин на событие OnWebPageComplite — это самая крайняя точка выполнения MODX-а уже после exit(), сохранения кеша документа и т.п. Первый заход (удалил вручную все файлы кеша):
Memory: 13.5409 Mb TotalTime: 0.1880 s
Далее:
Memory: 10.1396 Mb TotalTime: 0.0640 s
Кстати, на всякий случай код плагина: gist.github.com/Fi1osof/5062419 Можно доработать с проверкой прав доступов и всегда видеть актуальную нагрузку на сервер.
В общем, проверяем результаты на магазине (кстати, сразу хочу уточнить, что документ не пустой, а имеет 8 связанных TV-параметров, один из которых — картинка с кастомным медиамсурсом). Первый заход
Memory: 24.1438 Mb TotalTime: 0.4360 s
Далее:
Memory: 18.4103 Mb TotalTime: 0.0960 s
То есть имеем прирост к используемой памяти почти 10 метров сразу. Это потому что у нас кешируется вся карта УРЛ-ов контекста, а у нас там 13000+ документов. Кеш-файл контекста — почти 2 метра.
Очевидное решение — надо сокращать кеш-файл контекста. Я уже писал подробно про тонкие моменты в кешировании MODX-а и про свой патч cacheOptimizer. Ставим его и отключаем кеширование карты ресурсов для контекста web. Новые результаты:
Memory: 16.1369 Mb TotalTime: 0.2640 s
Memory: 10.4021 Mb TotalTime: 0.0720 s
То есть в штатном режиме у нас потребляет почти столько же памяти, как и на голой системе.
Проблема 2. Page not found (404)
Эта проблема вытекает прямиком из предыдущего решения :-) Так как мы отрубили кеширование карты УРЛ-ов, то теперь MODX не сможет по УРЛ-у «понимать», к какой странице у нас происходит обращение при использовании ЧПУ. Сразу уточню, что если у вас не будет использоваться ЧПУ, то это не должно быть для вас проблемой (хотя кто сегодня не использует ЧПУ?), или если у вас не большой магазин (до 1000 товаров), то можно и не отрубать карту страниц, лишний мегабайт оперативки — не проблема.
Итак, для решения этой проблемы я решил использовать собственный роутер. Я просто написал новый класс, расширяющий modRequest, и немного подправил пару методов. Логика следующая: при обращении к странице, MODX пытается найти id ресурса по запрошенному УРЛ-у в кеше. (УРЛ уже очищенный, то есть без всяких параметров и т.п.). Если находит, то возвращает ID и далее все происходит в штатном режиме. Если нет, то пытается найти документ в БД по uri. Находит — записывает id в кеш и далее возвращает id. Если нет, то стандартная процедура OnPageNotFound (так что еще можно заюзать свой плагин для модификации поиска).
Этот дополнительный класс будет поставляться вместе с shopModx, и если кому-то понадобится (если будет большой магазин), то просто включаете его в настройках (ключ modRequest.class).
Так же есть вариант сразу все страницы загонять в кеш, допустим, при обновлении кеша (юзать плагин на событие OnSiteRefresh).
Проблема 3. Много файлов кеша
Представляю, как многие читали предыдущее решение, и думали «ну что за дебил!» :-)
Да, плодить сотни тысяч кеш-файлов — это полный маразм. Но здесь ключевое слово — файлов. Да, именно их состояние (файлы) нам и не дают покоя. Потому в данном случае просто используем другой кеш-провайдер, а не файловый. Я решил использовать memcached, так как с ним уже хоть как-то сталкивался, и устанавливал на сервере, а вы можете использовать и другой, какой пожелаете. В стандартную сборку Рево так же поставляются memcache и APC.
Свой выбор в пользу кеш-механизма на оперативке я аргументировал тем, что упрощается сброс кеша. Попробуйте с жесткого диска удалить 1 000 000 файлов. Это будет происходить оооочень долго. В случае же с memcached сброс кеша делается просто и быстро.
$modx->cacheManager->getCacheProvider()->flush();
Еще один огромный плюс memcached — хранить можно любые типы данных, включая объекты. Исключение только — ресурсы (к примеру соединение с базой данных) и объекты, среди свойств которых есть ресурсы. Такие объекты должны создаваться с методами __sleep() и __wakeup(), чтобы перед сохранением они удаляли все свойства-ресурсы, а при восстановлении из кеша могли эти свойства вновь создать.
Итак, смотри результаты. Первый заход
Memory: 15.0709 Mb TotalTime: 0.1040 s
Далее
Memory: 10.403 Mb TotalTime: 0.0640 s
По-моему очень хорошо для некешированного контекста на 13000+ документов.
Проблема 4. Массовое обновление документов при изменении системных настроек
Не буду объяснять зачем, но понадобилось мне изменить суффикс контейнеров. Поменял, и Ajax-ответа так и не дождался… Полез смотреть процессор /system/settings/updatefromgrid. Есть в нем такой метод checkForRefreshURIs(). В общем, если изменялись «friendly_urls», «use_alias_path» или «container_suffix», то он сигнализирует, что надо обновить УРЛы. Все правильно. Но проблема в том, что он пытается обновить все документы без разбору, даже не контейнеры. К тому же еще и условие сортировки по menuindex зачем-то добавляет (хотя нас порядок вложенности интересует, а не индекс меню). В общем этот процесс делал сервер плакать. Добавил условие isfolder=1, и тогда за 6 секунд обновил все контейнеры. Больше менять суффиксы не буду :-)
Резюме
На практике мы получили полную обработку документа на сайте с 13000+ документами (в двух таблицах) и 43000+ TV-шек, менее чем за 0.3 секунды при обновленном кеше. Из кеша — менее чем за 0.1 сек. Условно можно считать, что на этом этапе разница между большим и маленьким сайтом заканчивается, так как дальнейшие тормоза возможны только на уровне рендеринга страницы, а это уже зависит от того, как мы шаблоны напишем и т.п. Этот момент я распишу в следующей статье (скорее всего завтра). Но сразу скажу, что я буду делать на Smarty, так как ИМХО делать все это на чистых чанках и сниппетах — куча проблем.
И напоследок результаты локального теста 100 клиентов по 1000 запросов каждый: gist.github.com/Fi1osof/462e1af10ab7b95311df Time per request: 44.224 [ms] (mean, across all concurrent requests)

Добавить комментарий