<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 7850字,需瀏覽 16分鐘

           ·

          2021-04-12 12:25

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

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

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

          一、安裝 Octane 擴(kuò)展包

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

          composer require laravel/octane
          php artisan octane:install

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

          二、什么是 RoadRunner

          RoadRunner 是一個基于 Go 語言編寫的高性能 PHP 應(yīng)用服務(wù)器,它可以利用 Go 在并發(fā)編程中的優(yōu)勢,基于協(xié)程實現(xiàn)高性能的 HTTP 服務(wù)器,然后將用戶請求轉(zhuǎn)發(fā)給常駐內(nèi)存的 PHP-Worker 進(jìn)行處理,這樣一來,在原有 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

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

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


          至此,我們就準(zhǔn)備好了 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 標(biāo)識告知 Octane 在項目文件發(fā)生變更后自動重啟服務(wù)器,只需要在啟動 Octane 時帶上這個標(biāo)識即可:

          php artisan octane:start --watch

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

          npm install --save-dev chokidar

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

          默認(rèn)情況下,Octane 會根據(jù)機(jī)器 CPU 的內(nèi)核數(shù)來啟動對應(yīng)數(shù)量的請求處理器進(jìn)程(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

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

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

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

          php artisan octane:reload

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

          六、注意事項

          由于一個 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ù)代碼目前存在這些問題,需要進(jìn)行調(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ù)容器類似,因為不同用戶請求對象不同,并且可能帶有認(rèn)證狀態(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 擴(kuò)展包,對于 RoadRunner 而言,對應(yīng)的解決方案是 RoadRunner 官方提供的 Laravel 擴(kuò)展包,其安裝流程也非常簡單:

          composer require spiral/roadrunner:v2.0 nyholm/psr7   # 安裝 roadrunner 依賴
          ./vendor/bin/rr get-binary                            # 下載 roadrunner 二進(jìn)制文件(與平臺相關(guān))
          composer require spiral/roadrunner-laravel "^4.0"     # 安裝 roadrunner laravel 擴(kuò)展包
          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)用。

          八、基準(zhǔn)測試性能對比

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

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

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


          (全文完)

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

          瀏覽 83
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  台湾高清无码视频在线观看 | 欧美色图亚洲第一 | 商务合作TG@DJYT8 | 香蕉国产成人 | 亚洲欧洲在线无码 |