1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        基于 RoadRunner 驅(qū)動 Octane 構(gòu)建高性能 Laravel 應(yīng)用

        共 7850字,需瀏覽 16分鐘

         ·

        2021-04-12 12:25

        Laravel Octane 已于昨天發(fā)布了 Beta 版,關(guān)于 Laravel Octane 學院君在之前專門發(fā)布過一篇文章簡單介紹過,這是 Laravel 官方提供的基于 Swoole/RoadRunner 構(gòu)建高性能 Laravel 應(yīng)用的解決方案,現(xiàn)在你可以按照官方文檔安裝這個擴展包并進行測試。

        由于后續(xù)學院君主要精力都在 Golang 上,這里我們以 RoadRunner 為例進行演示。

        Laravel Octane 需要 PHP 8.0+ 及 Laravel 8.35+ 環(huán)境。

        一、安裝 Octane 擴展包

        我們可以通過如下兩條指令安裝 Laravel Octane:

        composer require laravel/octane
        php artisan octane:install

        接下來,我們就可以在 config/octane.php 中指定使用 Swoole 還是 RoadRunner 作為 HTTP 服務(wù)器,默認是 roadrunner

        二、什么是 RoadRunner

        RoadRunner 是一個基于 Go 語言編寫的高性能 PHP 應(yīng)用服務(wù)器,它可以利用 Go 在并發(fā)編程中的優(yōu)勢,基于協(xié)程實現(xiàn)高性能的 HTTP 服務(wù)器,然后將用戶請求轉(zhuǎn)發(fā)給常駐內(nèi)存的 PHP-Worker 進行處理,這樣一來,在原有 PHP 代碼基本不變的情況下,可以充分利用 Go 的高性能和 PHP 的開發(fā)效率打造支持高性能、高并發(fā)的 Web 系統(tǒng):

        更多詳情可以參考 RoadRunner 官方文檔:https://roadrunner.dev/。

        三、通過 Sail 安裝 RoadRunner

        我們可以基于 Sail 的本地 Docker 開發(fā)環(huán)境中安裝 RoadRunner:

        ./vendor/bin/sail up
        ./vendor/bin/sail composer require spiral/roadrunner

        安裝完擴展包后,還要在 Sail 容器環(huán)境中安裝適用于當前 Linux 發(fā)行版本的 RoadRunner 二進制可執(zhí)行文件:

        ./vendor/bin/sail shell
        # 在 Sail shell 環(huán)境中執(zhí)行
        ./vendor/bin/rr get-binary  


        至此,我們就準備好了 RoadRunner 底層環(huán)境,接下來,就可以基于 Octane 來啟動 RoadRunner 服務(wù)器了。

        四、通過 Octane 啟動 RoadRunner

        要實現(xiàn)這個功能,需要自定義 Sail 容器啟動關(guān)聯(lián)文件 supervisor.conf,為此需要先發(fā)布它:

        ./vendor/bin/sail artisan sail:publish

        然后修改 docker/8.0/supervisord.conf 中的 command 指令如下:

        command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --port=80

        這樣一來,Sail 容器就會基于 RoadRunner 作為 PHP 應(yīng)用的 HTTP 服務(wù)器。

        確保項目根目錄下的 rr 具備可執(zhí)行權(quán)限后,重新構(gòu)建 Sail 容器:

        chmod +x ./rr
        ./vendor/bin/sail build

        最后重新啟動 Sail 容器中的服務(wù):

        ./vendor/bin/sail down
        ./vendor/bin/sail up -d

        這個時候,容器中的 Laravel 應(yīng)用就是基于 RoadRunner 驅(qū)動的了。

        基于 Swoole 驅(qū)動 Laravel Octane 的操作流程可以參考 Octane 官方文檔,這里不再單獨演示了。

        五、Octane 日常使用

        監(jiān)聽本地文件變動

        RoadRunner/Swoole 之所以能夠極大提升 Laravel 性能,本質(zhì)上都是將 Laravel 應(yīng)用常駐內(nèi)存了,這樣做的一個代價是犧牲了 PHP 本地調(diào)試的便利性,每次修改文件后需要重啟 RoadRunner/Swoole 服務(wù)器才能讓修改生效。

        為了方便本地開發(fā),Laravel Octane 引入了 --watch 標識告知 Octane 在項目文件發(fā)生變更后自動重啟服務(wù)器,只需要在啟動 Octane 時帶上這個標識即可:

        php artisan octane:start --watch

        該功能依賴本地開發(fā)環(huán)境安裝了 Node,并且安裝了 Chokidar 文件監(jiān)聽庫:

        npm install --save-dev chokidar

        指定 Worker 進程數(shù)和最大處理請求數(shù)

        默認情況下,Octane 會根據(jù)機器 CPU 的內(nèi)核數(shù)來啟動對應(yīng)數(shù)量的請求處理器進程(Worker),你也可以在基于 Octane 啟動服務(wù)器時通過 --workers 參數(shù)手動指定 Worker 數(shù)量:

        php artisan octane:start --workers=4

        PHP 應(yīng)用常駐內(nèi)存帶來的另一個問題是內(nèi)存泄露,你可以通過 --max-request 參數(shù)指定一個 Worker 最多能夠處理的請求數(shù)來解決這個問題:

        php artisan octane:start --max-requests=250

        當超過這個限制后,Octane 會優(yōu)雅重啟該 Worker。

        優(yōu)雅重啟 Worker 進程

        和 Nginx 類似,你可以通過 roload 指令優(yōu)雅重啟所有 PHP Worker 進程:

        php artisan octane:reload

        以上是 RoadRunner/Swoole 驅(qū)動 Octane 服務(wù)器的通用功能,針對 Swoole,Octane 還提供了獨有的并發(fā)編程、定時器、高性能緩存等功能,你可以參考 Octane 文檔了解明細,這里不專門介紹了。

        六、注意事項

        由于一個 Worker 會處理多個請求,而在同一個 Worker 中,只會在初始化時加載一次 Laravel 應(yīng)用,后面的請求會復(fù)用第一次加載的服務(wù)容器(意味著所有服務(wù)提供者的 registerboot 方法只有第一次加載時會被調(diào)用,這就是所謂的「常駐內(nèi)存」),所以我們在切換到基于 Laravel Octane 驅(qū)動 的 HTTP 服務(wù)器時,對于服務(wù)注入要格外小心,不要將后續(xù)會變動的對象以單例模式注入服務(wù)容器,也不要讓有狀態(tài)的數(shù)據(jù)被所有請求共享。

        Octane 會在不同請求間自動處理所有官方框架提供功能的狀態(tài)重置,但是無法重置你自己在業(yè)務(wù)代碼中編寫的全局狀態(tài),這里我們列舉一些常見的容易出問題的幾個典型示例,如果你的業(yè)務(wù)代碼目前存在這些問題,需要進行調(diào)整。

        容器注入

        不要將服務(wù)容器、請求實例或者其他會發(fā)生變動的對象以單例模式注入到某個服務(wù)的構(gòu)造函數(shù):

        use App\Service;

        /**
         * Register any application services.
         *
         * @return void
         */

        public function register()
        {
            $this->app->singleton(Service::class, function ($app) {
                return new Service($app);
            });
        }

        這會導(dǎo)致后續(xù)請求只能解析出初次調(diào)用該 register 方法時傳入構(gòu)造函數(shù)的對象。要解決這個問題,可以通過普通模式注入或者閉包方式注入:

        use App\Service;
        use Illuminate\Container\Container;

        $this->app->bind(Service::class, function ($app) {
            return new Service($app);
        });

        $this->app->singleton(Service::class, function () {
            return new Service(fn () => Container::getInstance());
        });

        這樣一來,每次解析出來的都將是最新的對象實例。

        請求注入

        請求注入和服務(wù)容器類似,因為不同用戶請求對象不同,并且可能帶有認證狀態(tài),所以不能在不同請求之間共享,也就不能作為構(gòu)造函數(shù)參數(shù)以單例模式注入服務(wù)容器:

        use App\Service;

        /**
         * Register any application services.
         *
         * @return void
         */

        public function register()
        {
            $this->app->singleton(Service::class, function ($app) {
                return new Service($app['request']);
            });
        }

        解決思路和服務(wù)容器一樣,通過普通模式注入或閉包模式注入即可:

        use App\Service;

        $this->app->bind(Service::class, function ($app) {
            return new Service($app['request']);
        });

        $this->app->singleton(Service::class, function ($app) {
            return new Service(fn () => $app['request']);
        });

        // 或者,還可以直接在服務(wù)方法中傳入具體請求字段值

        $service->method($request->input('name'));

        對于控制器而言,由于其構(gòu)造函數(shù)也是在服務(wù)注冊初始化期間完成的,所以不要在其構(gòu)造函數(shù)中注入請求對象,但是可以在具體的控制器方法中注入 Illuminate\Http\Request 實例獲取請求信息。

        配置注入

        應(yīng)用配置也是一個會在運行時發(fā)生變更的對象,所以不應(yīng)該在單例模式服務(wù)注入時以構(gòu)造函數(shù)參數(shù)形式傳入:

        use App\Service;

        /**
         * Register any application services.
         *
         * @return void
         */

        public function register()
        {
            $this->app->singleton(Service::class, function ($app) {
                return new Service($app->make('config'));
            });
        }

        解決方案還是普通模式注入和閉包模式注入:

        use App\Service;
        use Illuminate\Container\Container;

        $this->app->bind(Service::class, function ($app) {
            return new Service($app->make('config'));
        });

        $this->app->singleton(Service::class, function () {
            return new Service(fn () => Container::getInstance()->make('config'));
        });

        七、在低版本 Laravel 中引入 RoadRunner

        目前 Laravel Octane 只能在 PHP 8.0+ 和 Laravel 8.35+ 版本中使用,如果想要在低版本 PHP/Laravel 中引入 RoadRunner/Swoole,該怎么做呢?

        對應(yīng) Swoole 而言,對應(yīng)的解決方案是 LaravelS 擴展包,對于 RoadRunner 而言,對應(yīng)的解決方案是 RoadRunner 官方提供的 Laravel 擴展包,其安裝流程也非常簡單:

        composer require spiral/roadrunner:v2.0 nyholm/psr7   # 安裝 roadrunner 依賴
        ./vendor/bin/rr get-binary                            # 下載 roadrunner 二進制文件(與平臺相關(guān))
        composer require spiral/roadrunner-laravel "^4.0"     # 安裝 roadrunner laravel 擴展包
        php ./artisan vendor:publish --provider='Spiral\RoadRunnerLaravel\ServiceProvider' --tag=config # 發(fā)布配置文件

        在項目根目錄下更新下載 rr 過程中自動生成的 .rr.yaml 文件如下:

        server:
          command: "php ./vendor/bin/rr-worker start"

        http:
          address: 0.0.0.0:8080
          middleware: ["headers", "static", "gzip"]
          pool:
              num_workers: 6
            max_jobs: 0
            supervisor:
              exec_ttl: 60s
          headers:
            response:
              X-Powered-By: "RoadRunner"
          static:
            dir: "public"
            forbid: [".php"]

        啟動 RoadRunner:

        ./rr serve -c ./.rr.yaml

        這樣也可以訪問基于 RoadRunner 驅(qū)動的 Laravel 應(yīng)用。

        八、基準測試性能對比

        最后,我們來看下基于傳統(tǒng) PHP-FPM 驅(qū)動的 Laravel 應(yīng)用和基于 RoadRunner 驅(qū)動的 Laravel 應(yīng)用基準測試性能對比。

        這里我們模擬通過 4 個線程對 50 個并發(fā)請求進行基準測試,持續(xù)時間是 30s,基于 PHP-FPM 驅(qū)動 Laravel 應(yīng)用的 RPS 是 500+:

        同等條件下,基于 RoadRunner 驅(qū)動 Laravel 應(yīng)用的 RPS 則達到了 4000+,是 PHP-FPM  的 8 倍左右,在短短 30s 內(nèi)處理的請求量達到了 12萬+,各項細節(jié)指數(shù)也優(yōu)于 PHP-FPM:


        (全文完)

        本教程首發(fā)在 Laravel 學院,你也可以長按下面的二維碼關(guān)注本公眾號,閱讀學院君發(fā)布的最新教程:

        瀏覽 83
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            我想看日逼片 | 青青草国内自拍视频 | 扒开老师双腿猛进入喷水视频 | 色色色色色色色色色网 | 日本一级无码视频 | 国产探花在线精品一区二区 | free日本xxxxhd777 | 国产aaa精品 | 久久鬼色| 操逼黄片网站 |