• Время чтения ~2 мин
  • 19.06.2023

Некоторое время назад я начал задаваться вопросом о длительном времени начальной загрузки приложения, основанного на Laravel. Я начал отладку и выяснил, что эта проблема связана с отсутствием отложенных поставщиков. Это было странно, потому что мы использовали много отложенных провайдеров. После дополнительной отладки мы обнаружили, что AggregateProviders не уважает отложенных поставщиков.

Проблема

В нашем приложении было более 60 AggregateServiceProviders, подобных следующему:В этом случае, согласно документации:

use Illuminate\Support\AggregateServiceProvider;

final class SampleProvider extends AggregateServiceProvider
{
    protected $providers = [
        EventsProvider::class,
        ImplementationsProvider::class,
        RoutingProvider::class,
    ];
}

Если ваш поставщик регистрирует привязки только в контейнере сервиса, вы можете отложить его регистрацию до тех пор, ImplementationsProvider implements \Illuminate\Contracts\Support\DeferrableProvider пока одна из зарегистрированных привязок не понадобится. Отсрочка загрузки такого провайдера повысит производительность вашего приложения, так как оно не загружается из файловой системы при каждом запросе.

https://laravel.com/docs/10.x/providers#deferred-providersЕсли мы посмотрим на метод register в, мы увидим следующую реализацию:Это означает, что каждый поставщик, добавленный в AggregateServiceProvider, будет регистрироваться немедленно при каждом запросе,

public function register()
{
    $this->instances = [];

    foreach ($this->providers as $provider) {
        $this->instances[] = $this->app->register($provider);
    }
}

потому что для регистрации отложенного поставщика в \Illuminate\Support\AggregateServiceProvider контейнере должен использоваться метод registerDeferredProvider($provider, $service = null);.

Не используйте AggregateServiceProvider Таким образом, вывод прост: не используйте AggregateServiceProvider

. Я рекомендую использовать что-то вроде этого:

final class SampleProvider
{
    /**
     * @return string[]
     */
    public static function providers(): array
    {
        return [
            EventsProvider::class,
            ImplementationsProvider::class,
            RoutingProvider::class,
        ];
    }
}

Тогда в конфигурации мы можем загрузить провайдеров следующим образом:

'providers' => array_merge(
    // ...
    \App\Modules\Module\SampleProvider::providers(),
    // ...
);

Тест, чтобы проверить метод предоставляет При рефакторинге этих провайдеров легко ошибиться, теперь мы должны правильно настроить метод

provide во всех отложенных поставщиках, потому что они будут загружаться только тогда, когда требуется определенная зависимость, и контейнер должен найти подходящего поставщика с определением этой зависимости. Так что было бы неплохо написать собственное правило PHPStan или просто небольшой хитрый тест в PHPUnit.

Результаты после частичного рефакторинга

Выполнив частичный рефакторинг этой проблемы (2/3 AggregateServiceProviders), мы смогли сократить время начальной загрузки приложения более чем на 20%. Это, вероятно, всего несколько миллисекунд на запрос (я оцениваю прибыль в 5 мс), конечно, не слишком много, но это имеет значение для каждого запроса. Кроме того, в будущем, когда приложение будет расти, это будет более серьезной проблемой, поэтому лучше выяснить эту проблему и не влиять на производительность таким глупым способом.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297