Octane(加速引擎)

未匹配的标注

Laravel Octane

介绍

Laravel Octane 旨在为您的应用程序提供高性能服务,支持 SwooleRoadRunner。 Octane 启动您的应用程序一次,将其保存在内存中,然后以超音速向其发送请求。

安装

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

composer require laravel/octane

安装 Octane 后,您可以执行 octane:install Artisan 命令,这会将 Octane 的配置文件安装到应用程序中:

php artisan octane:install

服务器先决条件

注意:Laravel Octane 需要 PHP 8.0+

RoadRunner

RoadRunner 由使用 Go 构建的 PHP 服务中间层。当您第一次启动 RoadRunner 的 Octane 服务器时,Octane 会为您下载并安装 RoadRunner 二进制文件。

通过 Laravel Sail 安装 RoadRunner

如果您打算使用 Laravel Sail 开发您的应用程序,您应该运行以下命令来安装 Octane 和 RoadRunner:

./vendor/bin/sail up

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

接下来应启动 Sail shell 并使用 rr 可执行文件来检查基于 Linux 的最新版本的 RoadRunner 二进制文件:

./vendor/bin/sail shell

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

安装 RoadRunner 二进制文件后可退出 Sail shell 会话。接下来需要调整 Sail 使用的 supervisor.conf 文件以保持应用运行。首先,执行 sail:publish Artisan 命令:

./vendor/bin/sail artisan sail:publish

接下来,更新应用程序的 docker/supervisord.conf 文件的 command 指令,以便 Sail 使用 Octane 作为服务器为您的应用提供服务:

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=8000

最后,确保 rr 二进制文件是可执行的并重新构建 Sail 镜像:

chmod +x ./rr

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

Swoole

如果你打算使用 Swoole 应用服务器来配合 Laravel Octane,你必须安装 Swoole PHP 扩展。通常可以通过 PECL 完成:

pecl install swoole

通过 Laravel Sail 安装 Swoole

注意:在通过 Sail 提供 Octane 应用程序之前,请确保您拥有最新版本的 Laravel Sail 并在应用程序的根目录中执行 ./vendor/bin/sail build --no-cache

您可以使用 Laravel Sail (Laravel 基于 Docker 的官方开发环境)开发基于 Swoole 的 Octane 应用程序。Laravel Sail 默认包含 Swoole 扩展。但是,您仍然需要调整 Sail 使用的 supervisor.conf 文件以保持应用运行。首先,执行 sail:publish Artisan 命令:

./vendor/bin/sail artisan sail:publish

接下来,更新应用程序的 docker/supervisord.conf 文件的 command 指令,以便 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=8000

最后,构建您的 Sail 镜像:

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

启用服务

Octane 服务器可以通过 octane:start Artisan 命令启动。此命令将使用由应用程序的 octane 配置文件的 server 配置选项指定的服务器:

php artisan octane:start

Octane 将在 8000 端口上启动服务器(可配置),因此您可以在 Web 浏览器中通过 http://localhost:8000 访问您的应用程序。

使用 HTTPS

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

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

通过 Nginx 为应用提供服务

提示:如果你还没有准备好管理自己的服务器配置,或者不习惯配置运行健壮的 Laravel Octane 应用所需的所有各种服务,请查看 Laravel Forge

在生产环境中,您应该在传统 Web 服务器(例如 Nginx 或 Apache)之后为 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;
    }
}

监视文件修改

由于应用在 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

指定最大请求计数

为了防止内存泄漏,Octane 可在处理了给定数量的请求后优雅地重新启动 worker。要指示 Octane 执行此操作,您可以使用 --max-requests 选项:

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

重载 Workers

您可以使用 octane:reload 命令优雅地重新启动 Octane 服务器的应用 workers。通常,这应该在部署后完成,以便将新部署的代码加载到内存中并用于为后续请求提供服务:

php artisan octane:reload

停止服务器

你可以使用 octane:stop Artisan 命令停止 Octane 服务器:

php artisan octane:stop

检查服务器状态

你可以使用 octane:status Artisan 命令检查 Octane 服务器的当前状态:

php artisan octane:status

依赖注入和 Octane

因为代码启用一次后,将其保存在内存中,因此在构建应用程序时应特别注意。例如,应用程序服务提供器的 registerboot 方法只会在请求工作器最初启动时执行一次。在后续请求中,将重用相同的应用程序实例。

鉴于此,在将应用服务容器或请求注入任何对象的构造函数时应特别小心。该对象需支持容器的陈旧版本用以对后续请求的支持。

Octane 将自动处理在请求之间重置框架状态。但是,Octane 并不总是知道如何重置由您的应用创建的全局状态。因此,您应该了解如何以一种对 Octane 友好的方式构建您的应用。下面,我们将讨论在使用 Octane 时可能导致问题的常见情况。

容器注入

一般来说,你应该避免将应用服务容器或 HTTP 请求实例注入到其他对象的构造函数中。例如,以下绑定将整个应用程序服务容器注入到绑定为单例的对象中:

use App\Service;

/**
 * 注册应用服务。
 *
 * @return void
 */
public function register()
{
    $this->app->singleton(Service::class, function ($app) {
        return new Service($app);
    });
}

在此示例中,如果在应用程序启动过程中解析了 Service 实例,则容器将被注入到服务中,并且该容器将在后续请求中由 Service 实例持有。这 可能 对于您的特定应用程序来说不是问题;但是,它可能导致容器意外丢失在引导周期后期或后续请求中添加的绑定。

作为解决方法,可以停止将绑定注册为单例,或者将容器解析器闭包注入到始终解析当前容器实例的服务中:

use App\Service;
use Illuminate\Container\Container;

$this->app->bind(Service::class, function ($app) {
    return new Service($app);
});

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

全局 app 助手和 Container::getInstance() 方法会一直返回应用容器的最新版本。

请求注入

通常,应该避免在其他对象的构造器中注入应用服务容器或 HTTP 请求实例。例如,下面将一整个请求实例作为单例绑定到一个对象上:

use App\Service;

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

例子中,如果 Service 实例在应用启动时生成,HTTP 请求会注入到这个服务中,随后请求中的相同请求都会被 Service 实例处理。这样,全部的 headers ,输入,和查询数据,以及其他数据都会不正确的。

想要解决此问题,就需要停止单例绑定,或者将请求处理程序注入到一个始终生成当前请求实例的服务中。又或者,最推荐的方法是将对象需要的请求信息作为传参:

use App\Service;

$this->app->bind(Service::class, function ($app) {
    return new Service($app['request']);
});

$this->app->singleton(Service::class, function ($app) {
    return new Service(fn () => $app['request']);
});

// Or...

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

全局 request 助手始终返回当前处理的请求,因此可以安全地使用。

提示:控制器方法中或者路由闭包中,依然可以使用 Illuminate\Http\Request 作为类型提示。

配置仓库注入

通常,应该避免将配置仓库实例注入到其他对象的构造器中。例如,下面的绑定将配置仓库作为单例注入到对象中:

use App\Service;

/**
 * 注册程序服务
 *
 * @return void
 */
public function register()
{
    $this->app->singleton(Service::class, function ($app) {
        return new Service($app->make('config'));
    });
}

例子中,如果请求中的配置值改变了,服务将无法获取新值,因为它依赖原始的仓库实例。

如要解决,应该停止单例绑定,或者将配置仓库注入到一个类中:

use App\Service;
use Illuminate\Container\Container;

$this->app->bind(Service::class, function ($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;

/**
 * 处理列表请求
 *
 * @param  \Illuminate\Http\Request  $request
 * @return void
 */
public function index(Request $request)
{
    Service::$data[] = Str::random(10);

    // ...
}

开发应用时,需要特别注意尽量避免造成这些类型的内存泄漏。本地开发时时刻监控内存使用情况,从而确保不会造成新的内存泄漏。

并行任务

注意:这个功能仅支持 Swoole.

Swoole 允许并行执行一些轻量化的后台任务。 这可以通过 Octane 的 concurrently 方法实现。可以将这个方法与 PHP 数组解构结合来接收每个操作的结果:

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

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

Octane 利用 Swoole 的 “task workers” 处理并行任务,在与传入请求完全不同的进程中执行。并行任务可用的执行者的数量由 octane:start 命令的 --task-workers 参数决定:

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

计时与间隔

注意:此功能仅支持 Swoole.

Swoole 中,“计时”操作能够按指定的秒数间隔执行。tick 方法可以注册 “计时” 回调操作。tick 方法的第一个参数代表计时器的名称,第二个参数代表指定间隔执行的回调。

例子中,注册了一个每 10 秒执行一次的闭包。通常,tick 方法应该在应用服务提供者的 boot 方法中调用:

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

使用 immediate 方法指示 Octane 在启动服务时立即调用 tick 回调,此后每N秒调用一次:

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

Octane 缓存

注意:此功能仅支持 Swoole.

使用 Swoole 时,您可以利用 Octane 缓存驱动程序,它提供高达每秒 200 万次操作的读取和写入速度。因此,对于需要从其缓存层获得极高读/写速度的应用程序,此缓存驱动程序是绝佳选择。

此缓存驱动程序由 Swoole tables 提供支持。 服务器上的所有 Workers 都可以使用缓存中存储的所有数据。但是,当服务器重新启动时,缓存的数据将被刷新:

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 tables 并与之交互。 Swoole 表提供了极高的性能吞吐量,服务器上的所有 Workers 都可以访问这些表中的数据。但是,当服务器重新启动时,其中的数据将丢失。

表在应用 octane 配置文件 tables 数组配置中设定。最大运行 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 表支持的列类型有: stringintfloat

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

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
上一篇 下一篇
Summer
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
贡献者:5
讨论数量: 0
发起讨论 只看当前版本


暂无话题~