• Час читання ~2 хв
  • 19.06.2023

Минулого разу я аналізував продуктивність контейнера Ларавеля. Я стикався з тим, що програма витрачає багато часу на створення залежностей, особливо для важких кінцевих точок. Це було дивно, тому що я хотів би очікувати, що відповідна логіка повинна бути найважчою частиною запиту.

Проблема

Виявляється, за замовчуванням кожна залежність в Laravel не поділяється. Тому, коли програмі потрібна певна залежність, контейнер створить і введе новий екземпляр цієї залежності. Наприклад, у Symfony типово всі залежності поділяються:

У контейнері послуг за замовчуванням усі служби спільно використовуються. Це означає, що кожного разу, коли ви отримуєте службу, ви отримуватимете один і той самий екземпляр.

https://symfony.com/doc/current/service_container/shared.htmlОтже, реалізація контейнера в Laravel є дивною, оскільки це означає, що кожна велика програма, що використовує Laravel, матиме проблему з введенням великої кількості випадків залежностей. Це витрачає багато ресурсів, тому що додаток повинен розпізнавати параметри певного класу за допомогою Reflection (магія 😄 автопроводки), а потім будувати всі залежності і робити те ж саме під час побудови залежностей цього конкретного класу і так далі. 🤯

\Illuminate\Container\Container::resolve

// If an instance of the type is currently being managed as a singleton we'll
// just return an existing instance instead of instantiating new instances
// so the developer can keep using the same objects instance every time.
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
    return $this->instances[$abstract];
}

Як це вирішити?

Використовуйте scoped або singleton замість bind у постачальниках:

$this->app->scoped(ServiceInterface::class, Service::class);
$this->app->singleton(ServiceInterface::class, Service::class);

Існує одна відмінність між scoped та singleton.

Метод scoped зв'язує клас або інтерфейс в контейнер, який повинен бути вирішений тільки один раз в рамках даного запиту Laravel / життєвого циклу завдання. Хоча цей метод схожий на метод одинака, екземпляри, зареєстровані за допомогою методу області, будуть очищатися кожного разу, коли програма Laravel починає новий "життєвий цикл", наприклад, коли працівник Laravel Octane обробляє новий запит або коли працівник черги Laravel обробляє нову роботу:Ви також можете вручну очистити обмежені залежності:

Ви також можете вручну очистити обмежені залежності:

$this->container->forgetScopedInstances();

У більшості випадків ця зміна має бути безпечною, але будьте обережні та перевірте, чи ваші послуги не мають громадянства.

Послуги автоматичного підключення

На жаль, неспільні залежності використовуються за замовчуванням. Отже, це означає, що коли у нас є автоматичні дротові послуги без будь-якого інтерфейсу, нам не потрібно оголошувати їх жодному постачальнику, тоді ми не можемо зробити їх спільними.

Щоб вирішити цю проблему, я знайшов лише наступне неприємне рішення для цього:

$this->app->scoped(Service:class, Service:class)

Таким чином, ми можемо просто створити постачальника та визначити послугу, використовуючи метод масштабування, щоб зробити цю послугу спільною.

Порівняння

До

Після

Ці результати після деяких змін від прив'язки до сфери, але все ще не всі, тому ще є куди вдосконалюватися. Час у Blackfire не має значення, але ми все ще бачимо підвищення продуктивності на ~ 60% під час побудови залежностей.

Резюме

Мені цікаво, що стало першопричиною реалізації контейнера таким чином. Це означає, що Laravel може бути не найкращим варіантом для великих монолітних застосувань. Кілька років тому я працював з величезним монолітом на базі фреймворку Symfony, розробленим близько 50 бекенд-розробниками, і навіть уявити собі не можу використовувати Laravel в такому проекті з такою конструкцією контейнера. Напевно, це був би кошмар.

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