Laravel Octane
- Вступ
- Встановлення
- Вимоги до сервера
- Обслуговування вашого застосунку
- Ін'єкція залежностей та Octane
- Управління Витоками Пам'яті
- Конкурентні Завдання
- Тіки та Інтервали
- Кеш Octane
- Таблиці
Вступ
Laravel Octane значно підвищує продуктивність вашого застосунку, обслуговуючи ваш застосунок за допомогою високопродуктивних серверів застосунків, включаючи FrankenPHP, Open Swoole, Swoole та RoadRunner. Octane завантажує ваш застосунок один раз, зберігає його в пам'яті, а потім обробляє запити на надзвукових швидкостях.
Встановлення
Octane може бути встановлений через менеджер пакетів компонувальник:
composer require laravel/octane
Після встановлення Octane, ви можете виконати команду Artisan octane:install, яка встановить файл конфігурації Octane у ваш застосунок:
php artisan octane:install
Вимоги до сервера
FrankenPHP
FrankenPHP — це сервер PHP-застосунків, написаний на Go, який підтримує сучасні веб-функції, такі як ранні підказки, стиснення Brotli та Zstandard. Коли ви встановлюєте Octane і обираєте FrankenPHP як свій сервер, Octane автоматично завантажить і встановить для вас бінарний файл FrankenPHP.
FrankenPHP via Laravel Sail
Якщо ви плануєте розробляти свій застосунок, використовуючи Laravel Sail, вам слід виконати наступні команди для встановлення Octane та FrankenPHP:
./vendor/bin/sail up
./vendor/bin/sail composer require laravel/octane
Далі слід використати команду Artisan octane:install для встановлення бінарного файлу FrankenPHP:
./vendor/bin/sail artisan octane:install --server=frankenphp
Нарешті, додайте змінну середовища SUPERVISOR_PHP_COMMAND до визначення сервісу laravel.test у файлі docker-compose.yml вашого застосунку. Ця змінна середовища міститиме команду, яку Sail використовуватиме для обслуговування вашого застосунку за допомогою Octane замість PHP development server:
services:
laravel.test:
environment:
SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=frankenphp --host=0.0.0.0 --admin-port=2019 --port='${APP_PORT:-80}'"
XDG_CONFIG_HOME: /var/www/html/config
XDG_DATA_HOME: /var/www/html/data
Щоб увімкнути HTTPS, HTTP/2 та HTTP/3, застосуйте ці зміни:
services:
laravel.test:
ports:
- '${APP_PORT:-80}:80'
- '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
- '443:443'
- '443:443/udp'
environment:
SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --host=localhost --port=443 --admin-port=2019 --https"
XDG_CONFIG_HOME: /var/www/html/config
XDG_DATA_HOME: /var/www/html/data
Зазвичай, ви повинні отримувати доступ до вашого застосунку FrankenPHP Sail через https://localhost, оскільки використання https://127.0.0.1 вимагає додаткової конфігурації і не рекомендується.
FrankenPHP через Docker
Використання офіційних Docker-образів FrankenPHP може забезпечити покращену продуктивність та використання додаткових розширень, які не включені в статичні інсталяції FrankenPHP. Крім того, офіційні Docker-образи надають підтримку для запуску FrankenPHP на платформах, які він не підтримує нативно, таких як Windows. Офіційні Docker-образи FrankenPHP підходять як для локальної розробки, так і для використання в продакшені.
Ви можете використовувати наступний Dockerfile як відправну точку для контейнеризації вашого застосунку на базі FrankenPHP з Laravel:
FROM dunglas/frankenphp RUN install-php-extensions \ pcntl # Додайте інші PHP-розширення тут... COPY . /app ENTRYPOINT ["php", "artisan", "octane:frankenphp"]
Потім, під час розробки, ви можете використовувати наступний файл Docker Compose для запуску вашого застосунку:
# compose.yaml
services:
frankenphp:
build:
context: .
entrypoint: php artisan octane:frankenphp --workers=1 --max-requests=1
ports:
- "8000:8000"
volumes:
- .:/app
Якщо параметр --log-level явно передано до команди php artisan octane:start, Octane використовуватиме власний реєстратор FrankenPHP і, якщо не налаштовано інакше, створюватиме структуровані JSON-логи.
Ви можете звернутися до офіційної документації FrankenPHP для отримання додаткової інформації про запуск FrankenPHP з Docker.
RoadRunner
RoadRunner працює на основі бінарного файлу RoadRunner, який створено за допомогою Go. Перший раз, коли ви запускаєте сервер Octane на основі RoadRunner, Octane запропонує завантажити та встановити бінарний файл RoadRunner для вас.
RoadRunner via Laravel Sail
Якщо ви плануєте розробляти свій застосунок, використовуючи Laravel Sail, вам слід виконати наступні команди для встановлення Octane та RoadRunner:
./vendor/bin/sail up
./vendor/bin/sail composer require laravel/octane spiral/roadrunner-cli spiral/roadrunner-http
Далі, ви повинні запустити оболонку Sail і використати виконуваний файл rr, щоб отримати останню збірку RoadRunner для Linux:
./vendor/bin/sail shell # Усередині оболонки Sail... ./vendor/bin/rr get-binary
Потім додайте змінну середовища SUPERVISOR_PHP_COMMAND до визначення сервісу laravel.test у файлі docker-compose.yml вашого застосунку. Ця змінна середовища міститиме команду, яку Sail використовуватиме для обслуговування вашого застосунку за допомогою Octane замість PHP development server:
services:
laravel.test:
environment:
SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --rpc-port=6001 --port='${APP_PORT:-80}'"
Нарешті, переконайтеся, що бінарний файл rr є виконуваним, і створіть ваші образи Sail:
chmod +x ./rr
./vendor/bin/sail build --no-cache
Swoole
Якщо ви плануєте використовувати сервер застосунків Swoole для обслуговування вашого застосунку Laravel Octane, ви повинні встановити розширення Swoole PHP. Зазвичай це можна зробити через PECL:
pecl install swoole
Open Swoole
Якщо ви хочете використовувати сервер застосунків Open Swoole для обслуговування вашого застосунку Laravel Octane, ви повинні встановити розширення Open Swoole PHP. Зазвичай це можна зробити через PECL:
pecl install openswoole
Використання Laravel Octane з Open Swoole надає таку ж функціональність, як і Swoole, наприклад, паралельні завдання, тики та інтервали.
Swoole via Laravel Sail
Перш ніж запускати Octane застосунок через Sail, переконайтеся, що у вас встановлена остання версія Laravel Sail, і виконайте ./vendor/bin/sail build --no-cache у кореневому каталозі вашого застосунку.
Альтернативно, ви можете розробляти свій застосунок Octane на базі Swoole, використовуючи Laravel Sail, офіційне середовище розробки на базі Docker для Laravel. Laravel Sail включає розширення Swoole за замовчуванням. Однак, вам все ще потрібно буде налаштувати файл docker-compose.yml, який використовується Sail.
Щоб почати, додайте змінну середовища SUPERVISOR_PHP_COMMAND до визначення сервісу laravel.test у файлі docker-compose.yml вашого застосунку. Ця змінна середовища міститиме команду, яку Sail використовуватиме для обслуговування вашого застосунку за допомогою Octane замість PHP сервера розробки:
services:
laravel.test:
environment:
SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=swoole --host=0.0.0.0 --port='${APP_PORT:-80}'"
Нарешті, збудуйте ваші образи Sail:
./vendor/bin/sail build --no-cache
Конфігурація Swoole
Swoole підтримує кілька додаткових параметрів конфігурації, які ви можете додати до вашого файлу конфігурації octane за потреби. Оскільки їх рідко потрібно змінювати, ці параметри не включені в файл конфігурації за замовчуванням:
'swoole' => [
'options' => [
'log_file' => storage_path('logs/swoole_http.log'),
'package_max_length' => 10 * 1024 * 1024,
],
],
Обслуговування Вашого застосунку
Сервер Octane можна запустити за допомогою команди Artisan octane:start. За замовчуванням ця команда використовуватиме сервер, вказаний у параметрі конфігурації server файлу конфігурації octane вашого застосунку:
php artisan octane:start
За замовчуванням Octane запустить сервер на порту 8000, тому ви можете отримати доступ до вашого застосунку в веб-браузері через http://localhost:8000.
Обслуговування вашого застосунку через HTTPS
За замовчуванням, застосунки, що працюють через Octane, генерують посилання з префіксом http://. Змінна середовища OCTANE_HTTPS, яка використовується у файлі конфігурації вашого застосунку config/octane.php, може бути встановлена на true при обслуговуванні вашого застосунку через HTTPS. Коли це значення конфігурації встановлено на true, Octane інструктує Laravel префіксувати всі згенеровані посилання з https://:
'https' => env('OCTANE_HTTPS', false),
Обслуговування вашого застосунку через Nginx
Якщо ви ще не готові керувати власною конфігурацією сервера або не відчуваєте себе впевнено в налаштуванні всіх різноманітних сервісів, необхідних для запуску надійного застосунку Laravel Octane, ознайомтеся з Laravel Cloud, який пропонує повністю керовану підтримку Laravel Octane.
У виробничих середовищах ви повинні обслуговувати ваш Octane застосунок за традиційним веб-сервером, таким як Nginx або Apache. Це дозволить веб-серверу обслуговувати ваші статичні ресурси, такі як зображення та таблиці стилів, а також керувати завершенням дії вашого SSL сертифіката.
У наведеному нижче прикладі конфігурації Nginx, Nginx буде обслуговувати статичні ресурси сайту та проксирувати запити до сервера Octane, який працює на порту 8000:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name domain.com;
server_tokens off;
root /home/forge/domain.com/public;
index index.php;
charset utf-8;
location /index.php {
try_files /not_exists @octane;
}
location / {
try_files $uri $uri/ @octane;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
access_log off;
error_log /var/log/nginx/domain.com-error.log error;
error_page 404 /index.php;
location @octane {
set $suffix "";
if ($uri = /index.php) {
set $suffix ?$query_string;
}
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header Scheme $scheme;
proxy_set_header SERVER_PORT $server_port;
proxy_set_header REMOTE_ADDR $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://127.0.0.1:8000$suffix;
}
}
Спостереження за змінами файлів
Оскільки ваш застосунок завантажується в пам'ять один раз, коли сервер Octane запускається, будь-які зміни у файлах вашого застосунку не будуть відображені при оновленні вашого браузера. Наприклад, визначення маршрутів, додані до вашого файлу routes/web.php, не будуть відображені, поки сервер не буде перезапущено. Для зручності ви можете використовувати прапорець --watch, щоб вказати Octane автоматично перезапускати сервер при будь-яких змінах файлів у вашому застосунку:
php artisan octane:start --watch
Перш ніж використовувати цю функцію, ви повинні переконатися, що Node встановлено у вашому локальному середовищі розробки. Крім того, ви повинні встановити бібліотеку для спостереження за файлами Chokidar у вашому проекті:
npm install --save-dev chokidar
Ви можете налаштувати каталоги та файли, які слід відстежувати, використовуючи параметр конфігурації watch у файлі конфігурації config/octane.php вашого застосунку.
Вказання кількості обробників
За замовчуванням Octane запустить обробник запитів застосунку для кожного ядра процесора, наданого вашим комп'ютером. Ці обробники будуть використовуватися для обробки вхідних HTTP-запитів, коли вони надходять до вашого застосунку. Ви можете вручну вказати, скільки обробників ви хочете запустити, використовуючи опцію --workers при виклику команди octane:start:
php artisan octane:start --workers=4
Якщо ви використовуєте сервер застосунків Swoole, ви також можете вказати, скільки "обробників завдань" ви бажаєте запустити:
php artisan octane:start --workers=4 --task-workers=6
Вказання максимальної кількості запитів
Щоб допомогти запобігти випадковим витокам пам'яті, Octane плавно перезапускає будь-який обробник після обробки 500 запитів. Щоб налаштувати цю кількість, ви можете використовувати опцію --max-requests:
php artisan octane:start --max-requests=250
Перезавантаження обробників
Ви можете плавно перезапустити обробники застосунку сервера Octane за допомогою команди octane:reload. Зазвичай це слід робити після розгортання, щоб ваш щойно розгорнутий код був завантажений у пам'ять і використовувався для обслуговування наступних запитів:
php artisan octane:reload
Зупинка сервера
Ви можете зупинити сервер Octane, використовуючи Artisan команду octane:stop:
php artisan octane:stop
Перевірка стану сервера
Ви можете перевірити поточний статус сервера Octane за допомогою команди Artisan octane:status:
php artisan octane:status
Ін'єкція залежностей та Octane
Оскільки Octane завантажує ваш застосунок один раз і зберігає його в пам'яті під час обробки запитів, є кілька застережень, які слід враховувати під час створення вашого застосунку. Наприклад, методи register і boot сервіс-провайдерів вашого застосунку будуть виконані лише один раз, коли обробник запитів спочатку завантажується. При наступних запитах буде повторно використовуватися той самий екземпляр застосунку.
У зв'язку з цим, слід приділяти особливу увагу при ін'єкції сервіс-контейнера застосунку або запиту в конструктор будь-якого об'єкта. Роблячи це, об'єкт може мати застарілу версію контейнера або запиту при наступних запитах.
Octane автоматично оброблятиме скидання будь-якого стану першої сторони фреймворку між запитами. Однак Octane не завжди знає, як скинути глобальний стан, створений вашим застосунком. Тому вам слід знати, як побудувати ваш застосунок таким чином, щоб він був сумісний з Octane. Нижче ми обговоримо найпоширеніші ситуації, які можуть викликати проблеми під час використання Octane.
Ін'єкція контейнера
Загалом, слід уникати ін'єкції сервіс-контейнера застосунку або екземпляра HTTP-запиту в конструктори інших об'єктів. Наприклад, наступне зв'язування ін'єктує весь сервіс-контейнер застосунку в об'єкт, який зв'язаний як сінглтон:
use App\Service; use Illuminate\Contracts\Foundation\Application; /** * Зареєструвати будь-які служби застосунку. */ public function register(): void { $this->app->singleton(Service::class, function (Application $app) { return new Service($app); }); }
У цьому прикладі, якщо екземпляр Service буде вирішено під час процесу завантаження застосунку, контейнер буде впроваджено в сервіс, і цей самий контейнер буде утримуватись екземпляром Service при наступних запитах. Це може не бути проблемою для вашого конкретного застосунку; однак, це може призвести до того, що контейнер несподівано не матиме зв'язків, які були додані пізніше в циклі завантаження або під час наступного запиту.
Як обхідний шлях, ви можете або припинити реєстрацію зв'язування як сінглтон, або ви можете ввести замикання резолвера контейнера в сервіс, яке завжди резолвить поточний екземпляр контейнера:
use App\Service;
use Illuminate\Container\Container;
use Illuminate\Contracts\Foundation\Application;
$this->app->bind(Service::class, function (Application $app) {
return new Service($app);
});
$this->app->singleton(Service::class, function () {
return new Service(fn () => Container::getInstance());
});
Глобальний хелпер app та метод Container::getInstance() завжди повертатимуть останню версію контейнера застосунку.
Ін'єкція запитів
Загалом, слід уникати ін'єкції сервіс-контейнера застосунку або екземпляра HTTP-запиту в конструктори інших об'єктів. Наприклад, наступне зв'язування ін'єктує весь екземпляр запиту в об'єкт, який зв'язаний як сінглтон:
use App\Service; use Illuminate\Contracts\Foundation\Application; /** * Зареєструвати будь-які служби застосунку. */ public function register(): void { $this->app->singleton(Service::class, function (Application $app) { return new Service($app['request']); }); }
У цьому прикладі, якщо екземпляр Service буде вирішено під час процесу завантаження застосунку, HTTP-запит буде впроваджено в сервіс, і цей самий запит буде утримуватись екземпляром Service при наступних запитах. Тому всі заголовки, введені дані та дані рядка запиту будуть некоректними, як і всі інші дані запиту.
Як обхідний шлях, ви можете або припинити реєстрацію зв'язування як сінглтон, або ви можете ввести замикання для вирішення запиту в сервіс, яке завжди вирішує поточний екземпляр запиту. Або ж, найбільш рекомендований підхід - просто передати конкретну інформацію запиту, яка потрібна вашому об'єкту, одному з методів об'єкта під час виконання:
use App\Service; use Illuminate\Contracts\Foundation\Application; $this->app->bind(Service::class, function (Application $app) { return new Service($app['request']); }); $this->app->singleton(Service::class, function (Application $app) { return new Service(fn () => $app['request']); }); // або... $service->method($request->input('name'));
Глобальний хелпер request завжди повертатиме запит, який застосунок наразі обробляє, і тому його безпечно використовувати у вашому застосунку.
Дозволено використовувати типізацію екземпляра Illuminate\Http\Request у методах вашого контролера та замиканнях маршруту.
Ін'єкція Репозиторію Конфігурації
Загалом, слід уникати ін'єкції екземпляра репозиторію конфігурації в конструктори інших об'єктів. Наприклад, наступне зв'язування ін'єктує репозиторій конфігурації в об'єкт, який зв'язаний як сінглтон:
use App\Service; use Illuminate\Contracts\Foundation\Application; /** * Зареєструвати будь-які служби застосунку. */ public function register(): void { $this->app->singleton(Service::class, function (Application $app) { return new Service($app->make('config')); }); }
У цьому прикладі, якщо значення конфігурації змінюються між запитами, цей сервіс не матиме доступу до нових значень, оскільки він залежить від початкового екземпляра репозиторію.
Як обхідний шлях, ви можете або припинити реєстрацію зв'язування як сінглтон, або ви можете впровадити замикання резолвера репозиторію конфігурації в клас:
use App\Service;
use Illuminate\Container\Container;
use Illuminate\Contracts\Foundation\Application;
$this->app->bind(Service::class, function (Application $app) {
return new Service($app->make('config'));
});
$this->app->singleton(Service::class, function () {
return new Service(fn () => Container::getInstance()->make('config'));
});
Глобальний config завжди повертатиме останню версію репозиторію конфігурації, тому його безпечно використовувати у вашому застосунку.
Управління Витоками Пам'яті
Запам'ятайте, Octane зберігає ваш застосунок у пам'яті між запитами; тому додавання даних до статично підтримуваного масиву призведе до витоку пам'яті. Наприклад, наступний контролер має витік пам'яті, оскільки кожен запит до застосунку продовжуватиме додавати дані до статичного масиву $data:
use App\Service; use Illuminate\Http\Request; use Illuminate\Support\Str; /** * Обробити вхідний запит. */ public function index(Request $request): array { Service::$data[] = Str::random(10); return [ // ... ]; }
Під час створення вашого застосунку слід особливо уважно уникати створення таких типів витоків пам'яті. Рекомендується стежити за використанням пам'яті вашим застосунком під час локальної розробки, щоб переконатися, що ви не вводите нові витоки пам'яті у ваш застосунок.
Конкурентні завдання
Ця функція вимагає Swoole.
Коли ви використовуєте Swoole, ви можете виконувати операції одночасно за допомогою легковагих фонових завдань. Ви можете досягти цього, використовуючи метод Octane concurrently. Ви можете поєднати цей метод з деструктуризацією масивів PHP, щоб отримати результати кожної операції:
use App\Models\User;
use App\Models\Server;
use Laravel\Octane\Facades\Octane;
[$users, $servers] = Octane::concurrently([
fn () => User::all(),
fn () => Server::all(),
]);
Конкурентні завдання, оброблені Octane, використовують "обробники завдань" Swoole і виконуються в абсолютно іншому процесі, ніж вхідний запит. Кількість обробників, доступних для обробки конкурентних завдань, визначається директивою --task-workers у команді octane:start:
php artisan octane:start --workers=4 --task-workers=6
Коли викликаєте метод concurrently, не слід надавати більше ніж 1024 завдання через обмеження, накладені системою завдань Swoole.
Тіки та Інтервали
Ця функція вимагає Swoole.
Коли ви використовуєте Swoole, ви можете зареєструвати операції "tick", які будуть виконуватися кожну вказану кількість секунд. Ви можете зареєструвати "tick" зворотні виклики через метод tick. Перший аргумент, наданий методу tick, повинен бути рядком, що представляє ім'я тікера. Другий аргумент повинен бути callable, який буде викликаний з вказаним інтервалом.
У цьому прикладі ми зареєструємо замикання, яке буде викликатися кожні 10 секунд. Зазвичай метод tick слід викликати в межах методу boot одного з сервіс-провайдерів вашого застосунку:
Octane::tick('simple-ticker', fn () => ray('Ticking...'))
->seconds(10);
Використовуючи метод immediate, ви можете вказати Octane негайно викликати зворотний виклик tick, коли сервер Octane спочатку запускається, і кожні N секунд після цього:
Octane::tick('simple-ticker', fn () => ray('Ticking...'))
->seconds(10)
->immediate();
Кеш Octane
Ця функція вимагає Swoole.
Коли ви використовуєте Swoole, ви можете скористатися драйвером кешу Octane, який забезпечує швидкість читання та запису до 2 мільйонів операцій на секунду. Тому цей драйвер кешу є відмінним вибором для застосунків, яким потрібна надзвичайна швидкість читання/запису з їхнього шару кешування.
Цей драйвер кешу працює на основі таблиць Swoole. Всі дані, збережені в кеші, доступні всім обробникам на сервері. Однак кешовані дані будуть очищені при перезапуску сервера:
Cache::store('octane')->put('framework', 'Laravel', 30);
Максимальну кількість записів, дозволених у кеші Octane, можна визначити у файлі конфігурації octane вашого застосунку.
Інтервали кешування
На додаток до типових методів, наданих системою кешування Laravel, драйвер кешу Octane має кеші на основі інтервалів. Ці кеші автоматично оновлюються через вказаний інтервал і повинні бути зареєстровані в методі boot одного з сервіс-провайдерів вашого застосунку. Наприклад, наступний кеш буде оновлюватися кожні п'ять секунд:
use Illuminate\Support\Str;
Cache::store('octane')->interval('random', function () {
return Str::random(10);
}, seconds: 5);
Таблиці
Ця функція вимагає Swoole.
Коли ви використовуєте Swoole, ви можете визначати та взаємодіяти з власними довільними таблицями Swoole. Таблиці Swoole забезпечують надзвичайну продуктивність, і дані в цих таблицях можуть бути доступні для всіх обробників на сервері. Однак дані в них будуть втрачені при перезапуску сервера.
Таблиці повинні бути визначені в конфігураційному масиві tables файлу конфігурації octane вашого застосунку. Приклад таблиці, яка дозволяє максимум 1000 рядків, вже налаштований для вас. Максимальний розмір стовпців типу string може бути налаштований шляхом вказання розміру стовпця після типу стовпця, як показано нижче:
'tables' => [
'example:1000' => [
'name' => 'string:1000',
'votes' => 'int',
],
],
Щоб отримати доступ до таблиці, ви можете використовувати метод Octane::table:
use Laravel\Octane\Facades\Octane;
Octane::table('example')->set('uuid', [
'name' => 'Nuno Maduro',
'votes' => 1000,
]);
return Octane::table('example')->get('uuid');
Типи стовпців, які підтримуються таблицями Swoole: string, int та float.
