Как оптимизировать PHP Laravel Web Application для высокой производительности?

Laravel это много вещей. Но пост не один из них. Давайте изучим некоторые приемы торговли, чтобы она пошла быстрее!


Ни один PHP разработчик не тронут Laravel В эти дни. Они либо разработчики младшего или среднего уровня, которым нравится быстрое развитие, предлагаемое Laravel, либо старшие разработчики, которые вынуждены изучать Laravel из-за давления рынка.

В любом случае, нельзя отрицать, что Laravel оживил экосистему PHP (я бы, конечно, давно бы покинул мир PHP, если бы Laravel там не было).

Отрывок (несколько оправданный) самовосхваления от Laravel

Тем не менее, поскольку Laravel наклоняется назад, чтобы упростить вам задачу, это означает, что под этим он выполняет тонны и тонны работы, чтобы обеспечить вам комфортную жизнь разработчика. У всех «магических» функций Laravel, которые, кажется, просто работают, есть слои за слоями кода, которые нужно запускать при каждом запуске функции. Даже простое исключение отслеживает, насколько глубока кроличья нора (обратите внимание, где начинается ошибка, вплоть до основного ядра):

Для того, что кажется ошибкой компиляции в одном из представлений, существует 18 вызовов функций для трассировки. Я лично сталкивался с 40, и может быть больше, если вы используете другие библиотеки и плагины.

Точка, по умолчанию это слои на слоях кода, делает Laravel медленным.

Насколько медленный Laravel?

Честно говоря, на этот вопрос невозможно ответить по нескольким причинам..

Первый, Не существует общепринятого, объективного и разумного стандарта для измерения скорости веб-приложений. Быстрее или медленнее по сравнению с чем? При каких условиях?

второй, веб-приложение зависит от многих вещей (базы данных, файловой системы, сети, кэша и т. д.), и говорить о скорости глупо. Очень быстрое веб-приложение с очень медленной базой данных – очень медленное веб-приложение. ��

Но именно из-за этой неопределенности популярны эталонные тесты. Хотя они ничего не значат (см. это а также это), они предоставляют некоторые ориентиры и помогают нам сойти с ума. Поэтому, с готовностью к действию, давайте получим неправильное, грубое представление о скорости среди сред PHP.

Проходя мимо этого довольно респектабельного GitHub источник, вот как выстраиваются фреймворки PHP при сравнении:

Вы можете даже не заметить Laravel здесь (даже если вы прищурите очень сильно), если вы не бросите свое дело до конца хвоста. Да, дорогие друзья, Laravel идет последним! Конечно, большинство из этих «фреймворков» не очень практичны и даже не полезны, но они говорят нам, насколько медленным Laravel по сравнению с другими более популярными.

Обычно эта «медлительность» не используется в приложениях, потому что наши повседневные веб-приложения редко достигают больших цифр. Но как только они это сделают (скажем, более 200-500 параллелей), серверы начинают задыхаться и умирать. Это время, когда даже использование большего количества оборудования не решает проблему, и счета за инфраструктуру растут так быстро, что ваши высокие идеалы облачных вычислений рушатся.

Но эй, взбодрись! Эта статья не о том, что нельзя сделать, а о том, что можно сделать. ��

Хорошей новостью является то, что вы можете сделать многое, чтобы ваше приложение Laravel работало быстрее. Несколько раз быстро. Да, без шуток. Вы можете сделать одну и ту же кодовую базу баллистической и сэкономить несколько сотен долларов на счетах за инфраструктуру / хостинг каждый месяц. Как? Давайте доберемся до этого.

Четыре типа оптимизации

На мой взгляд, оптимизацию можно проводить на четырех разных уровнях (то есть, когда речь идет о приложениях PHP):

  1. Язык уровня: Это означает, что вы используете более быструю версию языка и избегаете определенных функций / стилей кодирования в языке, которые замедляют ваш код.
  2. Рамки уровня: Это то, что мы расскажем в этой статье.
  3. Инфраструктура уровня: Настройка вашего менеджера процессов PHP, веб-сервера, базы данных и т. Д..
  4. Оборудование уровня: Переход к лучшему, более быстрому и более мощному провайдеру аппаратного хостинга.

Все эти типы оптимизации имеют свое место (например, оптимизация php-fpm довольно важна и эффективна). Но в центре внимания этой статьи будут оптимизации только типа 2: те, которые относятся к фреймворку.

Кстати, за нумерацией нет никаких объяснений, и это не принятый стандарт. Я только что сделал это. Пожалуйста, никогда не цитируйте меня и не говорите: «Нам нужна оптимизация типа 3 на нашем сервере», иначе руководитель вашей команды убьет вас, найдет меня, а затем убьет и меня. ��

И вот, наконец, мы добрались до земли обетованной.

Будьте в курсе n + 1 запросов к базе данных

Проблема n + 1 является распространенной проблемой при использовании ORM. У Laravel есть мощный ORM под названием Eloquent, который настолько красив и удобен, что мы часто забываем посмотреть, что происходит.

Рассмотрим очень распространенный сценарий: отображение списка всех заказов, сделанных данным списком клиентов. Это довольно часто встречается в системах электронной коммерции и любых интерфейсах отчетности в целом, где нам нужно отображать все объекты, связанные с некоторыми объектами..

В Laravel мы можем представить себе функцию контроллера, которая выполняет такую ​​работу:

Класс OrdersController расширяет контроллер
{
// …

публичная функция getAllByCustomers (запрос $ request, массив $ ids) {
$ customer = Customer :: findMany ($ ids);
$ orders = collect (); // новая коллекция

foreach ($ клиентов как $ клиентов) {
$ orders = $ orders->слияния ($ клиент->заказы);
}

обратный просмотр (‘admin.reports.orders’, [‘orders’ => $ заказов]);
}
}

Сладкий! И что еще важнее, элегантно, красиво. ����

К сожалению, это ужасный способ написания кода на Laravel.

Вот почему.

Когда мы просим ORM искать указанных клиентов, генерируется такой SQL-запрос:

ВЫБЕРИТЕ * ОТ ПОКУПАТЕЛЕЙ, ГДЕ ВХОДИТ (22, 45, 34, …);

Что именно так, как и ожидалось. В результате все возвращенные строки сохраняются в коллекции $ Customers внутри функции контроллера..

Теперь мы переходим к каждому клиенту по очереди и получаем их заказы. Это выполняет следующий запрос . . .

SELECT * FROM заказов WHERE customer_id = 22;

. . . столько раз, сколько есть клиентов.

Другими словами, если нам нужно получить данные заказа для 1000 клиентов, общее количество выполненных запросов к базе данных будет равно 1 (для выборки всех данных клиентов) + 1000 (для выборки данных заказа для каждого клиента) = 1001. Это отсюда и название n + 1.

Можем ли мы сделать лучше? Безусловно! Используя так называемую загрузку, мы можем заставить ORM выполнить JOIN и вернуть все необходимые данные в одном запросе! Нравится:

$ orders = Customer :: findMany ($ ids)->с ( «Заказы»)->получить();

Результирующая структура данных является вложенной, конечно, но данные заказа могут быть легко извлечены. Результирующий одиночный запрос, в данном случае, выглядит примерно так:

ВЫБРАТЬ * ОТ ПОКУПАТЕЛЕЙ ВНУТРЕННЕЕ СОЕДИНЕНИЕ заказы ВКЛ customer.id = orders.customer_id WHERE customer.id IN (22, 45, …)

Конечно, один запрос лучше, чем тысяча дополнительных запросов. Представьте, что произойдет, если обработать 10 000 клиентов! Или не дай Бог, если мы тоже хотим показывать предметы, содержащиеся в каждом заказе! Помните, название техники очень хочется загружать, и это почти всегда хорошая идея..

Кэшировать конфигурацию!

Одной из причин гибкости Laravel являются тонны файлов конфигурации, которые являются частью инфраструктуры. Хотите изменить, как / где хранятся изображения?

Ну, просто измените файл config / filesystems.php (по крайней мере, на момент написания). Хотите работать с несколькими драйверами очереди? Не стесняйтесь, чтобы описать их в config / queue.php. Я только посчитал и обнаружил, что есть 13 файлов конфигурации для различных аспектов платформы, гарантируя, что вы не будете разочарованы, независимо от того, что вы хотите изменить.

Учитывая природу PHP, каждый раз, когда приходит новый веб-запрос, Laravel просыпается, загружает все и анализирует все эти файлы конфигурации, чтобы выяснить, как в этот раз поступить иначе. За исключением того, что это глупо, если ничего не изменилось за последние несколько дней! Перестройка конфигурации при каждом запросе – это пустая трата времени, которого можно (на самом деле, нужно) избежать, а выходом является простая команда, которую предлагает Laravel:

Конфигурация php artisan: кеш

Это объединяет все доступные конфигурационные файлы в один и кеш где-то для быстрого поиска. В следующий раз, когда появится веб-запрос, Laravel просто прочитает этот файл и приступит к работе..

Тем не менее, кэширование конфигурации – чрезвычайно деликатная операция, которая может взорваться у вас на лице. Самое важное, что после того, как вы введете эту команду, функция env () будет вызываться везде, кроме файлов конфигурации, и будет возвращаться ноль!

Это имеет смысл, когда вы думаете об этом. Если вы используете кэширование конфигурации, вы говорите фреймворку: «Вы знаете, что, я думаю, я все настроил правильно и уверен на 100%, что не хочу, чтобы они менялись». Другими словами, вы ожидаете, что среда останется статичной, для чего нужны файлы .env.

С учетом вышесказанного, вот некоторые железные, священные, нерушимые правила кэширования конфигурации:

  1. Делайте это только в производственной системе.
  2. Делайте это, только если вы действительно, действительно уверены, что хотите заморозить конфигурацию.
  3. Если что-то пойдет не так, отмените настройку с помощью php artisan cache: clear
  4. Молитесь, чтобы ущерб, нанесенный бизнесу, не был значительным!

Уменьшить автозагрузку сервисов

Чтобы быть полезным, Laravel загружает массу услуг, когда просыпается. Они доступны в файле config / app.php как часть ключа массива «provider». Давайте посмотрим на то, что у меня есть в моем случае:

/ *
|————————————————————————–
| Автозагрузочные сервис-провайдеры
|————————————————————————–
|
| Поставщики услуг, перечисленные здесь, будут автоматически загружены на
| запрос к вашей заявке. Не стесняйтесь добавлять свои собственные услуги в
| этот массив для предоставления расширенной функциональности вашим приложениям.
|
* /

«провайдеры» => [

/ *
* Поставщики услуг Laravel Framework…
* /
Осветите \ Auth \ AuthServiceProvider :: класс,
Осветите \ Broadcasting \ BroadcastServiceProvider :: класс,
Осветите \ автобус \ BusServiceProvider :: класс,
Осветите \ Cache \ CacheServiceProvider :: класс,
Осветите \ Foundation \ провайдеры \ ConsoleSupportServiceProvider :: класс,
Осветите \ Cookie \ CookieServiceProvider :: класс,
Осветить \ Database \ DatabaseServiceProvider :: класс,
Осветите \ Encryption \ EncryptionServiceProvider :: класс,
Осветите \ Filesystem \ FilesystemServiceProvider :: класс,
Осветите \ Foundation \ провайдеры \ FoundationServiceProvider :: класс,
Осветить \ HASHING \ HashServiceProvider :: класс,
Осветите \ почта \ MailServiceProvider :: класс,
Осветите \ Уведомления \ NotificationServiceProvider :: класс,
Осветите \ Разбивка \ PaginationServiceProvider :: класс,
Осветите \ Pipeline \ PipelineServiceProvider :: класс,
Осветите \ Queue \ QueueServiceProvider :: класс,
Осветите \ Redis \ RedisServiceProvider :: класс,
Осветите \ Auth \ Пароли \ PasswordResetServiceProvider :: класс,
Осветите \ Session \ SessionServiceProvider :: класс,
Осветите \ Translation \ TranslationServiceProvider :: класс,
Осветите \ Validation \ ValidationServiceProvider :: класс,
Осветите \ View \ ViewServiceProvider :: класс,

/ *
* Поставщики услуг пакета…
* /

/ *
* Поставщики услуг приложений…
* /
App \ провайдеры \ AppServiceProvider :: класс,
App \ провайдеры \ AuthServiceProvider :: класс,
// App \ Providers \ BroadcastServiceProvider :: class,
App \ провайдеры \ EventServiceProvider :: класс,
App \ провайдеры \ RouteServiceProvider :: класс,

],

Я еще раз посчитал, и в списке 27 услуг! Теперь вам могут понадобиться все из них, но вряд ли.

Например, в данный момент я создаю REST API, что означает, что мне не нужен Session Service Provider, View Service Provider и т.д. Я также могу отключить Auth Service Provider, Pagination Service Provider, Provider Service Provider и т. Д. В целом, почти половина из них не нужны для моего случая использования.

Внимательно изучите ваше приложение. Нужны ли все эти поставщики услуг? Но ради всего святого, пожалуйста, не слепо комментируйте эти сервисы и продвигайтесь в производство! Запустите все тесты, проверьте вещи вручную на устройствах разработки и постановки и будьте очень параноиком, прежде чем нажимать на курок. ��

Будьте мудры со стеками промежуточного программного обеспечения

Когда вам нужна некоторая пользовательская обработка входящего веб-запроса, создание нового промежуточного программного обеспечения является ответом. Теперь соблазнительно открыть app / Http / Kernel.php и вставить промежуточное ПО в стек веб или API; таким образом, он становится доступным через приложение, и если он не делает что-то навязчивое (например, ведение журнала или уведомление).

Однако, по мере роста приложения, эта коллекция глобального промежуточного программного обеспечения может стать тихим бременем для приложения, если все (или большинство) из них присутствуют в каждом запросе, даже если для этого нет бизнес-причин..

Другими словами, будьте осторожны с тем, где вы добавляете / применяете новое промежуточное ПО. Может быть удобнее добавлять что-то глобально, но в долгосрочной перспективе снижение производительности очень велико. Я знаю, какую боль вам придется испытать, если вы будете выборочно применять промежуточное ПО каждый раз, когда происходят новые изменения, но я бы с готовностью принял эту рекомендацию и рекомендовал бы ее!

Избегайте ОРМ (время от времени)

Хотя Eloquent делает многие аспекты взаимодействия с БД приятными, это достигается за счет скорости. Будучи картографом, ORM не только должен извлекать записи из базы данных, но и создавать экземпляры объектов модели и гидрировать (заполнять их) их данными столбцов..

Таким образом, если вы выполняете простое $ users = User :: all () и, скажем, есть 10 000 пользователей, платформа извлечет 10 000 строк из базы данных и внутренне сделает 10 000 новых пользователей () и заполнит их свойства соответствующими данными. , Это огромный объем работы, выполняемой за кулисами, и если база данных находится там, где ваше приложение становится узким местом, обходить ORM иногда является хорошей идеей..

Это особенно актуально для сложных SQL-запросов, где вам нужно много раз прыгать и делать замыкания при замыканиях, но при этом получать эффективный запрос. В таких случаях предпочтительнее выполнить DB :: raw () и написать запрос вручную..

Проходя мимо это Изучение производительности даже для простых вставок Eloquent значительно медленнее с ростом числа записей:

Максимально используйте кеширование

Один из самых секретов оптимизации веб-приложений – это кэширование..

Для непосвященных кэширование означает предварительные вычисления и хранение дорогостоящих результатов (дорогостоящих с точки зрения использования процессора и памяти) и просто возвращает их при повторении одного и того же запроса..

Например, в магазине электронной коммерции он может встретить товары из 2 миллионов, большую часть времени люди интересуются свежими товарами, в определенном ценовом диапазоне и для определенной возрастной группы. Запрашивать эту информацию в базе данных бесполезно – поскольку запрос меняется не часто, лучше хранить эти результаты там, где мы можем быстро получить к ним доступ..

Laravel имеет встроенную поддержку для нескольких типов кэширование. В дополнение к использованию драйвера кэширования и созданию системы кэширования с нуля, вы можете использовать некоторые пакеты Laravel, которые упрощают кеширование моделей, кеширование запросов, так далее.

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

Предпочитаю кэширование в памяти

Когда вы кешируете что-то в Laravel, у вас есть несколько вариантов того, где хранить результирующие вычисления, которые необходимо кэшировать. Эти параметры также известны как кеш драйверов. Таким образом, хотя возможно и совершенно разумно использовать файловую систему для хранения результатов кэширования, на самом деле кеширование не совсем такое..

В идеале вы хотите использовать кэш в оперативной памяти (полностью в оперативной памяти), такой как Redis, Memcached, MongoDB и т. Д., Чтобы при более высоких нагрузках кэширование служило жизненно важным использованием, а не становилось узким местом..

Теперь вы можете подумать, что наличие SSD-диска – это почти то же самое, что и использование флешки, но даже не близко. Даже неформальный тесты показать, что по скорости ОЗУ превосходит SSD в 10-20 раз.

Моя любимая система кэширования – Redis. Это смехотворно быстро (100 000 операций чтения в секунду – это обычное явление), а для очень больших систем кеша может быть преобразовано в кластер с легкостью.

Кэшировать маршруты

Как и в конфигурации приложения, маршруты не меняются со временем и являются идеальным кандидатом для кэширования. Это особенно верно, если вы не можете переносить большие файлы, такие как я, и в итоге разбиваете свои web.php и api.php на несколько файлов. Одна команда Laravel объединяет все доступные маршруты и сохраняет их под рукой для последующего доступа:

php ремесленный маршрут: кеш

А когда вы добавите или измените маршруты, просто выполните:

маршрут ремесленника php: очистить

Оптимизация изображений и CDN

Изображения являются сердцем и душой большинства веб-приложений. По совпадению, они также являются крупнейшими потребителями пропускной способности и одной из главных причин медленных приложений / веб-сайтов. Если вы просто наивно храните загруженные изображения на сервере и отправляете их обратно в ответах HTTP, вы упускаете огромную возможность оптимизации.

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

Вместо этого перейдите к решению, как Cloudinary который автоматически изменяет размеры и оптимизирует изображения на лету.

Если это невозможно, используйте что-то вроде Cloudflare для кэширования и обслуживания изображений, пока они хранятся на вашем сервере..

И если даже это невозможно, небольшая доработка программного обеспечения вашего веб-сервера для сжатия ресурсов и направления браузера посетителя для кеширования, имеет большое значение. Вот как будет выглядеть фрагмент конфигурации Nginx:

сервер {

# файл урезан

# Настройки сжатия gzip
GZIP на;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied любой;
gzip_vary on;

# контроль кэша браузера
location ~ * \. (ico | css | js | gif | jpeg | jpg | png | woff | ttf | otf | svg | woff2 | eot) $ {
истекает 1d;
access_log off;
add_header Прагма public;
add_header Cache-Control "общедоступный, максимальный возраст = 86400";
}
}

Я знаю, что оптимизация изображений не имеет ничего общего с Laravel, но это такой простой и мощный трюк (и им так часто пренебрегают), который не мог с собой поделать.

Оптимизация автозагрузчика

Автозагрузка – это аккуратная, не очень старая функция в PHP, которая, возможно, спасла язык от гибели. Тем не менее, процесс поиска и загрузки соответствующего класса путем расшифровки заданной строки пространства имен занимает время и его можно избежать в производственных развертываниях, где желательна высокая производительность. Еще раз, у Laravel есть решение для одной команды:

установка композитора –optimize-autoloader –no-dev

Подружиться с очередями

Очереди это то, как вы обрабатываете вещи, когда их много, и каждая из них занимает несколько миллисекунд. Хорошим примером является отправка электронных писем – широко распространенный вариант использования в веб-приложениях состоит в том, чтобы отбрасывать несколько уведомлений по электронной почте, когда пользователь выполняет некоторые действия.

Например, во вновь выпущенном продукте вы можете захотеть, чтобы руководство компании (около 6-7 адресов электронной почты) получало уведомление всякий раз, когда кто-то размещает заказ выше определенной стоимости. Предполагая, что ваш почтовый шлюз может ответить на ваш SMTP-запрос за 500 мс, мы говорим о хорошем 3-4-секундном ожидании пользователя, прежде чем сработает подтверждение заказа. Очень плохой кусок UX, я уверен согласна.

Решение проблемы – сохранять задания по мере их поступления, сообщать пользователю, что все прошло хорошо, и обрабатывать их (несколько секунд) позже. Если есть ошибка, задания в очереди могут быть повторены несколько раз, прежде чем они будут объявлены с ошибками.

Кредиты: Microsoft.com

Хотя система очередей немного усложняет настройку (и добавляет некоторые накладные расходы на мониторинг), она необходима в современном веб-приложении..

Оптимизация активов (Laravel Mix)

Для любых внешних ресурсов в вашем приложении Laravel, пожалуйста, убедитесь, что есть конвейер, который компилирует и минимизирует все файлы активов. Те, кому удобна система пакетирования, такая как Webpack, Gulp, Parcel и т. Д., Не должны беспокоиться, но если вы уже этого не делаете, Laravel Mix это серьезная рекомендация.

Mix – это легкая (и при всей честности!) Обертка вокруг Webpack, которая заботится обо всех ваших CSS, SASS, JS и т. Д. Файлах для производства. Типичный файл .mix.js может быть таким маленьким, и он все еще творит чудеса:

const mix = require (‘laravel-mix’);

mix.js (‘resources / js / app.js’, ‘public / js’)
.sass (‘resources / sass / app.scss’, ‘public / css’);

Это автоматически обеспечивает импорт, минимизацию, оптимизацию и весь процесс, когда вы готовы к производству и запускаете серийное производство. Mix заботится не только о традиционных файлах JS и CSS, но и о компонентах Vue и React, которые могут быть в рабочем процессе приложения..

Больше информации Вот!

Вывод

Оптимизация производительности – это больше искусство, чем наука. Знание того, как и сколько нужно делать, важнее, чем что делать. Тем не менее, нет предела тому, сколько и что вы можете оптимизировать в приложении Laravel..

Но что бы вы ни делали, я хотел бы оставить вам несколько прощальных советов: оптимизацию следует проводить, когда есть веская причина, а не потому, что это звучит хорошо или потому что вы параноидально настроены относительно производительности приложений для 100 000+ пользователей, хотя на самом деле всего 10.

Если вы не уверены, нужно ли оптимизировать ваше приложение или нет, вам не нужно выбивать гнездо пословиц. Рабочее приложение, которое кажется скучным, но делает именно то, что нужно, в десять раз более желательно, чем приложение, которое было оптимизировано в гибридный супермашин-мутант, но время от времени терпит неудачу.

И, чтобы новичок стал мастером Laravel, проверьте это онлайн курс.

Пусть ваши приложения будут работать намного быстрее! ��

Jeffrey Wilson Administrator
Sorry! The Author has not filled his profile.
follow me
    Like this post? Please share to your friends:
    Adblock
    detector
    map