Как да оптимизираме PHP Laravel уеб приложение за висока производителност?

Laravel е много неща. Но бързият не е един от тях. Нека научим някои трикове на търговията, за да я ускорим!


Нито един PHP разработчик не е докоснат от Laravel Тези дни. Те са или младши или средни разработчици, които обичат бързото развитие, което предлага Laravel, или са старши разработчици, които са принудени да учат Laravel поради натиска на пазара.

Така или иначе, няма отричане, че Laravel е съживил PHP екосистемата (аз със сигурност бих напуснал света на PHP отдавна, ако 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, наречен Красноречив, който е толкова красив, толкова удобен, че често забравяме да погледнем какво става.

Помислете за много често срещан сценарий: показване на списъка на всички поръчки, направени от даден списък с клиенти. Това е доста често в системите за електронна търговия и във всички интерфейси за отчитане като цяло, където трябва да показваме всички субекти, свързани с някои субекти.

В Laravel можем да си представим функция на контролер, която върши работата по този начин:

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

обществена функция getAllByCustomers (Поискайте $ заявка, масив $ ids) {
$ клиенти = Клиент :: findMany ($ ids);
$ поръчки = събиране (); // нова колекция

foreach ($ клиенти като $ клиент) {
$ поръчки = $ поръчки->сливане ($ клиент->поръчки);
}

изглед за връщане (‘admin.reports.orders’, [‘поръчки’ => $ поръчки]);
}
}

Сладка! И по-важното – елегантен, красив. ����

За съжаление, това е пагубен начин да се напише код в Laravel.

Ето защо.

Когато помолим ORM да търси дадените клиенти, се генерира SQL заявка като тази:

ИЗБЕРЕТЕ * ОТ клиентите, КЪДЕТО ID IN (22, 45, 34,.);

Което е точно както се очаква. В резултат на това всички върнати редове се съхраняват в клиентите на колекция $ във функцията на контролера.

Сега променяме всеки клиент един по един и получаваме техните поръчки. Това изпълнява следната заявка . . .

ИЗБЕРЕТЕ * ОТ поръчки, КЪДЕ customer_id = 22;

. . . колкото пъти има клиенти.

С други думи, ако трябва да получим данни за поръчката за 1000 клиенти, общият брой изпълнени заявки към база данни ще бъде 1 (за извличане на всички данни на клиентите) + 1000 (за получаване на данни за поръчка за всеки клиент) = 1001. Това е откъде идва името n + 1.

Можем ли да се справим по-добре? Разбира се! Използвайки това, което е известно като нетърпеливо зареждане, можем да принудим ORM да извърши ПРИЛОЖЕНИЕ и да върне всички необходими данни в една заявка! Като този:

$ поръчки = Клиент :: findMany ($ ids)->с ( “поръчки”)->получите ();

Получената структура на данни е вложена, но сигурно, но данните от поръчката могат лесно да бъдат извлечени. Получената единична заявка в този случай е нещо подобно:

ИЗБЕРЕТЕ * ОТ клиентите ВЪТРЕШНИ РЕГИСТРАЦИИ НА ЗАДЪЛЖЕНИЯ НА клиентите.id = поръчки.устройство_ид КЪДЕ клиентите.id В (22, 45, …);

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

Кеширайте конфигурацията!

Една от причините за гъвкавостта на Laravel са тоновете конфигурационни файлове, които са част от рамката. Искате да промените как / къде се съхраняват изображенията?

Е, просто променете файла config / filesystems.php (поне при писане). Искате ли да работите с множество драйвери на опашки? Чувствайте се свободни да ги опишете в config / queue.php. Току-що преброих и установих, че има 13 конфигурационни файла за различни аспекти на рамката, гарантирайки, че няма да останете разочаровани, независимо какво искате да промените.

Като се има предвид естеството на PHP, всеки път, когато се появи нова заявка за уеб, Laravel се събужда, стартира всичко и анализира всички тези конфигурационни файлове, за да разбере как да правим нещата по различен начин този път. Само дето е глупаво, ако през последните дни нищо не се е променило! Възстановяването на конфигурацията при всяка заявка е загуба, която може да бъде (всъщност трябва да бъде) избягвана, а изходът е проста команда, която Laravel предлага:

php artisan config: кеш

Това се състои в това да комбинирате всички налични конфигурационни файлове в един и кешът е някъде за бързо извличане. Следващия път, когато има заявка за уеб, Laravel просто ще прочете този единствен файл и ще продължи.

Това каза, кеширането на конфигурация е изключително деликатна операция, която може да се взриви в лицето ви. Най-голямото нещо е, че след като сте издали тази команда, функцията env () се обажда отвсякъде, с изключение на конфигурационните файлове, ще се върне нула!

Има смисъл, когато мислиш за това. Ако използвате кеширане на конфигурация, казвате на рамката: „Знаеш ли какво, мисля, че съм настроил нещата добре и съм 100% сигурен, че не искам те да се променят.“ С други думи, очаквате средата да остане статична, за което са предназначени .env файловете.

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

  1. Правете го само на производствена система.
  2. Правете го само ако сте наистина, наистина сте сигурни, че искате да замразите конфигурацията.
  3. В случай че нещо се обърка, отменете настройката с php artisan кеш: изчистете
  4. Молете се, че щетите, нанесени на бизнеса, не са били значителни!

Намалете автоматично заредените услуги

За да бъде полезен, Laravel зарежда един тон услуги, когато се събуди. Те са на разположение във файла config / app.php като част от ключа на масива „доставчици“. Нека да разгледаме какво имам в моя случай:

/ *
|————————————————————————–
| Автоматично заредени доставчици на услуги
|————————————————————————–
|
| Изброените тук доставчици на услуги ще бъдат автоматично заредени на
| искане към вашата кандидатура. Чувствайте се свободни да добавите свои собствени услуги към
| този масив за предоставяне на разширена функционалност на вашите приложения.
|
* /

‘доставчици’ => [

/ *
* Доставчици на рамкови услуги на Laravel…
* /
Illuminate \ упълномощаване \ AuthServiceProvider :: клас,
Illuminate \ Broadcasting \ BroadcastServiceProvider :: клас,
Illuminate \ автобус \ BusServiceProvider :: клас,
Illuminate \ Cache \ CacheServiceProvider :: клас,
Illuminate \ фондация \ доставчици \ ConsoleSupportServiceProvider :: клас,
Illuminate \ Cookie \ CookieServiceProvider :: клас,
Illuminate \ Database \ DatabaseServiceProvider :: клас,
Illuminate \ Encryption \ EncryptionServiceProvider :: клас,
Illuminate \ файлова система \ FilesystemServiceProvider :: клас,
Illuminate \ фондация \ доставчици \ FoundationServiceProvider :: клас,
Illuminate \ хеширане \ HashServiceProvider :: клас,
Illuminate \ Mail \ MailServiceProvider :: клас,
Illuminate \ Известия \ NotificationServiceProvider :: клас,
Illuminate \ Pagination \ PaginationServiceProvider :: клас,
Illuminate \ Pipeline \ PipelineServiceProvider :: клас,
Illuminate \ Queue \ QueueServiceProvider :: клас,
Illuminate \ Redis \ RedisServiceProvider :: клас,
Illuminate \ упълномощаване \ пароли \ PasswordResetServiceProvider :: клас,
Illuminate \ Session \ SessionServiceProvider :: клас,
Illuminate \ преводи \ TranslationServiceProvider :: клас,
Illuminate \ Validation \ ValidationServiceProvider :: клас,
Illuminate \ View \ ViewServiceProvider :: клас,

/ *
* Доставчици на пакетни услуги…
* /

/ *
* Доставчици на услуги за приложения…
* /
App \ доставчици \ AppServiceProvider :: клас,
App \ доставчици \ AuthServiceProvider :: клас,
// Приложение \ Доставчици \ BroadcastServiceProvider :: клас,
App \ доставчици \ EventServiceProvider :: клас,
App \ доставчици \ RouteServiceProvider :: клас,

],

Още веднъж преброих и има изброени 27 услуги! Сега може да са ви необходими всички, но това е малко вероятно.

Например в момента изграждам REST API, което означава, че не ми трябва доставчик на сесийна услуга, преглед на доставчик на услуги и т.н. И тъй като аз правя някои неща по моя начин и не следя по подразбиране рамката , Мога също да деактивирам доставчика на услуги за автентичност, доставчик на услуги за създаване на страници, доставчик на услуги за превод и т.н. Като цяло почти половината от тях са ненужни за моя случай на употреба.

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

Бъдете мъдри със стекове на междинен софтуер

Когато се нуждаете от някаква персонализирана обработка на входящата уеб заявка, създаването на нов междинен софтуер е отговорът. Сега е изкушаващо да отворите app / Http / Kernel.php и да залепите междинния софтуер в мрежата или api стека; по този начин той става достъпен в приложението и ако не прави нещо натрапчиво (като вписване или известяване например).

Въпреки това, с увеличаването на приложението, тази колекция от глобален междинен софтуер може да се превърне в безшумна тежест за приложението, ако всички (или мнозинство) от тях присъстват във всяка заявка, дори ако няма бизнес причина за това.

С други думи, внимавайте къде добавяте / прилагате нов междинен софтуер. Може да бъде по-удобно да добавите нещо в световен мащаб, но наказанието за изпълнение е много високо в дългосрочен план. Знам болката, която трябва да изтърпите, ако селективно прилагате междинен софтуер всеки път, когато има нова промяна, но това е болка, която с готовност ще приемам и препоръчвам!

Избягвайте ORM (на моменти)

Въпреки че Красноречието прави много аспекти на взаимодействието с БД приятни, това идва с цената на скоростта. Като картограф, ORM не само трябва да извлича записи от базата данни, но и да създава моделни обекти и да гихидратира (попълва ги) с данни от колони..

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

Това е особено вярно за сложни SQL заявки, при които трябва да прескачате много обръчи и да пишете затваряния при затваряне и пак да завършите с ефективна заявка. В такива случаи е за предпочитане да се прави DB :: raw () и да се пише заявката на ръка.

Минавам това проучване на производителността, дори и за прости вмъквания Красноречивото е много по-бавно, тъй като броят на записите се увеличава:

Използвайте кеширането колкото е възможно повече

Една от най-добре пазените тайни на оптимизацията на уеб приложения е кеширането.

За непосветените кеширането означава предварително изчисляване и съхраняване на скъпи резултати (скъпи по отношение на използването на процесора и паметта) и просто връщането им, когато една и съща заявка се повтаря.

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

Laravel има вградена поддръжка за няколко типа кеширане. В допълнение към използването на драйвер за кеширане и изграждане на кеширащата система от самото начало, може да искате да използвате някои пакети Laravel, които улесняват кеширане на модел, кеширане на заявки, и т.н..

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

Предпочитайте кеширане в паметта

Когато кеширате нещо в Laravel, имате няколко опции къде да съхранявате получените изчисления, които трябва да се кешират. Тези опции са известни също като кеш драйвери. Така че, въпреки че е възможно и напълно разумно да използвате файловата система за съхранение на резултати от кеш, не е всъщност какво е кеширането..

В идеалния случай искате да използвате кеш в паметта (живееща изцяло в RAM паметта) като Redis, Memcached, MongoDB и т.н., така че при по-големи натоварвания кеширането служи за жизненоважно използване, а не да се превърне в самото тясно място.

Сега може да си помислите, че да имате SSD диск е почти същото като използването на RAM памет, но дори не е близо. Дори неформални еталони показват, че RAM превъзхожда SSD 10-20 пъти, когато става дума за скорост.

Любимата ми система, когато става дума за кеширане, е Redis. е нелепо бързо (100 000 операции за четене в секунда са често срещани), а за много големи кеш системи могат да се превърнат в a струпване лесно.

Кеширайте маршрутите

Точно като конфигурацията на приложението, маршрутите не се променят много с течение на времето и са идеален кандидат за кеширане. Това е особено вярно, ако не можете да понасяте големи файлове като мен и в крайна сметка разделяте уеб.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 на;

# контрол на кеша на браузъра
местоположение ~ * \. (ico | css | js | gif | jpeg | jpg | png | woff | ttf | otf | svg | woff2 | eot) $ {
изтича 1г;
access_log изключен;
add_header Pragma 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 = изисквам (‘laravel-mix’);

mix.js (‘ресурси / js / app.js’, ‘public / js’)
.sass (‘ресурси / sass / app.scss’, ‘public / css’);

Това автоматично се грижи за вноса, минимизацията, оптимизацията и целия шебанг, когато сте готови за производство и стартирате npm run производство. Mix се грижи не само за традиционните JS и CSS файлове, но и за Vue и React компоненти, които може да имате в работния процес на приложението си.

Повече информация тук!

заключение

Оптимизацията на производителността е повече изкуство, отколкото наука – знанието как и колко да се направи е важно, отколкото какво да се прави. Това каза, че няма край колко и какво можете да оптимизирате в приложението на Laravel.

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

Ако не сте сигурни дали трябва да оптимизирате приложението си или не, не е нужно да ритате гнездото на пословичните стършели. Работещо приложение, което се чувства скучно, но прави точно това, което трябва, е десет пъти по-желателно от приложение, което е оптимизирано в мутант хибриден супермашина, но от време на време пада плоско.

И, за да стане newbiew да стане майстор на 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