優(yōu)化 PHP 和 Laravel 以提高 Web 應(yīng)用的性能

Laravel

轉(zhuǎn)載自 Laravel 論壇:https://learnku.com/laravel/t/47213

Laravel 有很多東西熔恢。但是快不是其中之一疗绣。讓我們學(xué)習(xí)一些優(yōu)化技巧池颈,以加快運(yùn)行速度凡涩!

自從 Laravel 誕生以來(lái)搂鲫,沒(méi)有一個(gè) PHP 開發(fā)人員不受她的影響锄禽。他們是喜歡 Laravel 提供的快速開發(fā)的初級(jí)或中級(jí)開發(fā)人員潜必,或者是由于市場(chǎng)壓力而被迫學(xué)習(xí) Laravel 的高級(jí)開發(fā)人員。

不管怎樣沃但,不可否認(rèn)的是磁滚,Laravel 已經(jīng)振興了 PHP 生態(tài)系統(tǒng)(我確定,如果沒(méi)有 Laravel宵晚,早就離開了 PHP 世界了)垂攘。

Laravel

對(duì) Laravel 的評(píng)價(jià)節(jié)選

但是,由于 Laravel 竭盡全力讓您的事情變得簡(jiǎn)單坝疼,這意味著它在底層做了大量工作搜贤,以確保您作為開發(fā)人員能有一個(gè)舒適的編程體驗(yàn)。 Laravel 所有看似「神奇」的功能都有一層又一層的代碼钝凶,每當(dāng)運(yùn)行一個(gè)功能時(shí)都需要啟動(dòng)這些代碼層仪芒。甚至是一個(gè)簡(jiǎn)單的異常都會(huì)深究到底層 (從錯(cuò)誤那里開始唁影,一直到內(nèi)核):

Laravel

對(duì)于一個(gè)視圖中似乎是編譯錯(cuò)誤的情況,有 18 個(gè)函數(shù)調(diào)用要跟蹤掂名。我個(gè)人遇到過(guò) 40 個(gè)的据沈,如果您使用其他庫(kù)和插件,則可能會(huì)更多饺蔑。

重點(diǎn)是锌介,默認(rèn)情況下,這樣層層嵌套的代碼猾警,使得 Laravel 速度很慢孔祸。

Laravel 有多慢?

說(shuō)實(shí)話发皿,這個(gè)問(wèn)題根本無(wú)法回答崔慧,原因有幾個(gè)。

首先穴墅,目前還沒(méi)有公認(rèn)的惶室、客觀的、合理的標(biāo)準(zhǔn)來(lái)衡量網(wǎng)絡(luò)應(yīng)用的速度玄货。與什么相比更快或更慢皇钞?在什么條件下?

第二松捉,一個(gè) Web 應(yīng)用取決于很多東西(數(shù)據(jù)庫(kù)夹界、文件系統(tǒng)、網(wǎng)絡(luò)惩坑、緩存等)掉盅,所以談?wù)撍俣仁呛苡薮赖摹R粋€(gè)非骋允妫快的 Web 應(yīng)用趾痘,如果有一個(gè)非常慢的數(shù)據(jù)庫(kù),那么它就是一個(gè)非常慢的 Web 應(yīng)用蔓钟。

但這種不確定性正是基準(zhǔn)測(cè)試受歡迎的原因永票。盡管它們毫無(wú)意義(參見(jiàn) 這里這里),但它們提供了一些 參考框架滥沫,幫助我們避免生氣侣集。因此,最好有所保留兰绣,讓我們對(duì) PHP 框架之間的速度有一個(gè)錯(cuò)誤的世分、粗略的認(rèn)識(shí)。

根據(jù)這個(gè)相當(dāng)值得尊敬的 GitHub 源碼缀辩,以下是 PHP 框架的對(duì)比情況臭埋。

Laravel

你可能根本不會(huì)注意到 Laravel 在這里 (即使你真的很努力地瞇著眼睛)踪央, 除非你把你的目光投到最尾部。是的瓢阴,親愛(ài)的朋友們畅蹂,Laravel 排在最后! 現(xiàn)在荣恐,理所當(dāng)然的液斜,這些「框架」中的大多數(shù)都不是很實(shí)用,甚至沒(méi)有什么用處叠穆,但它確實(shí)告訴我們少漆,與其他更流行的框架相比,Laravel 是多么的慢痹束。

通常情況下检疫,這種「慢」在應(yīng)用中不會(huì)出現(xiàn), 因?yàn)槲覀內(nèi)粘5?Web 應(yīng)用很少達(dá)到很高的數(shù)據(jù)量祷嘶。但是一旦達(dá)到了(比如高達(dá) 200-500 以上的并發(fā)量),服務(wù)器就會(huì)開始阻塞而死夺溢。這時(shí)候即使扔再多的硬件也解決不了問(wèn)題论巍,基礎(chǔ)架構(gòu)費(fèi)用迅速攀升,你對(duì)云計(jì)算的崇高理想轟然倒塌风响。

Laravel

不過(guò)嘉汰,嘿嘿,振作起來(lái)吧状勤! 這篇文章并不是講什么不能做鞋怀, 而是講什么可以做。

好消息是持搜, 你可以做很多事情來(lái)讓你的 Laravel 應(yīng)用更快密似。幾倍的速度。 是的葫盼,不是開玩笑残腌。你可以讓同樣的代碼庫(kù)變得快速,每個(gè)月節(jié)省幾百美元的基礎(chǔ)設(shè)施/托管費(fèi)用贫导。 怎么做抛猫?讓我們開始吧。

四種類型的優(yōu)化

在我看來(lái)孩灯,優(yōu)化可以在四個(gè)不同的層面上進(jìn)行(當(dāng)涉及到PHP應(yīng)用時(shí)闺金,就是):

  1. 語(yǔ)言層面:這意味著你使用更快的語(yǔ)言版本,并避免語(yǔ)言中特定的功能/編碼風(fēng)格峰档,使你的代碼速度變慢败匹。
  2. 框架層面:這些是我們?cè)诒疚闹幸婕暗膬?nèi)容匣距。
  3. 基礎(chǔ)設(shè)施層面:調(diào)整你的 PHP 進(jìn)程管理器、Web 服務(wù)器哎壳、數(shù)據(jù)庫(kù)等毅待。
  4. 硬件層面:轉(zhuǎn)向更好、更快归榕、更強(qiáng)大的硬件主機(jī)提供商尸红。

所有這些類型的優(yōu)化都有其存在的意義(例如,php-fpm 的優(yōu)化是非常關(guān)鍵和強(qiáng)大的)刹泄。但本文的重點(diǎn)是純粹的第 2 類優(yōu)化:那些與框架相關(guān)的優(yōu)化外里。

順便說(shuō)一下,這些編號(hào)背后沒(méi)有任何理由特石,也不是一個(gè)公認(rèn)的標(biāo)準(zhǔn)盅蝗。我只是編了這些。請(qǐng)千萬(wàn)不要引用我的話說(shuō):「我們的服務(wù)器需要 type-3 優(yōu)化」姆蘸,否則你的團(tuán)隊(duì)負(fù)責(zé)人會(huì)殺了你墩莫,找到我,然后把我也殺了逞敷。

現(xiàn)在狂秦,我們終于到了應(yīng)許之地。

要注意 n+1 數(shù)據(jù)庫(kù)查詢

n+1 查詢問(wèn)題是使用 ORM 時(shí)常見(jiàn)的問(wèn)題推捐。Laravel 有其強(qiáng)大的 ORM裂问,叫 Eloquent,它是如此的漂亮牛柒,如此的方便堪簿,以至于我們常常忘記了看是怎么回事。

考慮一個(gè)非常常見(jiàn)的場(chǎng)景:顯示指定客戶列表下的所有訂單皮壁。這在電子商務(wù)系統(tǒng)和任何需要顯示與某些實(shí)體相關(guān)的所有實(shí)體的列表中非常常見(jiàn)椭更,

我們可以想象有這樣一個(gè)控制器:

class OrdersController extends Controller
{
    // ...

    public function getAllByCustomers(Request $request, array $ids) {
        $customers = Customer::findMany($ids);
        $orders = collect(); // new collection

        foreach ($customers as $customer) {
            $orders = $orders->merge($customer->orders);
        }

        return view('admin.reports.orders', ['orders' => $orders]);
    }
}

太好了!更重要的是闪彼,優(yōu)雅甜孤,美麗。????

不幸的是畏腕,用 Laravel 編寫這樣的代碼是一種災(zāi)難性的方法缴川。

原因如下。

當(dāng)我們使用 ORM 查找給定的客戶實(shí)體時(shí)描馅,會(huì)生成這樣一個(gè)SQL查詢語(yǔ)句:

SELECT * FROM customers WHERE id IN (22, 45, 34, . . .);

這與預(yù)期的完全一致把夸。結(jié)果,所有返回的行都被存儲(chǔ)在控制器函數(shù)中的集合 $customers 中铭污。

現(xiàn)在我們逐一循環(huán)處理每個(gè)客戶恋日,并獲取他們的訂單膀篮。這將執(zhí)行下面的查詢……

SELECT * FROM orders WHERE customer_id = 22;

……有多少客戶就有多少次。

換句話說(shuō)岂膳,如果我們需要獲取 1000 個(gè)客戶的訂單數(shù)據(jù)誓竿,那么執(zhí)行的數(shù)據(jù)庫(kù)查詢總數(shù)將是1(用于獲取所有客戶的數(shù)據(jù))+1000(用于獲取每個(gè)客戶的訂單數(shù)據(jù))=1001。這就是 n+1 這個(gè)名字的由來(lái)谈截。

我們可以做得更好嗎? 當(dāng)然可以筷屡! 通過(guò)使用預(yù)加載,我們可以強(qiáng)制 ORM 執(zhí)行 JOIN簸喂,并在一次查詢中返回所有需要的數(shù)據(jù)毙死! 就像這樣:

$orders = Customer::findMany($ids)->with('orders')->get();

由此產(chǎn)生的數(shù)據(jù)結(jié)構(gòu)是一個(gè)嵌套結(jié)構(gòu),當(dāng)然喻鳄,但訂單數(shù)據(jù)可以很容易地提取出來(lái)扼倘。在這種情況下,產(chǎn)生的單個(gè)查詢是這樣的除呵。

SELECT * FROM customers INNER JOIN orders ON customers.id = orders.customer_id WHERE customers.id IN (22, 45, ...);

ps:我覺(jué)得原作者理解有誤再菊,預(yù)查詢使用的where in,產(chǎn)生的語(yǔ)句應(yīng)該是這樣:

SELECT * FROM customers WHERE id IN (22, 45, ...);
SELECT * FROM orders WHERE customer_id IN(22, 45, ...);

然后在循環(huán)插入到對(duì)應(yīng)的對(duì)象中。

當(dāng)然竿奏,一次查詢比多查詢一千次要好袄简。想象一下,如果有一萬(wàn)個(gè)客戶要處理泛啸,會(huì)發(fā)生什么情況!或者說(shuō)秃症,如果我們還想顯示每個(gè)訂單中包含的項(xiàng)目候址,那簡(jiǎn)直就是天方夜譚!記住种柑,這個(gè)技術(shù)的名字叫預(yù)加載岗仑,它幾乎在任何時(shí)候都能派上用場(chǎng)。

緩存配置聚请!

Laravel 的靈活性的原因之一是它有大量的配置文件荠雕, 這些文件是框架的一部分。想要改變圖片的存儲(chǔ)方式/位置驶赏?

好吧炸卑,只要修改 config/filesystems.php 文件就可以了(至少寫到這里)。想要使用多個(gè)隊(duì)列驅(qū)動(dòng)煤傍?可以在 config/queue.php 中隨意描述盖文。我剛剛統(tǒng)計(jì)了一下,發(fā)現(xiàn)針對(duì)框架的不同方面有 13 個(gè)配置文件蚯姆,保證你無(wú)論想改什么都不會(huì)失望五续。

Laravel

鑒于 PHP 的特性洒敏,每當(dāng)一個(gè)新的 Web 請(qǐng)求進(jìn)來(lái),Laravel 就會(huì)被喚醒疙驾, 啟動(dòng)所有的東西凶伙, 并解析所有的配置文件來(lái)找出這次該如何做不同的事情。 如果這幾天什么都沒(méi)變它碎,那就太傻了函荣!每次請(qǐng)求都要重建配置文件是一種浪費(fèi),這是可以 (實(shí)際上链韭,必須) 避免的偏竟,解決的辦法是 Laravel 提供的一個(gè)簡(jiǎn)單的命令:

php artisan config:cache

這樣做的目的是把所有可用的配置文件合并成一個(gè)文件,并緩存在某個(gè)地方以便快速檢索敞峭。 下一次有 Web 請(qǐng)求的時(shí)候踊谋,Laravel 會(huì)簡(jiǎn)單地讀取這個(gè)單一的文件并開始工作。

也就是說(shuō)旋讹,配置緩存是一個(gè)極其微妙的操作殖蚕,可能會(huì)在你的面前炸開。最大的陷阱是一旦你發(fā)出這個(gè)命令沉迹,除了配置文件之外睦疫,其他地方的 env() 函數(shù)調(diào)用都會(huì)返回 null

仔細(xì)想想確實(shí)有道理鞭呕。如果你使用配置緩存蛤育,你就是在告訴框架:「你知道嗎,我覺(jué)得我已經(jīng)把東西設(shè)置得很好了葫松,我 100% 確定我不希望它們改變瓦糕。」 換句話說(shuō)腋么,你希望環(huán)境保持靜態(tài)咕娄,這就是 .env 文件的作用。

說(shuō)到這里珊擂,這里有一些鐵定的圣勒、神圣的、不可違背的配置緩存規(guī)則:

  1. 只在生產(chǎn)系統(tǒng)上做摧扇。
  2. 只有在你非常非常確定要凍結(jié)配置的情況下才做圣贸。
  3. 萬(wàn)一出了問(wèn)題,用 php artisan cache:clear 撤銷設(shè)置扳剿。
  4. 祈禱對(duì)企業(yè)造成的損失不是很大旁趟!

減少自動(dòng)加載的服務(wù)

為了幫助大家, Laravel在喚醒時(shí)加載了大量的服務(wù), 這些服務(wù)在 config/app.php 文件中作為 'providers' 數(shù)組鍵的一部分。讓我們來(lái)看看我的情況:

/*
    |--------------------------------------------------------------------------
    | Autoloaded Service Providers
    |--------------------------------------------------------------------------
    |
    | The service providers listed here will be automatically loaded on the
    | request to your application. Feel free to add your own services to
    | this array to grant expanded functionality to your applications.
    |
    */

    'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

    ],

我再一次數(shù)了數(shù),一共列出了 27 項(xiàng)服務(wù)锡搜! 現(xiàn)在橙困,你可能需要所有的服務(wù),但不太可能耕餐。

例如凡傅,我現(xiàn)在正好在構(gòu)建一個(gè) REST API,這意味著我不需要 Session Service Provider肠缔、View Service Provider 等夏跷。而且由于我是按照自己的方式來(lái)做一些事情,而不是按照框架的默認(rèn)值來(lái)做明未,所以我也可以禁用 Auth Service Provider槽华、Pagination Service Provider、Translation Service Provider 等趟妥∶ㄌ總而言之,對(duì)于我的用例來(lái)說(shuō)披摄,這些幾乎有一半是不必要的亲雪。

仔細(xì)審視一下你的應(yīng)用吧。它是否需要所有這些服務(wù)提供者疚膊?但是看在上帝的份上义辕,請(qǐng)不要盲目地注釋掉這些服務(wù),然后推送到生產(chǎn)中去寓盗! 運(yùn)行所有的測(cè)試灌砖,在開發(fā)機(jī)和暫存機(jī)上手動(dòng)檢查,并且在扣動(dòng)扳機(jī)之前要非常非常偏執(zhí)傀蚌。

明智地使用中間件堆棧周崭。

當(dāng)你需要對(duì)傳入的 Web 請(qǐng)求進(jìn)行一些自定義處理時(shí),創(chuàng)建一個(gè)新的中間件就是答案≡牛現(xiàn)在,打開 app/Http/Kernel.php 并將中間件粘在 webapi 堆棧中是很有誘惑力的美澳;這樣一來(lái)销部,它就會(huì)在整個(gè)應(yīng)用程序中變得可用,而且如果它沒(méi)有做一些侵入性的事情(例如制跟,像日志或通知)舅桩。

然而,隨著應(yīng)用程序的增長(zhǎng)雨膨,如果所有(或大多數(shù))這些全局中間件都存在于每個(gè)請(qǐng)求中擂涛,那么這個(gè)全局中間件的集合可能會(huì)成為應(yīng)用程序的一個(gè)無(wú)聲負(fù)擔(dān),即使沒(méi)有業(yè)務(wù)原因聊记。

換句話說(shuō)撒妈,要小心你在哪里添加/應(yīng)用新的中間件恢暖。在全局范圍內(nèi)添加一些東西可能會(huì)更方便,但從長(zhǎng)遠(yuǎn)來(lái)看狰右,性能懲罰是非常高的杰捂。我知道如果每次有新的變化都要有選擇地應(yīng)用中間件,你要承受的痛苦棋蚌,但這是我心甘情愿承受的痛苦嫁佳,也是我所推薦的!

避免使用 ORM (有時(shí))

雖然 Eloquent 讓 DB 交互的很多方面變得愉悅,但它是以速度為代價(jià)的谷暮。作為一個(gè)映射器蒿往,ORM 不僅要從數(shù)據(jù)庫(kù)中獲取記錄,還要實(shí)例化模型對(duì)象湿弦,并用列數(shù)據(jù)對(duì)其進(jìn)行填充瓤漏。

所以,如果你做一個(gè)簡(jiǎn)單的 $users = User::all()省撑,比如有10000個(gè)用戶赌蔑,框架會(huì)從數(shù)據(jù)庫(kù)中獲取 10000 行記錄,并在內(nèi)部做 10000 個(gè) new User()竟秫,并用相關(guān)數(shù)據(jù)填充他們的屬性娃惯。這是大量的工作在幕后進(jìn)行,如果數(shù)據(jù)庫(kù)是你的應(yīng)用成為瓶頸的地方肥败,繞過(guò) ORM 有時(shí)是個(gè)好主意趾浅。

這對(duì)于復(fù)雜的 SQL 查詢來(lái)說(shuō)尤其如此,在這種情況下馒稍,你必須跳很多的圈子皿哨,寫一個(gè)又一個(gè)的閉包,但最終還是能得到一個(gè)高效的查詢纽谒。在這種情況下证膨,最好做一個(gè) DB::raw(),然后手工寫查詢鼓黔。

根據(jù) 這個(gè) 的性能研究, 即使是簡(jiǎn)單的插入央勒, Eloquent 也會(huì)隨著記錄數(shù)量的增加而變慢:

Laravel

盡量使用緩存

Web 應(yīng)用優(yōu)化中最保守的秘密之一就是緩存。

對(duì)于新手來(lái)說(shuō)澳化,緩存的意思是預(yù)先計(jì)算和存儲(chǔ)昂貴的結(jié)果(昂貴的 CPU 和內(nèi)存使用量)崔步,并在重復(fù)相同的查詢時(shí)簡(jiǎn)單地返回。

例如缎谷,在一個(gè)電商商店里井濒,可能會(huì)遇到,在 200 萬(wàn)種產(chǎn)品中,大多數(shù)時(shí)候人們都會(huì)對(duì)那些新鮮出爐的瑞你、在一定價(jià)格范圍內(nèi)的酪惭、針對(duì)特定年齡段的產(chǎn)品感興趣。在數(shù)據(jù)庫(kù)中查詢這些信息是很浪費(fèi)的——因?yàn)椴樵兊膬?nèi)容不會(huì)經(jīng)常變化捏悬,所以最好把這些結(jié)果存儲(chǔ)在我們可以快速訪問(wèn)的地方撞蚕。

Laravel 內(nèi)置支持多種類型的緩存。除了使用緩存驅(qū)動(dòng)和從底層構(gòu)建緩存系統(tǒng)外过牙,你可能還想使用一些Laravel 包甥厦,方便模型緩存查詢緩存等寇钉。

但是請(qǐng)注意, 在一定的簡(jiǎn)化用例之外, 預(yù)制的緩存包可能會(huì)帶來(lái)更多的問(wèn)題, 而不是解決這些問(wèn)題.

優(yōu)先選擇內(nèi)存緩存

當(dāng)你在 Laravel 中緩存一些東西時(shí)刀疙, 你有幾個(gè)選項(xiàng)可以選擇將需要緩存的計(jì)算結(jié)果存儲(chǔ)在哪里。這些選項(xiàng)也被稱為 緩存驅(qū)動(dòng)扫倡。所以谦秧, 雖然使用文件系統(tǒng)來(lái)存儲(chǔ)緩存結(jié)果是可能的,也是完全合理的撵溃,但這并不是緩存的真正目的疚鲤。

理想情況下,你希望使用內(nèi)存中(完全活在 RAM 中)的緩存缘挑,比如 Redis集歇、Memcached、MongoDB 等语淘,這樣在較高的負(fù)載下诲宇,緩存就能起到至關(guān)重要的作用,而不是自己成為瓶頸惶翻。

現(xiàn)在姑蓝,你可能會(huì)認(rèn)為擁有 SSD 磁盤和使用 RAM 棒幾乎是一樣的,但還差得遠(yuǎn)吕粗。即使是非正式的 基準(zhǔn)測(cè)試也顯示纺荧,在速度方面,RAM優(yōu)于SSD的10-20倍颅筋。

在緩存方面虐秋,我最喜歡的系統(tǒng)是 Redis。它的速度 快得離譜(每秒 10 萬(wàn)次讀取操作是很常見(jiàn)的)垃沦,對(duì)于非常大的緩存系統(tǒng),可以很容易地演變成一個(gè) 集群用押。

緩存路由

就像應(yīng)用程序的配置一樣肢簿,路由不會(huì)隨著時(shí)間的推移而改變,是緩存的理想選擇。如果你像我一樣無(wú)法忍受大文件池充,并且最終把你的 web.phpapi.php 分割成幾個(gè)文件的話桩引,這一點(diǎn)尤其適用。 一個(gè)簡(jiǎn)單的Laravel命令就可以把所有可用的路由打包并保存起來(lái)收夸, 方便以后的訪問(wèn):

php artisan route:cache

而當(dāng)你最終要增加或改變路由時(shí)坑匠,只需這樣做即可。

php artisan route:clear

圖像優(yōu)化和 CDN

圖片是大多數(shù)網(wǎng)絡(luò)應(yīng)用的核心和靈魂卧惜。巧合的是厘灼,它們也是最大的帶寬消耗者,也是導(dǎo)致應(yīng)用程序/網(wǎng)站速度慢的最大原因之一咽瓷。如果你只是簡(jiǎn)單地將上傳的圖片天真地存儲(chǔ)在服務(wù)器上设凹,然后以 HTTP 響應(yīng)的方式發(fā)送回來(lái),你就會(huì)讓一個(gè)巨大的優(yōu)化機(jī)會(huì)溜走茅姜。

我的第一個(gè)建議是不要在本地存儲(chǔ)圖片——有數(shù)據(jù)丟失的問(wèn)題要處理闪朱,而且取決于你的客戶在哪個(gè)地理區(qū)域,數(shù)據(jù)傳輸可能會(huì)非常緩慢钻洒。

相反奋姿,選擇像 Cloudinary 這樣的解決方案,它可以自動(dòng)動(dòng)態(tài)調(diào)整和優(yōu)化圖像的大小素标。

如果這不可能称诗,使用類似 Cloudflare 的東西來(lái)緩存和服務(wù)圖像,同時(shí)它們存儲(chǔ)在你的服務(wù)器上糯钙。

如果連這一點(diǎn)都做不到粪狼,調(diào)整一下你的網(wǎng)絡(luò)服務(wù)器軟件,壓縮資產(chǎn)并引導(dǎo)訪問(wèn)者的瀏覽器去緩存東西任岸,就會(huì)有很大的不同再榄。下面是一個(gè) Nginx 配置的片段。

server {

   # file truncated

    # gzip compression settings
    gzip on;
    gzip_comp_level 5;
    gzip_min_length 256;
    gzip_proxied any;
    gzip_vary on;

   # browser cache control
   location ~* \.(ico|css|js|gif|jpeg|jpg|png|woff|ttf|otf|svg|woff2|eot)$ {
         expires 1d;
         access_log off;
         add_header Pragma public;
         add_header Cache-Control "public, max-age=86400";
    }
}

我知道圖片優(yōu)化與 Laravel 無(wú)關(guān)享潜, 但這是一個(gè)如此簡(jiǎn)單而強(qiáng)大的技巧 (而且經(jīng)常被忽視)困鸥, 所以我忍不住了。

自動(dòng)加載器優(yōu)化

自動(dòng)加載是 PHP 中一個(gè)整潔的剑按、并不古老的功能疾就,它可以說(shuō)是拯救了這門語(yǔ)言的末日。盡管如此艺蝴,通過(guò)破譯給定的命名空間字符串來(lái)尋找和加載相關(guān)類的過(guò)程是需要時(shí)間的猬腰,在生產(chǎn)部署中,如果需要高性能猜敢,可以避免這個(gè)過(guò)程姑荷。 再一次盒延,Laravel 有一個(gè)單一命令的解決方案來(lái)解決這個(gè)問(wèn)題:

composer install --optimize-autoloader --no-dev

與隊(duì)列交朋友

隊(duì)列 是指當(dāng)有很多事情時(shí),你如何處理這些事情鼠冕,而且每件事情都需要幾毫秒才能完成添寺。一個(gè)很好的例子是發(fā)送電子郵件——在網(wǎng)絡(luò)應(yīng)用中,一個(gè)廣泛的用例是當(dāng)用戶執(zhí)行一些操作時(shí)懈费,發(fā)出幾封通知郵件计露。

例如,在一個(gè)新推出的產(chǎn)品中憎乙,你可能希望每當(dāng)有人下單超過(guò)一定值時(shí)票罐,公司領(lǐng)導(dǎo)層(大約6-7個(gè)電子郵件地址)就會(huì)收到通知。假設(shè)你的郵件網(wǎng)關(guān)能在500ms內(nèi)響應(yīng)你的SMTP請(qǐng)求寨闹,那么在訂單確認(rèn)啟動(dòng)之前胶坠,用戶需要等待3-4秒。一個(gè)非常糟糕的用戶體驗(yàn)繁堡,我相信你會(huì)同意沈善。

補(bǔ)救的辦法是在任務(wù)進(jìn)來(lái)的時(shí)候就把它們存儲(chǔ)起來(lái),告訴用戶一切都很順利椭蹄,然后再處理它們(幾秒鐘)闻牡。如果出現(xiàn)錯(cuò)誤,在宣布失敗之前绳矩,排隊(duì)的任務(wù)可以重試幾次罩润。

Laravel

雖然隊(duì)列系統(tǒng)使設(shè)置復(fù)雜化了一些 (并增加了一些監(jiān)控開銷), 但它在現(xiàn)代Web應(yīng)用中是不可缺少的翼馆。

資源優(yōu)化 (Laravel Mix)

對(duì)于你的 Laravel 應(yīng)用中的任何前端資源割以,請(qǐng)確保有一個(gè)管道可以編譯和最小化所有的資源文件。 那些對(duì) Webpack应媚,Gulp严沥,Parcel 等打包器系統(tǒng)很熟悉的人不需要費(fèi)心,但如果你還沒(méi)有這樣做中姜,Laravel Mix是一個(gè)可靠的推薦消玄。

Mix 是一個(gè)輕量級(jí)的 (老實(shí)說(shuō),很討人喜歡!) 圍繞Webpack的打包器丢胚,它可以處理你所有的 CSS翩瓜,SASS,JS 等文件携龟。 一個(gè)典型的 .mix.js 文件可以像這樣小兔跌,但仍然可以發(fā)揮出巨大的作用。

const mix = require('laravel-mix').mix.js('resources/js/app.js', 'public/js');

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

當(dāng)您準(zhǔn)備部署生產(chǎn)環(huán)境并運(yùn)行 npm run production 時(shí)峡蟋,它將自動(dòng)處理導(dǎo)入浮定,最小化相满,優(yōu)化以及整個(gè)工作流程。 Mix 不僅關(guān)心傳統(tǒng)的 JS和 CSS 文件桦卒,而且還關(guān)心您在應(yīng)用程序工作流程中可能使用的 Vue 和 React 組件。

更多信息參考 這里!

結(jié)論

性能優(yōu)化與其說(shuō)是科學(xué)匿又,不如說(shuō)是藝術(shù) —— 知道如何做以及做多少比做什么更重要方灾。也就是說(shuō),在 Laravel 應(yīng)用中可以優(yōu)化的內(nèi)容和數(shù)量是無(wú)限的碌更。

但無(wú)論您做什么裕偿,我都希望留給您一些臨別的建議 —— 優(yōu)化應(yīng)該在有充分的理由時(shí)進(jìn)行,而不是因?yàn)樗犉饋?lái)不錯(cuò)痛单,也不是因?yàn)槟鷮?duì) 超過(guò) 100,000 個(gè)用戶的應(yīng)用程序的性能抱有偏執(zhí)嘿棘,而實(shí)際上只有 10 個(gè)用戶。

如果你不確定是否需要優(yōu)化你的應(yīng)用旭绒,那你就不要去捅這個(gè)馬蜂窩鸟妙。一個(gè)能正常運(yùn)轉(zhuǎn)的應(yīng)用,雖然有時(shí)感覺(jué)很無(wú)趣挥吵,但卻做了它必須做的事情重父,這比一個(gè)優(yōu)化成突變體混合型超級(jí)機(jī)器卻時(shí)不時(shí)會(huì)失敗的應(yīng)用要可取十倍。

討論請(qǐng)前往專業(yè)的 Laravel 論壇:https://learnku.com/laravel/t/47213

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末忽匈,一起剝皮案震驚了整個(gè)濱河市房午,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丹允,老刑警劉巖郭厌,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異雕蔽,居然都是意外死亡折柠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門萎羔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)液走,“玉大人,你說(shuō)我怎么就攤上這事贾陷≡悼簦” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵髓废,是天一觀的道長(zhǎng)巷懈。 經(jīng)常有香客問(wèn)我,道長(zhǎng)慌洪,這世上最難降的妖魔是什么顶燕? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任凑保,我火速辦了婚禮,結(jié)果婚禮上涌攻,老公的妹妹穿的比我還像新娘欧引。我一直安慰自己,他們只是感情好恳谎,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布芝此。 她就那樣靜靜地躺著,像睡著了一般因痛。 火紅的嫁衣襯著肌膚如雪婚苹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天鸵膏,我揣著相機(jī)與錄音膊升,去河邊找鬼。 笑死谭企,一個(gè)胖子當(dāng)著我的面吹牛廓译,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赞咙,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼责循,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了攀操?” 一聲冷哼從身側(cè)響起院仿,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎速和,沒(méi)想到半個(gè)月后歹垫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颠放,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年排惨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碰凶。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡暮芭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出欲低,到底是詐尸還是另有隱情辕宏,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布砾莱,位于F島的核電站瑞筐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏腊瑟。R本人自食惡果不足惜聚假,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一块蚌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧膘格,春花似錦峭范、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至政敢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胚迫,已是汗流浹背喷户。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留访锻,地道東北人褪尝。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像期犬,于是被迫代替她去往敵國(guó)和親河哑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345