一次 Laravel 性能分析全程笔记

大家都知道 laravel 项目写起来是挺爽,但是在生产环境性能不高,我们来抽丝剥茧分析我自己项目的运行时间消耗:

Bootstrap 耗时

步骤耗时
Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables0.3058910369873
Illuminate\Foundation\Bootstrap\LoadConfiguration3.6571025848389
Illuminate\Foundation\Bootstrap\HandleExceptions0.78296661376953
Illuminate\Foundation\Bootstrap\RegisterFacades9.0579986572266
Illuminate\Foundation\Bootstrap\RegisterProviders101.02701187134
Illuminate\Foundation\Bootstrap\BootProviders96.982002258301

观察初步结论: laravel 在调用 Illuminate\Foundation\Bootstrap\RegisterProvidersIlluminate\Foundation\Bootstrap\BootProvidersbootstrap 方法时,消耗时间是大头。

  • Illuminate\Foundation\Bootstrap\RegisterProviders 是用于注册服务提供者的。
  • Illuminate\Foundation\Bootstrap\BootProviders 是用于启动服务提供者的。
  • laravel 的内置server php artisan serve 自带了优化机制,上面数据仅体现首次加载的耗时。二次加载时会相比少很多。但此优化在 fpm 下无效。

我们进一步分析。

RegisterProviders 耗时

\Illuminate\Foundation\Bootstrap\RegisterProviders::bootstrap 方法代码如下:

    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        $app->registerConfiguredProviders();
    }

所以我们还是回到了 \Illuminate\Foundation\Application 这个文件:


    /**
     * Register all of the configured providers.
     *
     * @return void
     */
    public function registerConfiguredProviders()
    {
        $providers = Collection::make($this->config['app.providers'])
                        ->partition(function ($provider) {
                            return Str::startsWith($provider, 'Illuminate\\');
                        });

        $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);

        (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                    ->load($providers->collapse()->toArray());
    }

针对上面的 (new ProviderRepository)->load 进行耗时分析发现数据为

步骤耗时
Illuminate\Foundation\ProviderRepository::load61.771869659424

毋庸置疑这就是消耗时间的大头。

里面的代码为


    /**
     * Register the application service providers.
     *
     * @param  array  $providers
     * @return void
     */
    public function load(array $providers)
    {
        $manifest = $this->loadManifest();

        // First we will load the service manifest, which contains information on all
        // service providers registered with the application and which services it
        // provides. This is used to know which services are "deferred" loaders.
        if ($this->shouldRecompile($manifest, $providers)) {
            $manifest = $this->compileManifest($providers);
        }

        // Next, we will register events to load the providers for each of the events
        // that it has requested. This allows the service provider to defer itself
        // while still getting automatically loaded when a certain event occurs.
        foreach ($manifest['when'] as $provider => $events) {
            $this->registerLoadEvents($provider, $events);
        }

        // We will go ahead and register all of the eagerly loaded providers with the
        // application so their services can be registered with the application as
        // a provided service. Then we will set the deferred service list on it.
        foreach ($manifest['eager'] as $provider) {
            $this->app->register($provider);
        }

        $this->app->addDeferredServices($manifest['deferred']);
    }

而再经过定位,发现慢在这一行

        foreach ($manifest['eager'] as $provider) {
            $this->app->register($provider);
        }

又回到 \Illuminate\Foundation\Application

    /**
     * Register a service provider with the application.
     *
     * @param  \Illuminate\Support\ServiceProvider|string  $provider
     * @param  array  $options
     * @param  bool   $force
     * @return \Illuminate\Support\ServiceProvider
     */
    public function register($provider, $options = [], $force = false)
    {
        if (($registered = $this->getProvider($provider)) && ! $force) {
            return $registered;
        }

        // If the given "provider" is a string, we will resolve it, passing in the
        // application instance automatically for the developer. This is simply
        // a more convenient way of specifying your service provider classes.
        if (is_string($provider)) {
            $provider = $this->resolveProvider($provider);
        }

        if (method_exists($provider, 'register')) {
            $provider->register();
        }

        // If there are bindings / singletons set as properties on the provider we
        // will spin through them and register them with the application, which
        // serves as a convenience layer while registering a lot of bindings.
        if (property_exists($provider, 'bindings')) {
            foreach ($provider->bindings as $key => $value) {
                $this->bind($key, $value);
            }
        }

        if (property_exists($provider, 'singletons')) {
            foreach ($provider->singletons as $key => $value) {
                $this->singleton($key, $value);
            }
        }

        $this->markAsRegistered($provider);

        // If the application has already booted, we will call this boot method on
        // the provider class so it has an opportunity to do its boot logic and
        // will be ready for any usage by this developer's application logic.
        if ($this->booted) {
            $this->bootProvider($provider);
        }

        return $provider;
    }

在 register 方法中,根据 get_class($provider) 和 执行耗时,得出以下数据

步骤耗时
Illuminate\Events\EventServiceProvider0.02197265625
Illuminate\Log\LogServiceProvider0.005859375
Illuminate\Routing\RoutingServiceProvider0.011962890625
Illuminate\Auth\AuthServiceProvider0.024169921875
Illuminate\Cookie\CookieServiceProvider0.0048828125
Illuminate\Database\DatabaseServiceProvider9.678955078125
Illuminate\Encryption\EncryptionServiceProvider0.00732421875
Illuminate\Filesystem\FilesystemServiceProvider0.014892578125
Illuminate\Foundation\Providers\FormRequestServiceProvider0.0009765625
Illuminate\Foundation\Providers\FoundationServiceProvider0.416015625
Illuminate\Notifications\NotificationServiceProvider0.011962890625
Illuminate\Pagination\PaginationServiceProvider5.04296875
Illuminate\Session\SessionServiceProvider0.072021484375
Illuminate\View\ViewServiceProvider0.01318359375
Cog\Laravel\Love\Providers\LoveServiceProvider0.01708984375
Dingo\Api\Provider\RoutingServiceProvider0.0146484375
Dingo\Api\Provider\HttpServiceProvider0.03271484375
Dingo\Api\Provider\LaravelServiceProvider20.23583984375
Fideloper\Proxy\TrustedProxyServiceProvider0.001953125
InfyOm\AdminLTETemplates\AdminLTETemplatesServiceProvider0.001953125
InfyOm\Generator\InfyOmGeneratorServiceProvider0.045166015625
JeroenNoten\LaravelAdminLte\ServiceProvider0.013671875
Laracasts\Flash\FlashServiceProvider0.013916015625
Laravelfy\Validator\ServiceProvider0.001953125
Lshorz\Luocaptcha\LCaptchaServiceProvider0.01171875
Maatwebsite\Excel\ExcelServiceProvider6.778076171875
Overtrue\LaravelWeChat\ServiceProvider9.040771484375
Prettus\Repository\Providers\EventServiceProvider0.00390625
Prettus\Repository\Providers\RepositoryServiceProvider1.244140625
Spatie\Permission\PermissionServiceProvider0.3759765625
Tymon\JWTAuth\Providers\LaravelServiceProvider0.03515625
Collective\Html\HtmlServiceProvider0.025146484375
Yajra\DataTables\HtmlServiceProvider2.22314453125
Yajra\DataTables\ButtonsServiceProvider4.593017578125
Yajra\DataTables\DataTablesServiceProvider0.333984375
App\Providers\AppServiceProvider0.001953125
App\Providers\AuthServiceProvider0.001953125
App\Providers\EventServiceProvider0.001953125
App\Providers\RouteServiceProvider0.001708984375
App\Providers\ResponseMacroServicePrivoder37.69677734375
Overtrue\LaravelLang\TranslationServiceProvider0.01220703125
Illuminate\Validation\ValidationServiceProvider0.029052734375
Illuminate\Cache\CacheServiceProvider0.01318359375
Illuminate\Hashing\HashServiceProvider0.031005859375

得出 RegisterProviders 瓶颈的结论

  • App\Providers\ResponseMacroServicePrivoder 占用 37ms
  • Dingo\Api\Provider\LaravelServiceProvider 占用 20ms
  • Illuminate\Database\DatabaseServiceProvider 占用 9ms
  • Overtrue\LaravelWeChat\ServiceProvider 占用 9ms

BootProviders 耗时

服务提供者启动时间请求时
Illuminate\Database\DatabaseServiceProvider::boot0.8510748753.6809083125
Illuminate\Foundation\Providers\FormRequestServiceProvider::boot0.0229498750.0290524375
Illuminate\Notifications\NotificationServiceProvider::boot2.1137691259.91894525
Illuminate\Pagination\PaginationServiceProvider::boot0.0629881250.089843
EasyWeChatComposer\Laravel\ServiceProvider::boot6.591064312522.644042875
Cog\Laravel\Love\Providers\LoveServiceProvider::boot0.63110356252.3010250625
Dingo\Api\Provider\LaravelServiceProvider::boot9.22802737553.9980465
Fideloper\Proxy\TrustedProxyServiceProvider::boot0.15893568750.6091309375
InfyOm\AdminLTETemplates\AdminLTETemplatesServiceProvider::boot0.0336916250.0410155
Prettus\Repository\Providers\EventServiceProvider::boot0.0209963750.0432120625
Prettus\Repository\Providers\RepositoryServiceProvider::boot1.76000956258.361816625
Laracasts\Flash\FlashServiceProvider::boot0.1918941250.066894125
InfyOm\Generator\InfyOmGeneratorServiceProvider::boot0.08325131250.019042875
JeroenNoten\LaravelAdminLte\ServiceProvider::boot3.244140517.807128625
Laravelfy\Validator\ServiceProvider::boot2.94091787510.8391118125
Lshorz\Luocaptcha\LCaptchaServiceProvider::boot0.08325131250.075683375
Overtrue\LaravelWeChat\ServiceProvider::boot0.0747071250.0139165625
Spatie\Permission\PermissionServiceProvider::boot9.502685687515.3239749375
Tymon\JWTAuth\Providers\LaravelServiceProvider::boot1.07080012511.508300125
Yajra\DataTables\DataTablesServiceProvider::boot0.28393568751.0837404375
Yajra\DataTables\HtmlServiceProvider::boot0.08276318750.0651856875
Maatwebsite\Excel\ExcelServiceProvider::boot0.04614281250.0097655
Yajra\DataTables\ButtonsServiceProvider::boot0.05297856250.046875
App\Providers\AppServiceProvider::boot0.11791918750.0979000625
App\Providers\AuthServiceProvider::boot0.19018568750.437988125
App\Providers\EventServiceProvider::boot0.1967773750.8210441875
App\Providers\RouteServiceProvider::boot4.603271437512.817871375
App\Providers\ResponseMacroServicePrivoder::boot5.669189312516.917968
Laravel\Tinker\TinkerServiceProvider::boot0.3859868125null
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::boot0.1750488125null

原因分析

  • EasyWeChatComposer\Laravel\ServiceProvider::boot 的启动速度,略慢,分析原因: 代码 Github boot 方法中,加载了路由。而 Laravel 的路由,确实是比较慢的。

[未完]

    原文作者:ARGUS
    原文地址: https://segmentfault.com/a/1190000016411386
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞