Jakiś czas temu zacząłem się zastanawiać nad długim czasem bootstrap aplikacji opartej na Laravel. Rozpocząłem debugowanie i zorientowałem się, że ten problem był związany z brakiem odroczonych dostawców. To było dziwne, ponieważ korzystaliśmy z wielu odroczonych dostawców. Po dłuższym debugowaniu odkryliśmy, że AggregateProviders nie szanuje odroczonych dostawców.
Problem
Nasza aplikacja miała ponad 60 AggregateServiceProviders podobnych do następującego:W takim przypadku, zgodnie z dokumentacją
use Illuminate\Support\AggregateServiceProvider;
final class SampleProvider extends AggregateServiceProvider
{
protected $providers = [
EventsProvider::class,
ImplementationsProvider::class,
RoutingProvider::class,
];
}
:
Jeśli Twój dostawca rejestruje tylko powiązania w kontenerze usług, możesz odroczyć jego rejestrację,
ImplementationsProvider implements \Illuminate\Contracts\Support\DeferrableProvider
dopóki jedno z zarejestrowanych powiązań nie będzie faktycznie potrzebne. Odroczenie ładowania takiego dostawcy poprawi wydajność aplikacji, ponieważ nie jest ona ładowana z systemu plików na każde żądanie.
https://laravel.com/docs/10.x/providers#deferred-providersJeśli spojrzymy na rejestrację \Illuminate\Support\AggregateServiceProvider
metody, zobaczymy następującą implementację:Oznacza to, że każdy dostawca dodany do AggregateServiceProvider zostanie zarejestrowany natychmiast na każde żądanie, ponieważ aby zarejestrować odroczonego dostawcę w kontenerze,
public function register()
{
$this->instances = [];
foreach ($this->providers as $provider) {
$this->instances[] = $this->app->register($provider);
}
}
należy użyć metody registerDeferredProvider($provider, $service = null);
.
Nie używaj AggregateServiceProvider Więc wniosek jest prosty, nie używaj AggregateServiceProvider
. Polecam użycie czegoś takiego:Następnie w konfiguracji możemy załadować dostawców w ten sposób:
final class SampleProvider
{
/**
* @return string[]
*/
public static function providers(): array
{
return [
EventsProvider::class,
ImplementationsProvider::class,
RoutingProvider::class,
];
}
}
Test w celu zweryfikowania metody
'providers' => array_merge(
// ...
\App\Modules\Module\SampleProvider::providers(),
// ...
);
zapewnia Łatwo jest popełnić błąd podczas refaktoryzacji tych dostawców, teraz musimy mieć odpowiednio skonfigurowaną metodę zapewnia we wszystkich odroczonych dostawcach, ponieważ zostaną one załadowane tylko wtedy, gdy potrzebna jest konkretna zależność,
a kontener musi znaleźć odpowiedniego dostawcę z definicją tej zależności. Dobrze byłoby więc napisać niestandardową regułę PHPStan lub po prostu trochę trudny test w PHPUnit.
Wyniki po częściowej refaktoryzacji
Wykonując częściową refaktoryzację tego problemu (2/3 AggregateServiceProviders) udało nam się skrócić czas bootstrap aplikacji o ponad 20%. To prawdopodobnie tylko kilka milisekund na żądanie (szacuję zysk 5 ms), oczywiście nie za dużo, ale robi różnicę na każdym żądaniu. Co więcej, w przyszłości, gdy aplikacja będzie się rozwijać, będzie to większy problem, więc lepiej zrozumieć ten problem i uniknąć wpływu na wydajność w tak głupi sposób.