[停止服务器](#stopping-the-server)Laravel Octane 大幅提升您应用程序的性能,它通过使用高性能应用服务器,包括 FrankenPHP,Open Swoole,Swoole,和 RoadRunner,来服务您的应用程序。Octane 会一次性启动您的应用程序,将其保存在内存中,然后以超音速处理请求。
Octane 可以通过 Composer 包管理器安装:
composer require laravel/octane安装 Octane 后,您可以执行 octane:install Artisan 命令,该命令会将 Octane 的配置文件安装到您的应用中:
php artisan octane:install[FrankenPHP](https://frankenphp.dev) 是一个用 Go 编写的 PHP 应用程序服务器,支持现代 Web 功能,例如早期提示、Brotli 和 Zstandard 压缩。当您安装 Octane 并选择 FrankenPHP 作为您的服务器时,Octane 将自动为您下载并安装 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}'" # [tl! add]
XDG_CONFIG_HOME: /var/www/html/config # [tl! add]
XDG_DATA_HOME: /var/www/html/data # [tl! add]要启用 HTTPS、HTTP/2 和 HTTP/3,请改为应用这些修改:
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]通常,您应该通过 https://localhost 访问您的 FrankenPHP Sail 应用程序,因为使用 https://127.0.0.1 需要额外配置,并且不建议。
使用 FrankenPHP 的官方 Docker 镜像可以提供改进的性能,并可使用 FrankenPHP 静态安装中未包含的额外扩展。此外,官方 Docker 镜像还支持在 FrankenPHP 原生不支持的平台,例如 Windows 上运行 FrankenPHP。FrankenPHP 的官方 Docker 镜像既适用于本地开发,也适用于生产用途。
您可以使用以下 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如果 --log-level 选项被显式传递给 php artisan octane:start 命令,Octane 将使用 FrankenPHP 的原生日志记录器 并且,除非另行配置,将生成结构化的 JSON 日志。
您可以查阅 官方 FrankenPHP 文档 以获取有关使用 Docker 运行 FrankenPHP 的更多信息。
当使用 FrankenPHP 时,您可以在启动 Octane 时,使用 --caddyfile 选项指定一个自定义的 Caddyfile:
php artisan octane:start --server=frankenphp --caddyfile=/path/to/your/Caddyfile这让您能够自定义 FrankenPHP 的配置,使其超出默认设置的范围,例如添加自定义中间件、配置高级路由,或设置自定义指令。 您可以查阅 Caddy 官方文档 以获取有关 Caddyfile 语法和配置选项的更多信息。
RoadRunner 由 RoadRunner 二进制文件提供支持,该文件使用 Go 构建。首次启动基于 RoadRunner 的 Octane 服务器时,Octane 会提示你下载并安装 RoadRunner 二进制文件。
如果你计划使用 Laravel Sail 开发你的应用程序,你应该运行以下命令来安装 Octane 和 RoadRunner:
./vendor/bin/sail up
./vendor/bin/sail composer require laravel/octane spiral/roadrunner-cli spiral/roadrunner-http接下来,您应该启动一个 Sail shell 并使用 rr 可执行文件来获取 RoadRunner 二进制文件的最新 Linux 版本:
./vendor/bin/sail shell
# Within the Sail shell...
./vendor/bin/rr get-binary然后,添加一个 SUPERVISOR_PHP_COMMAND 环境变量到你应用的 docker-compose.yml 文件中的 laravel.test 服务定义中。这个环境变量将包含 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 应用服务器来为您的 Laravel Octane 应用程序提供服务,您必须安装 Swoole PHP 扩展。通常,这可以通过 PECL 完成:
pecl install swoole如果您想使用 Open Swoole 应用服务器来服务您的 Laravel Octane 应用,您必须安装 Open Swoole PHP 扩展。通常,这可以通过 PECL 完成:
pecl install openswoole将 Laravel Octane 与 Open Swoole 结合使用,可提供 Swoole 提供的相同功能,例如并发任务、滴答事件和定时器。
[!警告]
在通过 Sail 提供 Octane 应用程序之前,请确保您拥有最新版本的 Laravel Sail,并在您的应用程序的根目录中执行./vendor/bin/sail build --no-cache。
或者,您可以使用 Laravel Sail 开发基于 Swoole 的 Octane 应用程序,它是 Laravel 官方的基于 Docker 的开发环境。Laravel Sail 默认包含 Swoole 扩展。但是,您仍然需要调整 Sail 使用的 docker-compose.yml 文件。
要开始使用,请添加一个 SUPERVISOR_PHP_COMMAND 环境变量到你的应用程序 docker-compose.yml 文件中的 laravel.test 服务定义中。这个环境变量将包含 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-cacheSwoole 支持一些额外的配置选项,你可以在必要时将其添加到你的 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 在网络浏览器中访问您的应用程序。
如果您正在将 Octane 应用程序部署到生产环境,您应该使用一个进程监控器,例如 Supervisor,以确保 Octane 服务器持续运行。一个适用于 Octane 的 Supervisor 配置文件示例可能如下所示:
[program:octane]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/example.com/artisan octane:start --server=frankenphp --host=127.0.0.1 --port=8000
autostart=true
autorestart=true
user=forge
redirect_stderr=true
stdout_logfile=/home/forge/example.com/storage/logs/octane.log
stopwaitsecs=3600默认情况下,通过 Octane 运行的应用程序生成的链接前缀为 http://。OCTANE_HTTPS 环境变量,在您的应用程序的 config/octane.php 配置文件中使用,可以在通过 HTTPS 提供您的应用程序时设置为 true。当此配置值设置为 true 时,Octane 将指示 Laravel 为所有生成的链接添加 https:// 前缀:
'https' => env('OCTANE_HTTPS', false),[!NOTE]
如果您尚未准备好管理自己的服务器配置,或者不熟悉配置运行健壮的 Laravel Octane 应用程序所需的各种服务,请了解 Laravel Cloud,它提供完全托管的 Laravel Octane 支持。
在生产环境中,你应该在传统的 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你可以配置应被监视的目录和文件使用 watch 配置选项在你应用的 config/octane.php 配置文件中。
默认情况下,Octane 将为您的机器提供的每个 CPU 核心启动一个应用程序请求工作进程。这些工作进程将用于处理进入您应用程序的传入 HTTP 请求。您可以在调用 octane:start 命令时,使用 --workers 选项手动指定您希望启动的工作进程数量:
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默认情况下,Laravel Octane 通过应用程序 config/octane.php 配置文件中的 max_execution_time 选项,为传入请求设置了 30 秒的最大执行时间:
'max_execution_time' => 30,此设置定义了入站请求在被终止前允许执行的最大秒数。将此值设置为 0 将完全禁用执行时间限制。此配置选项对于处理长时间运行请求的应用程序特别有用,例如文件上传、数据处理或对外部服务的 API 调用。
[!WARNING]
当您修改max_execution_time配置时,您必须重启 Octane 服务器以使更改生效。
您可以平滑地重启 Octane 服务器的应用工作进程,使用 octane:reload 命令。通常,这应该在部署后进行,以便您的新部署代码加载到内存中,并用于处理后续请求:
php artisan octane:reload您可以使用 octane:stop Artisan 命令停止 Octane 服务器:
php artisan octane:stop您可以使用 octane:status Artisan 命令来检查 Octane 服务器的当前状态:
php artisan octane:status由于 Octane 会一次性启动您的应用程序并将其保存在内存中以处理请求,因此在构建您的应用程序时,您需要考虑一些注意事项。例如,您的应用程序服务提供者的 register 和 boot 方法只会在请求工作进程首次启动时执行一次。在后续请求中,相同的应用程序实例将会被重用。
鉴于此,当将应用程序服务容器或请求注入到任何对象的构造函数中时,您应该特别小心。这样做,该对象在后续请求中可能会持有一个过时的容器或请求版本。
Octane 将自动处理在请求之间重置任何第一方框架状态。然而,Octane 并非总是知道如何重置您的应用程序创建的全局状态。因此,您应该了解如何以对 Octane 友好的方式构建您的应用程序。下面,我们将讨论在使用 Octane 时可能导致问题的最常见情况。
通常来说,你应该避免将应用服务容器或 HTTP 请求实例注入到其他对象的构造函数中。例如,以下绑定将整个应用服务容器注入到一个绑定为单例的对象中:
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);
});
}在此示例中,如果 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;
/**
* Register any application services.
*/
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 助手将始终返回应用程序当前正在处理的请求,因此可以在你的应用程序中安全使用。
[!WARNING]
可以对控制器方法和路由闭包中的Illuminate\Http\Request实例进行类型提示。
一般来说,你应该避免将配置仓库实例注入到其他对象的构造函数中。例如,以下绑定将配置仓库注入到一个以单例形式绑定的对象中:
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'));
});
}在此示例中,如果配置值在请求之间发生更改,该服务将无法访问新值,因为它依赖于原始存储库实例。
作为一个权宜之计,你可以停止将该绑定注册为单例,或者你可以向该类注入一个配置仓库解析器闭包:
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;
/**
* Handle an incoming request.
*/
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 命令上的 --task-workers 指令决定:
php artisan octane:start --workers=4 --task-workers=6在调用 concurrently 方法时,您不应提供超过 1024 个任务因 Swoole 任务系统施加的限制。
[!WARNING]
此功能需要 Swoole.
在使用 Swoole 时,你可能会注册“tick”操作,这些操作会每隔指定的秒数执行一次。 你可以通过 tick 方法注册“tick”回调。 提供给 tick 方法的第一个参数应该是一个字符串,它表示计时器的名称。 第二个参数应该是一个可调用对象,它将以指定的间隔被调用。
在此示例中,我们将注册一个闭包,使其每10秒调用一次。通常,tick 方法应在 boot 方法中调用,该方法位于你的某个应用程序服务提供者中:
Octane::tick('simple-ticker', fn () => ray('Ticking...'))
->seconds(10);使用 immediate 方法,您可以指示 Octane 在 Octane 服务器初次启动时立即调用 tick 回调,以及此后每 N 秒调用一次:
Octane::tick('simple-ticker', fn () => ray('Ticking...'))
->seconds(10)
->immediate();[!WARNING]
此功能需要 Swoole。
使用 Swoole 时,您可以利用 Octane 缓存驱动,它提供高达每秒 200 万次操作的读写速度。因此,此缓存驱动是适用于需要从缓存层获得极致读写速度的应用程序的绝佳选择。
此缓存驱动由 Swoole 表 驱动。存储在缓存中的所有数据对服务器上的所有 worker 都可用。但是,当服务器重启时,缓存的数据将被清除:
Cache::store('octane')->put('framework', 'Laravel', 30);[!NOTE]
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 表提供极高的性能吞吐量,并且这些表中的数据可以被服务器上的所有 worker 访问。然而,当服务器重启时,其中的数据将会丢失。
表应在您应用程序的 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');[!WARNING]
Swoole 表支持的列类型有:string,int, 和float。