• Час читання ~7 хв
  • 27.06.2023

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

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

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

$app = require BASE_PATH . '/bootstrap/app.php'
$app->bootstrapWith([
    LoadEnvironmentVariables::class,
    LoadConfiguration::class,
    HandleExceptions::class,
    RegisterFacades::class,
    SetRequestForConsole::class,
    RegisterProviders::class,
    BootProviders::class,
]);
$app->loadDeferredProviders();
foreach ($app->make('config')->get('octane.warm') as $service) {
    $app->make($service);
}

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

На даний момент були викликані boot всі і методи невідкладених постачальників послуг, що означає, що всі register ваші служби додатків тепер прив'язані до контейнера. Іншими словами, контейнер тепер знає, як вирішити всі прив'язки служби додатків. Якщо ми зателефонуємо app('config') на цьому етапі, контейнер знатиме, як вирішити сховище конфігурації для вас.

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

Прив'язки в контейнері можуть бути зареєстровані як одинаки, ці спеціальні прив'язки будуть вирішені лише один раз у терміні служби. Вирішені екземпляри будуть зберігатися в контейнерному кеші, і ті самі екземпляри будуть повторно використовуватися протягом терміну дії програми. Це підвищує продуктивність вашої програми, оскільки вам не потрібно буде створювати ці екземпляри знову і знову щоразу, коли вам потрібно буде вирішити їх із контейнера.

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

Інсталяції вирішуються викликом $app->resolve('singleton') або $app->make('singleton'). Отже, якщо ви вирішите будь-які одинаки всередині ваших постачальників boot послуг або register методами. Ці одинаки будуть зберігатися. Одинаки, які вирішуються під час обробки запитів, не зберігаються, вони будуть побудовані на кожному запиті під час роботи під октаном. Вам не потрібно турбуватися про них.

Якщо ви хочете вирішити деякі одинаки під час запуску працівника, і ви явно не вирішуєте їх у своєму постачальнику послуг, Octane дозволить вам налаштувати набір послуг для попереднього розігріву для вас. Ось що роблять останні рядки коду в наведеному вище прикладі:

foreach ($app->make('config')->get('octane.warm') as $service) {
    $app->make($service);
}

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

Обробка запитів

Тепер, коли у нас є екземпляр програми з сервісним контейнером, який знає, як вирішити всі наші прив'язки, які також мають деякі попередньо вирішені (розігріті) одинаки, ми готові обробляти запити.

Ось як Octane обробляє вхідні запити:

$server->on('request', function($request) use ($app){
    $sandbox = clone $app;
    Container::setInstance($sandbox);
    $sandbox->make('events')->dispatch(new RequestReceived);
    $response = $sandbox->make(Kernel::class)->handle($request);
    Container::setInstance($app);
    return $response;
});

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

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

app('config')->set('services.aws.key', AWS_KEY_HERE);

під час обробки наступного запиту services.aws.key ключ конфігурації все одно утримуватиме значення, config встановлене попереднім запитом. Відстеження цих витоків може бути дуже складним, особливо якщо вони були мутовані сторонніми пакетами, не створеними розробником. З цієї причини Laravel надає програмі пісочниці окрему config послугу, яка обрізається після кожного запиту, зберігаючи оригінальну config службу незмінною всередині оригінального екземпляра програми.

$sandbox->instance('config', clone $sandbox['config']);

Код вище показує, як Octane надає кожній пісочниці клонований екземпляр оригінальної config служби.

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

Ось деякі важливі речі, які робить Octane під час RequestReceived відправлення події:

$sandbox->instance('config', clone $sandbox['config']);
$sandbox[Kernel::class]->setApplication($sandbox);
$sandbox['cache']->store('array')->flush();
$sandbox['session']->driver()->flush();
$sandbox['session']->driver()->regenerate();
$sandbox['translator']->setLocale($sandbox['config']['app.locale']);
$sandbox['translator']->setFallback($sandbox['config']['app.fallback_locale']);
$sandbox['auth']->forgetGuards();
$app->instance('request', $request);
$sandbox->instance('request', $request);

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

Далі Octane передав екземпляр пісочниці декільком контейнерним службам. Таким чином, при $this->app виклику всередині цих служб буде повернуто екземпляр пісочниці, а не вихідний екземпляр програми. Octane робить це для кількох служб, але ми показуємо послугу Kernel лише на нашому прикладі.

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

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

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

Що слід врахувати Тепер

, коли ми розуміємо, як Octane завантажує нашу програму і використовує її для обробки декількох вхідних запитів, я хочу виділити кілька готчі для розробників додатків та супроводжувачів пакетів:

Передача екземпляра програми службам Якщо службі

потрібно взаємодіяти з екземпляром програми, і ви передаєте його в конструкторі, переконайтеся, що ви використовуєте екземпляр програми, переданий зворотному дзвінку, а не той, який передано постачальнику послуг:Причина в тому, що містить посилання на оригінальний екземпляр програми,

// Instead of...
$this->app->bind(Service::class, function () {
    return new Service($this->app);
});
// Do...
$this->app->bind(Service::class, function ($app) {
    return new Service($app);
});

$this->app оскільки він використовувався для реєстрації постачальників під час завантаження. Поки пісочниця вирішує проблему служби, потрібно, щоб екземпляр пісочниці передавався до конструктора служб, а не до вихідного екземпляра.

Інший варіант — не передавати екземпляр додатку конструктору, а замість нього використовувати помічника app() всередині сервісу, або Container::getInstance(). Ці помічники завжди матимуть посилання на екземпляр пісочниці.

Передача екземпляра програми одиночникам

Не передавайте екземпляр програми одиночнику, передайте зворотний виклик, який натомість повертає екземпляр пісочниці.

// Instead of...
$this->app->singleton(Service::class, function ($app) {
    return new Service($app);
});
// Do...
$this->app->singleton(Service::class, function ($app) {
    return new Service(fn () => Container::getInstance());
});

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

Передача екземпляра запиту одинакам

Так само, як і у випадку з екземпляром програми, передайте зворотний дзвінок:

// Instead of...
$this->app->singleton(Service::class, function ($app) {
    return new Service($app['request']);
});
// Do...
$this->app->singleton(Service::class, function ($app) {
    return new Service(fn () => Container::getInstance()['request']);
});

Або скористайтеся помічником request() всередині служби і не вводьте запит як залежність.

Передача екземпляра репозиторію конфігурації одиночникам

Як і два випадки вище, передайте зворотний виклик замість цього або скористайтеся config() helper:

// Instead of...
$this->app->singleton(Service::class, function ($app) {
    return new Service($app['config']);
});
// Do...
$this->app->singleton(Service::class, function ($app) {
    return new Service(fn() => Container::getInstance()['config']);
});

Persisting Singletons

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

Щоб зберегти одинаки між запитами, ви можете або вирішити їх у ваших постачальників послуг, або додати їх до масиву warm всередині файлу конфігурації Octane:З іншого боку, якщо у вас є пакет, який реєструє та розв'язує одиночник всередині постачальника послуг, і ви хочете очистити цей екземпляр перед кожним запитом, додайте його до flush масиву всередині файлу конфігурації:

'warm' => [
    ...Octane::defaultServicesToWarm(),
    Service::class
],

'flush' => [
    Service::class
],

Октан видалить ці одинаки з контейнера після обробки кожного запиту.

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