翻译进度
14
分块数量
5
参与人数

Octane

这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。


Laravel 辛烷值

简介

Laravel 辛烷值 通过高性能应用服务器(如 FrankenPHP, Open Swoole, Swoole, 和 RoadRunner)来运行您的应用,从而大幅提升性能。Octane 会在启动时一次性加载您的应用并常驻内存,随后以超高速处理请求。

安装指南

可以通过 Composer 包管理器安装 Octane:

composer require laravel/octane

安装完成后,可以执行 octane:install Artisan 命令,该命令会在您的应用中安装 Octane 的配置文件:

php artisan octane:install

服务器环境要求

[!警告]
Laravel 辛烷值 需要 PHP 8.1+.

FrankenPHP

FrankenPHP 是一款基于 Go 语言开发的 PHP 应用服务器,支持早期提示(early hints)、Brotli 和 Zstandard 压缩等现代 Web 特性。当您安装 Octane 并选择 FrankenPHP 作为服务器时,Octane 将自动为您下载并安装 FrankenPHP 二进制文件。

xineny 翻译于 5个月前

通过 Laravel Sail 使用 FrankenPHP

如果您计划使用 Laravel Sail 进行开发,请运行以下命令安装 Octane 和 FrankenPHP:

./vendor/bin/sail up

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

接着使用 octane:install Artisan 命令安装 FrankenPHP 二进制文件:

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

最后,在您应用的 docker-compose.yml 文件中,为 laravel.test 服务添加 SUPERVISOR_PHP_COMMAND 环境变量。该变量将指定 Sail 使用 Octane 而非 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='${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

建议通过 https://localhost 访问 FrankenPHP Sail 应用。使用 https://127.0.0.1 需要额外配置,且不推荐使用该方式。

xineny 翻译于 5个月前

通过 Docker 使用 FrankenPHP

FrankenPHP 官方 Docker 镜像能提供更优性能,并支持静态安装版未包含的额外扩展。此外,这些镜像还支持在 Windows 等原生不支持 FrankenPHP 的平台上运行,既适用于本地开发也适用于生产环境。

以下 Dockerfile 可作为构建 FrankenPHP 驱动的 Laravel 应用容器的基础模板:

FROM dunglas/frankenphp

RUN install-php-extensions \
    pcntl
    # Add other PHP extensions here...

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

若向 php artisan octane:start 命令显式传递 --log-level 参数,Octane 将启用 FrankenPHP 原生日志记录器,默认生成结构化 JSON 日志(除非另行配置)。

更多 Docker 运行 FrankenPHP 的细节,请参阅官方 FrankenPHP 文档

RoadRunner

RoadRunner 由 Go 语言编写的 RoadRunner 二进制驱动。首次启动基于 RoadRunner 的 Octane 服务时,Octane 将自动提示下载并安装 RoadRunner 二进制文件。

通过 Laravel Sail 使用 RoadRunner

若计划使用 Laravel Sail 开发,请执行以下命令安装 Octane 和 RoadRunner:

./vendor/bin/sail up

./vendor/bin/sail composer require laravel/octane spiral/roadrunner-cli spiral/roadrunner-http
xineny 翻译于 5个月前

接下来,你应该启动一个 Sail shell,并使用 rr 可执行文件来获取最新的基于 Linux 构建的 RoadRunner 二进制文件:

./vendor/bin/sail shell

# 在 Sail shell 中...
./vendor/bin/rr get-binary

然后,在你的应用程序的docker-compose.yml文件中的 laravel.test服务定义中添加一个 SUPERVISOR_PHP_COMMAND 环境变量。这个环境变量将包含 Sail 用来使用 Octane 为你的应用提供服务的命令,而不是使用 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='${APP_PORT:-80}'" # [tl! add]

最后,确保 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 相同的功能,如并发任务、时钟周期和间隔。

通过 Laravel Sail 使用 Swoole

[!警告]
在通过 Sail 提供 Octane 应用服务之前,请确保你有最新版本的 Laravel Sail,并在应用程序的根目录中执行 ./vendor/bin/sail build --no-cache

ieras 翻译于 1周前

或者,你可以使用 Laravel Sail 来开发基于 Swoole 的 Octane 应用程序,这是 Laravel 的官方基于 Docker 的开发环境。Laravel Sail 默认包含 Swoole 扩展。然而,你仍需要调整 Sail 使用的 docker-compose.yml 文件。

要开始,向你应用程序的 docker-compose.yml 文件中的 laravel.test 服务定义中添加一个 SUPERVISOR_PHP_COMMAND 环境变量。这个环境变量将包含 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}'" # [tl! add]

最后,构建你的 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 服务器可以通过 octane:start Artisan 命令启动。默认情况下,此命令将使用你应用程序的 octane 配置文件的 server 指定的服务器:

php artisan octane:start

默认情况下,Octane 将在端口 8000 上启动服务器,所以你可以通过 http://localhost:8000 在 Web 浏览器中访问你的应用程序。

ieras 翻译于 1周前

通过 HTTPS 提供你的应用程序

默认情况下,通过Octane运行的应用程序会生成前缀为 http://的链接。 当通过HTTPS为应用程序提供服务时,可以将应用程序的config/octane.php配置文件中使用的OCTANE_HTTPS环境变量设置为true。 当此配置值设置为true时,Octane将指示Laravel在所有生成的链接前加上https://

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

通过 Nginx 为你的应用提供服务

[注意]
如果你还没有准备好管理自己的服务器配置,或者不熟悉配置运行一个强大的Laravel Octane应用程序所需的所有各种服务,请查看Laravel Cloud,它提供完全托管的Laravel Octane支持。

在生产环境中,您应该在Nginx或Apache等传统web服务器后面为Octane应用程序提供服务。这样做将允许web服务器为您的静态资产(如图像和样式表)提供服务,并管理您的SSL证书终止。

在下面的Nginx配置示例中,Nginx将向运行在端口8000上的Octane服务器提供站点的静态资产和代理请求:

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;
    }
}
_xiaoyang 翻译于 1周前

监视文件更改

由于您的应用程序在Octane服务器启动时仅加载一次到内存中,因此当您刷新浏览器时,对应用程序文件所做的任何更改都不会立即生效。例如,添加到routes/web.php文件中的路由定义只有在服务器重新启动后才会生效。为了方便起见,您可以使用--watch标志来指示Octane在应用程序中的任何文件发生更改时自动重新启动服务器:

php artisan octane:start --watch

在使用此功能之前,您应确保在本地开发环境中已安装Node。此外,您还应在项目中安装Chokidar文件监视库:

npm install --save-dev chokidar

您可以在应用程序的config/octane.php配置文件中,使用watch配置选项来配置应监视的目录和文件。

指定工作进程数量

默认情况下,Octane会为你的机器提供的每个CPU核心启动一个应用程序请求工作进程。当HTTP请求进入你的应用程序时,这些工作进程将用于处理这些请求。在调用octane:start命令时,你可以使用--workers选项手动指定要启动的工作进程数量:

php artisan octane:start --workers=4

如果你正在使用Swoole应用服务器,您还可以指定希望启动多少个任务工作进程

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

_xiaoyang 翻译于 1周前

Specifying the Max Request Count

To help prevent stray memory leaks, Octane gracefully restarts any worker once it has handled 500 requests. To adjust this number, you may use the --max-requests option:

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

Reloading the Workers

You may gracefully restart the Octane server's application workers using the octane:reload command. Typically, this should be done after deployment so that your newly deployed code is loaded into memory and is used to serve to subsequent requests:

php artisan octane:reload

Stopping the Server

You may stop the Octane server using the octane:stop Artisan command:

php artisan octane:stop

Checking the Server Status

You may check the current status of the Octane server using the octane:status Artisan command:

php artisan octane:status

Dependency Injection and Octane

Since Octane boots your application once and keeps it in memory while serving requests, there are a few caveats you should consider while building your application. For example, the register and boot methods of your application's service providers will only be executed once when the request worker initially boots. On subsequent requests, the same application instance will be reused.

In light of this, you should take special care when injecting the application service container or request into any object's constructor. By doing so, that object may have a stale version of the container or request on subsequent requests.

Octane will automatically handle resetting any first-party framework state between requests. However, Octane does not always know how to reset the global state created by your application. Therefore, you should be aware of how to build your application in a way that is Octane friendly. Below, we will discuss the most common situations that may cause problems while using Octane.

Container Injection

In general, you should avoid injecting the application service container or HTTP request instance into the constructors of other objects. For example, the following binding injects the entire application service container into an object that is bound as a singleton:

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

/**
 * Register any application services.
 */
public function register(): void
{
    $this->app->singleton(Service::class, function (Application $app) {
        return new Service($app);
    });
}

In this example, if the Service instance is resolved during the application boot process, the container will be injected into the service and that same container will be held by the Service instance on subsequent requests. This may not be a problem for your particular application; however, it can lead to the container unexpectedly missing bindings that were added later in the boot cycle or by a subsequent request.

As a work-around, you could either stop registering the binding as a singleton, or you could inject a container resolver closure into the service that always resolves the current container instance:

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

The global app helper and the Container::getInstance() method will always return the latest version of the application container.

Request Injection

In general, you should avoid injecting the application service container or HTTP request instance into the constructors of other objects. For example, the following binding injects the entire request instance into an object that is bound as a singleton:

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

/**
 * Register any application services.
 */
public function register(): void
{
    $this->app->singleton(Service::class, function (Application $app) {
        return new Service($app['request']);
    });
}

In this example, if the Service instance is resolved during the application boot process, the HTTP request will be injected into the service and that same request will be held by the Service instance on subsequent requests. Therefore, all headers, input, and query string data will be incorrect, as well as all other request data.

As a work-around, you could either stop registering the binding as a singleton, or you could inject a request resolver closure into the service that always resolves the current request instance. Or, the most recommended approach is simply to pass the specific request information your object needs to one of the object's methods at runtime:

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

The global request helper will always return the request the application is currently handling and is therefore safe to use within your application.

[!WARNING]
It is acceptable to type-hint the Illuminate\Http\Request instance on your controller methods and route closures.

Configuration Repository Injection

In general, you should avoid injecting the configuration repository instance into the constructors of other objects. For example, the following binding injects the configuration repository into an object that is bound as a singleton:

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

/**
 * Register any application services.
 */
public function register(): void
{
    $this->app->singleton(Service::class, function (Application $app) {
        return new Service($app->make('config'));
    });
}

In this example, if the configuration values change between requests, that service will not have access to the new values because it's depending on the original repository instance.

As a work-around, you could either stop registering the binding as a singleton, or you could inject a configuration repository resolver closure to the class:

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

The global config will always return the latest version of the configuration repository and is therefore safe to use within your application.

Managing Memory Leaks

Remember, Octane keeps your application in memory between requests; therefore, adding data to a statically maintained array will result in a memory leak. For example, the following controller has a memory leak since each request to the application will continue to add data to the static $data array:

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 [
        // ...
    ];
}

While building your application, you should take special care to avoid creating these types of memory leaks. It is recommended that you monitor your application's memory usage during local development to ensure you are not introducing new memory leaks into your application.

Concurrent Tasks

[!WARNING]
This feature requires Swoole.

When using Swoole, you may execute operations concurrently via light-weight background tasks. You may accomplish this using Octane's concurrently method. You may combine this method with PHP array destructuring to retrieve the results of each operation:

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

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

Concurrent tasks processed by Octane utilize Swoole's "task workers", and execute within an entirely different process than the incoming request. The amount of workers available to process concurrent tasks is determined by the --task-workers directive on the octane:start command:

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

When invoking the concurrently method, you should not provide more than 1024 tasks due to limitations imposed by Swoole's task system.

Ticks and Intervals

[!WARNING]
This feature requires Swoole.

When using Swoole, you may register "tick" operations that will be executed every specified number of seconds. You may register "tick" callbacks via the tick method. The first argument provided to the tick method should be a string that represents the name of the ticker. The second argument should be a callable that will be invoked at the specified interval.

In this example, we will register a closure to be invoked every 10 seconds. Typically, the tick method should be called within the boot method of one of your application's service providers:

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

Using the immediate method, you may instruct Octane to immediately invoke the tick callback when the Octane server initially boots, and every N seconds thereafter:

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

The Octane Cache

[!WARNING]
This feature requires Swoole.

When using Swoole, you may leverage the Octane cache driver, which provides read and write speeds of up to 2 million operations per second. Therefore, this cache driver is an excellent choice for applications that need extreme read / write speeds from their caching layer.

This cache driver is powered by Swoole tables. All data stored in the cache is available to all workers on the server. However, the cached data will be flushed when the server is restarted:

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

[!NOTE]
The maximum number of entries allowed in the Octane cache may be defined in your application's octane configuration file.

Cache Intervals

In addition to the typical methods provided by Laravel's cache system, the Octane cache driver features interval based caches. These caches are automatically refreshed at the specified interval and should be registered within the boot method of one of your application's service providers. For example, the following cache will be refreshed every five seconds:

use Illuminate\Support\Str;

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

Tables

[!WARNING]
This feature requires Swoole.

When using Swoole, you may define and interact with your own arbitrary Swoole tables. Swoole tables provide extreme performance throughput and the data in these tables can be accessed by all workers on the server. However, the data within them will be lost when the server is restarted.

Tables should be defined within the tables configuration array of your application's octane configuration file. An example table that allows a maximum of 1000 rows is already configured for you. The maximum size of string columns may be configured by specifying the column size after the column type as seen below:

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

To access a table, you may use the Octane::table method:

use Laravel\Octane\Facades\Octane;

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

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

[!WARNING]
The column types supported by Swoole tables are: string, int, and float.

本文章首发在 LearnKu.com 网站上。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
贡献者:5
讨论数量: 0
发起讨论 只看当前版本


暂无话题~