1. Пакеты
  2. Laravel Octane

Присоединяйся к нашему Telegram сообществу @webblend!

Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.

Введение

Laravel Octane увеличивает производительность вашего приложения, обслуживая его с использованием мощных серверов приложений, таких как Open Swoole, Swoole и RoadRunner. Octane загружает ваше приложение один раз, держит его в памяти, а затем подает ему запросы со сверхзвуковой скоростью.

Установка

Octane можно установить с помощью менеджера пакетов Composer:

composer require laravel/octane

После установки Octane вы можете выполнить команду Artisan octane:install, которая установит файл конфигурации Octane в ваше приложение:

php artisan octane:install

Требования к серверу

Внимание Laravel Octane требует PHP 8.1+.

RoadRunner

RoadRunner работает на бинарном файле RoadRunner, построенном с использованием Go. Первый раз, когда вы запускаете сервер Octane на основе RoadRunner, Octane предложит вам загрузить и установить бинарный файл RoadRunner.

RoadRunner через 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
 
# Within the Sail shell...
./vendor/bin/rr get-binary

После установки бинарного файла RoadRunner вы можете завершить сеанс оболочки Sail. Теперь вам нужно настроить файл supervisor.conf, используемый Sail для поддержания работы вашего приложения. Для начала выполните команду Artisan sail:publish:

./vendor/bin/sail artisan sail:publish

Затем обновите директиву command файла docker/supervisord.conf вашего приложения, чтобы Sail обслуживал ваше приложение с использованием Octane вместо 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=80

Наконец, убедитесь, что исполняемый файл rr доступен для выполнения, и построите образы Sail:

chmod +x ./rr
 
./vendor/bin/sail build --no-cache

Swoole

Если вы планируете использовать сервер приложений Swoole для обслуживания вашего приложения Laravel Octane, вы должны установить расширение PHP для Swoole. Обычно это можно сделать с помощью PECL:

pecl install swoole

Open Swoole

Если вы хотите использовать сервер приложений Open Swoole для обслуживания вашего приложения Laravel Octane, вы должны установить расширение PHP для Open Swoole. Обычно это можно сделать с помощью PECL:

pecl install openswoole

Использование Laravel Octane с Open Swoole предоставляет ту же функциональность, что и Swoole, такую как параллельные задачи, тики и интервалы.

Swoole через Laravel Sail

Внимание Перед тем как обслуживать приложение Octane через Sail, убедитесь, что у вас установлена последняя версия Laravel Sail, и выполните ./vendor/bin/sail build --no-cache в корневом каталоге вашего приложения.

В качестве альтернативы вы можете разрабатывать свое приложение Octane на основе Swoole с использованием Laravel Sail, официальной среды разработки Laravel на основе Docker. Laravel Sail включает расширение Swoole по умолчанию. Тем не менее, вам все равно нужно будет настроить файл supervisor.conf, используемый Sail, чтобы ваше приложение продолжало работать. Чтобы начать, выполните команду Artisan sail:publish:

./vendor/bin/sail artisan sail:publish

Затем обновите директиву command файла docker/supervisord.conf вашего приложения так, чтобы Sail обслуживал ваше приложение с использованием Octane вместо сервера разработки PHP:

command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=swoole --host=0.0.0.0 --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 Forge.

В производственных средах рекомендуется обслуживать ваше приложение 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']);
});
 
// Or...
 
$service->method($request->input('name'));

Глобальный помощник 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 вы можете выполнять операции параллельно с помощью легких фоновых задач. Это можно сделать с использованием метода concurrently Octane. Вы можете объединить этот метод с деструктуризацией массива 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, должен быть строкой, представляющей имя тикера. Вторым аргументом должна быть область видимости, которая будет вызвана с указанным интервалом.

В этом примере мы зарегистрируем замыкание, которое будет вызываться каждые 10 секунд. Обычно метод tick следует вызывать в методе boot одного из поставщиков служб вашего приложения:

Octane::tick('simple-ticker', fn () => ray('Ticking...'))
->seconds(10);

С использованием метода immediate вы можете указать Octane мгновенно вызывать обратный вызов тика при первоначальной загрузке сервера 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 строк, уже настроен для вас. Максимальный размер столбцов строк может быть настроен, указав размер столбца после типа столбца, как показано ниже:

'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.