Skip to content

Files

Latest commit

 

History

History
650 lines (462 loc) · 27.1 KB

octane.md

File metadata and controls

650 lines (462 loc) · 27.1 KB

Laravel Octane

Introdução

Laravel Octane turbina o desempenho do seu aplicativo servindo-o usando servidores de aplicativos de alta potência, incluindo FrankenPHP, Open Swoole, Swoole e RoadRunner. O Octane inicializa seu aplicativo uma vez, o mantém na memória e, em seguida, o alimenta com solicitações em velocidades supersônicas.

Instalação

O Octane pode ser instalado por meio do gerenciador de pacotes do Composer:

composer require laravel/octane

Após instalar o Octane, você pode executar o comando Artisan octane:install, que instalará o arquivo de configuração do Octane em seu aplicativo:

php artisan octane:install

Pré-requisitos do servidor

::: warning ATENÇÃO O Laravel Octane requer PHP 8.1+. :::

FrankenPHP

FrankenPHP é um servidor de aplicativos PHP, escrito em Go, que oferece suporte a recursos modernos da web, como dicas iniciais, Brotli e compactação Zstandard. Quando você instala o Octane e escolhe FrankenPHP como seu servidor, o Octane automaticamente baixa e instala o binário FrankenPHP para você.

FrankenPHP via Laravel Sail

Se você planeja desenvolver seu aplicativo usando Laravel Sail, você deve executar os seguintes comandos para instalar o Octane e o FrankenPHP:

./vendor/bin/sail up

./vendor/bin/sail composer require laravel/octane

Em seguida, você deve usar o comando Artisan octane:install para instalar o binário FrankenPHP:

./vendor/bin/sail artisan octane:install --server=frankenphp

Finalmente, adicione uma variável de ambiente SUPERVISOR_PHP_COMMAND à definição de serviço laravel.test no arquivo docker-compose.yml do seu aplicativo. Esta variável de ambiente conterá o comando que o Sail usará para servir seu aplicativo usando o Octane em vez do servidor de desenvolvimento PHP:

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=80" # [tl! add]
      XDG_CONFIG_HOME:  /var/www/html/config # [tl! add]
      XDG_DATA_HOME:  /var/www/html/data # [tl! add]

Para habilitar HTTPS, HTTP/2 e HTTP/3, aplique estas modificações:

services:
  laravel.test:
    ports:
        - '${APP_PORT:-80}:80'
        - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
        - '443:443' # [tl! add]
        - '443:443/udp' # [tl! add]
    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" # [tl! add]
      XDG_CONFIG_HOME:  /var/www/html/config # [tl! add]
      XDG_DATA_HOME:  /var/www/html/data # [tl! add]

Normalmente, você deve acessar seu aplicativo FrankenPHP Sail via https://localhost, pois usar https://127.0.0.1 requer configuração adicional e é desencorajado.

FrankenPHP via Docker

Usar as imagens oficiais do Docker do FrankenPHP pode oferecer melhor desempenho e o uso de extensões adicionais não incluídas nas instalações estáticas do FrankenPHP. Além disso, as imagens oficiais do Docker oferecem suporte para executar o FrankenPHP em plataformas que ele não suporta nativamente, como o Windows. As imagens oficiais do Docker do FrankenPHP são adequadas para desenvolvimento local e uso em produção.

Você pode usar o seguinte Dockerfile como ponto de partida para conteinerizar seu aplicativo Laravel com tecnologia FrankenPHP:

FROM dunglas/frankenphp

RUN install-php-extensions \
    pcntl
    # Adicione outras extensões PHP aqui...

COPY . /app

ENTRYPOINT ["php", "artisan", "octane:frankenphp"]

Então, durante o desenvolvimento, você pode utilizar o seguinte arquivo Docker Compose para executar seu aplicativo:

# compose.yaml
services:
  frankenphp:
    build:
      context: .
    entrypoint: php artisan octane:frankenphp --max-requests=1
    ports:
      - "8000:8000"
    volumes:
      - .:/app

Você pode consultar a documentação oficial do FrankenPHP para obter mais informações sobre como executar o FrankenPHP com o Docker.

RoadRunner

RoadRunner é alimentado pelo binário RoadRunner, que é construído usando Go. Na primeira vez que você iniciar um servidor Octane baseado em RoadRunner, o Octane oferecerá o download e a instalação do binário RoadRunner para você.

RoadRunner via Laravel Sail

Se você planeja desenvolver seu aplicativo usando Laravel Sail, você deve executar os seguintes comandos para instalar o Octane e o RoadRunner:

./vendor/bin/sail up

./vendor/bin/sail composer require laravel/octane spiral/roadrunner-cli spiral/roadrunner-http 

Em seguida, você deve iniciar um shell Sail e usar o executável rr para recuperar a última compilação baseada em Linux do binário RoadRunner:

./vendor/bin/sail shell

# Dentro do Sail shell...
./vendor/bin/rr get-binary

Então, adicione uma variável de ambiente SUPERVISOR_PHP_COMMAND à definição de serviço laravel.test no arquivo docker-compose.yml do seu aplicativo. Esta variável de ambiente conterá o comando que o Sail usará para servir seu aplicativo usando o Octane em vez do servidor de desenvolvimento PHP:

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=80" # [tl! add]

Finalmente, garanta que o binário rr seja executável e crie suas imagens Sail:

chmod +x ./rr

./vendor/bin/sail build --no-cache

Swoole

Se você planeja usar o servidor de aplicativos Swoole para servir seu aplicativo Laravel Octane, você deve instalar a extensão Swoole PHP. Normalmente, isso pode ser feito via PECL:

pecl install swoole

Open Swoole

Se você deseja usar o servidor de aplicativos Open Swoole para servir seu aplicativo Laravel Octane, você deve instalar a extensão Open Swoole PHP. Normalmente, isso pode ser feito via PECL:

pecl install openswoole

Usar o Laravel Octane com o Open Swoole concede a mesma funcionalidade fornecida pelo Swoole, como tarefas simultâneas, ticks e intervalos.

Swoole via Laravel Sail

::: warning ATENÇÃO Antes de servir um aplicativo Octane via Sail, certifique-se de ter a versão mais recente do Laravel Sail e execute ./vendor/bin/sail build --no-cache no diretório raiz do seu aplicativo. :::

Como alternativa, você pode desenvolver seu aplicativo Octane baseado em Swoole usando Laravel Sail, o ambiente de desenvolvimento oficial baseado em Docker para Laravel. O Laravel Sail inclui a extensão Swoole por padrão. No entanto, você ainda precisará ajustar o arquivo docker-compose.yml usado pelo Sail.

Para começar, adicione uma variável de ambiente SUPERVISOR_PHP_COMMAND à definição de serviço laravel.test no arquivo docker-compose.yml do seu aplicativo. Esta variável de ambiente conterá o comando que o Sail usará para servir seu aplicativo usando o Octane em vez do servidor de desenvolvimento 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=80" # [tl! add]

Finalmente, crie suas imagens Sail:

./vendor/bin/sail build --no-cache

Configuração do Swoole

O Swoole suporta algumas opções de configuração adicionais que você pode adicionar ao seu arquivo de configuração octane se necessário. Como raramente precisam ser modificadas, essas opções não estão incluídas no arquivo de configuração padrão:

'swoole' => [
    'options' => [
        'log_file' => storage_path('logs/swoole_http.log'),
        'package_max_length' => 10 * 1024 * 1024,
    ],
],

Servindo seu aplicativo

O servidor Octane pode ser iniciado por meio do comando Artisan octane:start. Por padrão, esse comando utilizará o servidor especificado pela opção de configuração server do arquivo de configuração octane do seu aplicativo:

php artisan octane:start

Por padrão, o Octane iniciará o servidor na porta 8000, para que você possa acessar seu aplicativo em um navegador da Web por meio de http://localhost:8000.

Servindo seu aplicativo via HTTPS

Por padrão, os aplicativos executados via Octane geram links prefixados com http://. A variável de ambiente OCTANE_HTTPS, usada no arquivo de configuração config/octane.php do seu aplicativo, pode ser definida como true ao servir seu aplicativo via HTTPS. Quando esse valor de configuração é definido como true, o Octane instruirá o Laravel a prefixar todos os links gerados com https://:

'https' => env('OCTANE_HTTPS', false),

Servindo seu aplicativo via Nginx

::: info NOTA Se você não estiver pronto para gerenciar sua própria configuração de servidor ou não estiver confortável configurando todos os vários serviços necessários para executar um aplicativo Laravel Octane robusto, confira Laravel Forge. :::

Em ambientes de produção, você deve servir seu aplicativo Octane por trás de um servidor web tradicional, como Nginx ou Apache. Isso permitirá que o servidor web sirva seus ativos estáticos, como imagens e folhas de estilo, bem como gerencie a terminação do seu certificado SSL.

No exemplo de configuração do Nginx abaixo, o Nginx servirá os ativos estáticos do site e as solicitações de proxy para o servidor Octane que está sendo executado na porta 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;
    }
}

Observando as alterações de arquivo

Como seu aplicativo é carregado na memória uma vez quando o servidor Octane é iniciado, quaisquer alterações nos arquivos do seu aplicativo não serão refletidas quando você atualizar seu navegador. Por exemplo, as definições de rota adicionadas ao seu arquivo routes/web.php não serão refletidas até que o servidor seja reiniciado. Para sua conveniência, você pode usar o sinalizador --watch para instruir o Octane a reiniciar automaticamente o servidor em quaisquer alterações de arquivo dentro do seu aplicativo:

php artisan octane:start --watch

Antes de usar este recurso, você deve garantir que o Node esteja instalado em seu ambiente de desenvolvimento local. Além disso, você deve instalar a biblioteca de monitoramento de arquivos Chokidar em seu projeto:

npm install --save-dev chokidar

Você pode configurar os diretórios e arquivos que devem ser monitorados usando a opção de configuração watch no arquivo de configuração config/octane.php do seu aplicativo.

Especificando a contagem de trabalhadores

Por padrão, o Octane iniciará um trabalhador de solicitação de aplicativo para cada núcleo de CPU fornecido pela sua máquina. Esses trabalhadores serão usados ​​para atender às solicitações HTTP recebidas conforme elas entram no seu aplicativo. Você pode especificar manualmente quantos workers você gostaria de iniciar usando a opção --workers ao invocar o comando octane:start:

php artisan octane:start --workers=4

Se você estiver usando o servidor de aplicativos Swoole, você também pode especificar quantos "task workers" você deseja iniciar:

php artisan octane:start --workers=4 --task-workers=6

Especificando a Contagem Máxima de Solicitações

Para ajudar a evitar vazamentos de memória, o Octane reinicia graciosamente qualquer worker depois de ter manipulado 500 solicitações. Para ajustar esse número, você pode usar a opção --max-requests:

php artisan octane:start --max-requests=250

Recarregando os Workers

Você pode reiniciar normalmente os workers de aplicativos do servidor Octane usando o comando octane:reload. Normalmente, isso deve ser feito após a implantação para que seu código recém-implantado seja carregado na memória e usado para atender a solicitações subsequentes:

php artisan octane:reload

Parando o servidor

Você pode parar o servidor Octane usando o comando Artisan octane:stop:

php artisan octane:stop

Verificando o status do servidor

Você pode verificar o status atual do servidor Octane usando o comando Artisan octane:status:

php artisan octane:status

Injeção de dependência e Octane

Como o Octane inicializa seu aplicativo uma vez e o mantém na memória enquanto atende às solicitações, há algumas ressalvas que você deve considerar ao criar seu aplicativo. Por exemplo, os métodos register e boot dos provedores de serviço do seu aplicativo serão executados apenas uma vez quando o trabalhador da solicitação inicializar. Em solicitações subsequentes, a mesma instância do aplicativo será reutilizada.

Em vista disso, você deve tomar cuidado especial ao injetar o contêiner de serviço do aplicativo ou a solicitação no construtor de qualquer objeto. Ao fazer isso, esse objeto pode ter uma versão obsoleta do contêiner ou da solicitação em solicitações subsequentes.

O Octane manipulará automaticamente a redefinição de qualquer estado de estrutura primária entre as solicitações. No entanto, o Octane nem sempre sabe como redefinir o estado global criado pelo seu aplicativo. Portanto, você deve estar ciente de como criar seu aplicativo de uma forma amigável ao Octane. Abaixo, discutiremos as situações mais comuns que podem causar problemas ao usar o Octane.

Injeção de contêiner

Em geral, você deve evitar injetar o contêiner de serviço do aplicativo ou a instância de solicitação HTTP nos construtores de outros objetos. Por exemplo, a seguinte vinculação injeta todo o contêiner de serviço do aplicativo em um objeto que é vinculado como um singleton:

use App\Service;
use Illuminate\Contracts\Foundation\Application;

/**
 * Registre quaisquer serviços de aplicação.
 */
public function register(): void
{
    $this->app->singleton(Service::class, function (Application $app) {
        return new Service($app);
    });
}

Neste exemplo, se a instância Service for resolvida durante o processo de inicialização do aplicativo, o contêiner será injetado no serviço e esse mesmo contêiner será mantido pela instância Service em solicitações subsequentes. Isso pode não ser um problema para seu aplicativo específico; no entanto, pode levar o contêiner a perder inesperadamente vinculações que foram adicionadas posteriormente no ciclo de inicialização ou por uma solicitação subsequente.

Como solução alternativa, você pode parar de registrar a vinculação como um singleton ou injetar um closure de resolvedor de contêiner no serviço que sempre resolve a instância de contêiner atual:

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());
});

O auxiliar global app e o método Container::getInstance() sempre retornarão a versão mais recente do contêiner do aplicativo.

Injeção de solicitação

Em geral, você deve evitar injetar o contêiner de serviço do aplicativo ou a instância de solicitação HTTP nos construtores de outros objetos. Por exemplo, a seguinte vinculação injeta a instância de solicitação inteira em um objeto que é vinculado como um singleton:

use App\Service;
use Illuminate\Contracts\Foundation\Application;

/**
 * Registre quaisquer serviços de aplicação.
 */
public function register(): void
{
    $this->app->singleton(Service::class, function (Application $app) {
        return new Service($app['request']);
    });
}

Neste exemplo, se a instância Service for resolvida durante o processo de inicialização do aplicativo, a solicitação HTTP será injetada no serviço e essa mesma solicitação será mantida pela instância Service em solicitações subsequentes. Portanto, todos os cabeçalhos, entradas e dados de string de consulta estarão incorretos, assim como todos os outros dados de solicitação.

Como solução alternativa, você pode parar de registrar a vinculação como um singleton ou injetar um closure de resolução de solicitação no serviço que sempre resolve a instância de solicitação atual. Ou a abordagem mais recomendada é simplesmente passar as informações de solicitação específicas que seu objeto precisa para um dos métodos do objeto em tempo de execução:

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']);
});

// Ou...

$service->method($request->input('name'));

O auxiliar global request sempre retornará a solicitação que o aplicativo está manipulando no momento e, portanto, é seguro para uso em seu aplicativo.

::: warning ATENÇÃO É aceitável dar uma dica de tipo na instância Illuminate\Http\Request em seus métodos de controlador e fechamentos de rota. :::

Injeção de repositório de configuração

Em geral, você deve evitar injetar a instância do repositório de configuração nos construtores de outros objetos. Por exemplo, a seguinte ligação injeta o repositório de configuração em um objeto que é ligado como um singleton:

use App\Service;
use Illuminate\Contracts\Foundation\Application;

/**
 * Registre quaisquer serviços de aplicação.
 */
public function register(): void
{
    $this->app->singleton(Service::class, function (Application $app) {
        return new Service($app->make('config'));
    });
}

Neste exemplo, se os valores de configuração mudarem entre as solicitações, esse serviço não terá acesso aos novos valores porque depende da instância do repositório original.

Como solução alternativa, você pode parar de registrar a ligação como um singleton ou pode injetar um closure de resolvedor de repositório de configuração na classe:

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'));
});

O config global sempre retornará a versão mais recente do repositório de configuração e, portanto, é seguro para uso em seu aplicativo.

Gerenciando vazamentos de memória

Lembre-se, o Octane mantém seu aplicativo na memória entre as solicitações; portanto, adicionar dados a uma matriz mantida estaticamente resultará em um vazamento de memória. Por exemplo, o controlador a seguir tem um vazamento de memória, pois cada solicitação ao aplicativo continuará adicionando dados à matriz estática $data:

use App\Service;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

/**
 * Handle an incoming request.
 */
public function index(Request $request): array
{
    Service::$data[] = Str::random(10);

    return [
        // ...
    ];
}

Ao construir seu aplicativo, você deve tomar cuidado especial para evitar criar esses tipos de vazamentos de memória. É recomendável que você monitore o uso de memória do seu aplicativo durante o desenvolvimento local para garantir que não esteja introduzindo novos vazamentos de memória em seu aplicativo.

Tarefas simultâneas

::: warning ATENÇÃO Este recurso requer Swoole. :::

Ao usar o Swoole, você pode executar operações simultaneamente por meio de tarefas leves em segundo plano. Você pode fazer isso usando o método concurrently do Octane. Você pode combinar este método com a desestruturação de array PHP para recuperar os resultados de cada operação:

use App\Models\User;
use App\Models\Server;
use Laravel\Octane\Facades\Octane;

[$users, $servers] = Octane::concurrently([
    fn () => User::all(),
    fn () => Server::all(),
]);

Tarefas simultâneas processadas pelo Octane utilizam os "task workers" do Swoole e são executadas em um processo totalmente diferente da solicitação recebida. A quantidade de workers disponíveis para processar tarefas simultâneas é determinada pela diretiva --task-workers no comando octane:start:

php artisan octane:start --workers=4 --task-workers=6

Ao invocar o método concurrently, você não deve fornecer mais de 1024 tarefas devido às limitações impostas pelo sistema de tarefas do Swoole.

Ticks e intervalos

::: warning ATENÇÃO Este recurso requer Swoole. :::

Ao usar o Swoole, você pode registrar operações "tick" que serão executadas a cada número especificado de segundos. Você pode registrar retornos de chamada "tick" por meio do método tick. O primeiro argumento fornecido ao método tick deve ser uma string que representa o nome do ticker. O segundo argumento deve ser um callable que será invocado no intervalo especificado.

Neste exemplo, registraremos um encerramento a ser invocado a cada 10 segundos. Normalmente, o método tick deve ser chamado dentro do método boot de um dos provedores de serviço do seu aplicativo:

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

Usando o método immediate, você pode instruir o Octane a invocar imediatamente o retorno de chamada tick quando o servidor Octane inicializar inicialmente e a cada N segundos depois disso:

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

O cache Octane

::: warning ATENÇÃO Este recurso requer Swoole. :::

Ao usar o Swoole, você pode aproveitar o driver de cache Octane, que fornece velocidades de leitura e gravação de até 2 milhões de operações por segundo. Portanto, este driver de cache é uma excelente escolha para aplicativos que precisam de velocidades extremas de leitura/gravação de sua camada de cache.

Este driver de cache é alimentado por tabelas Swoole. Todos os dados armazenados no cache estão disponíveis para todos os trabalhadores no servidor. No entanto, os dados em cache serão liberados quando o servidor for reiniciado:

Cache::store('octane')->put('framework', 'Laravel', 30);

::: info NOTA O número máximo de entradas permitidas no cache Octane pode ser definido no arquivo de configuração octane do seu aplicativo. :::

Intervalos de cache

Além dos métodos típicos fornecidos pelo sistema de cache do Laravel, o driver de cache Octane apresenta caches baseados em intervalo. Esses caches são atualizados automaticamente no intervalo especificado e devem ser registrados no método boot de um dos provedores de serviço do seu aplicativo. Por exemplo, o seguinte cache será atualizado a cada cinco segundos:

use Illuminate\Support\Str;

Cache::store('octane')->interval('random', function () {
    return Str::random(10);
}, seconds: 5);

Tabelas

::: warning ATENÇÃO Este recurso requer Swoole. :::

Ao usar o Swoole, você pode definir e interagir com suas próprias tabelas Swoole arbitrárias. As tabelas Swoole fornecem rendimento de desempenho extremo e os dados nessas tabelas podem ser acessados ​​por todos os trabalhadores no servidor. No entanto, os dados dentro delas serão perdidos quando o servidor for reiniciado.

As tabelas devem ser definidas na matriz de configuração tables do arquivo de configuração octane do seu aplicativo. Uma tabela de exemplo que permite um máximo de 1000 linhas já está configurada para você. O tamanho máximo de colunas de string pode ser configurado especificando o tamanho da coluna após o tipo de coluna, conforme visto abaixo:

'tables' => [
    'example:1000' => [
        'name' => 'string:1000',
        'votes' => 'int',
    ],
],

Para acessar uma tabela, você pode usar o método Octane::table:

use Laravel\Octane\Facades\Octane;

Octane::table('example')->set('uuid', [
    'name' => 'Nuno Maduro',
    'votes' => 1000,
]);

return Octane::table('example')->get('uuid');

::: warning ATENÇÃO Os tipos de coluna suportados pelas tabelas Swoole são: string, int e float. :::