该页面已由社区翻译,可能不是最新的。参见参考版本(英文)。

该页面已由社区翻译,可能不是最新的。参见参考版本(英文)。

该页面已由社区翻译,可能不是最新的。参见参考版本(英文)。

由...提供支持
性能

性能

默认情况下,FrankenPHP 尝试在性能和易用性之间提供良好的折衷。 但是,通过使用适当的配置,可以大幅提高性能。

# 线程和 Worker 数量

默认情况下,FrankenPHP 启动的线程和 worker(在 worker 模式下)数量是可用 CPU 核心数的 2 倍。

适当的值很大程度上取决于你的应用程序是如何编写的、它做什么以及你的硬件。 我们强烈建议更改这些值。为了获得最佳的系统稳定性,建议 num_threads x memory_limit < available_memory

要找到正确的值,最好运行模拟真实流量的负载测试。 k6Gatling 是很好的工具。

要配置线程数,请使用 php_serverphp 指令的 num_threads 选项。 要更改 worker 数量,请使用 frankenphp 指令的 worker 部分的 num 选项。

# max_threads

虽然准确了解你的流量情况总是更好,但现实应用往往更加 不可预测。max_threads 配置 允许 FrankenPHP 在运行时自动生成额外线程,直到指定的限制。 max_threads 可以帮助你确定需要多少线程来处理你的流量,并可以使服务器对延迟峰值更具弹性。 如果设置为 auto,限制将基于你的 php.ini 中的 memory_limit 进行估算。如果无法这样做, auto 将默认为 2x num_threads。请记住,auto 可能会严重低估所需的线程数。 max_threads 类似于 PHP FPM 的 pm.max_children。主要区别是 FrankenPHP 使用线程而不是 进程,并根据需要自动在不同的 worker 脚本和"经典模式"之间委派它们。

# Worker 模式

启用 worker 模式 大大提高了性能, 但你的应用必须适配以兼容此模式: 你需要创建一个 worker 脚本并确保应用不会泄漏内存。

# 不要使用 musl

官方 Docker 镜像的 Alpine Linux 变体和我们提供的默认二进制文件使用 musl libc

众所周知,当使用这个替代 C 库而不是传统的 GNU 库时,PHP 更慢, 特别是在以 ZTS 模式(线程安全)编译时,这是 FrankenPHP 所必需的。在大量线程环境中,差异可能很显著。

另外,一些错误只在使用 musl 时发生

在生产环境中,我们建议使用链接到 glibc 的 FrankenPHP,并使用适当的优化级别进行编译。

这可以通过使用 Debian Docker 镜像、使用我们的维护者提供的 .deb、.rpm 或 .apk 包,或通过从源代码编译 FrankenPHP 来实现。

对于更精简或更安全的容器,你可能需要考虑使用强化的 Debian 镜像而不是 Alpine。

# Go 运行时配置

FrankenPHP 是用 Go 编写的。

一般来说,Go 运行时不需要任何特殊配置,但在某些情况下, 特定的配置可以提高性能。

你可能想要将 GODEBUG 环境变量设置为 cgocheck=0(FrankenPHP Docker 镜像中的默认值)。

如果你在容器(Docker、Kubernetes、LXC…)中运行 FrankenPHP 并限制容器的可用内存, 请将 GOMEMLIMIT 环境变量设置为可用内存量。

有关更多详细信息,专门针对此主题的 Go 文档页面 是充分利用运行时的必读内容。

# file_server

默认情况下,php_server 指令自动设置文件服务器来 提供存储在根目录中的静态文件(资产)。

此功能很方便,但有成本。 要禁用它,请使用以下配置:

php_server {
    file_server off
}

# try_files

除了静态文件和 PHP 文件外,php_server 还会尝试提供你应用程序的索引 和目录索引文件(/path/ -> /path/index.php)。如果你不需要目录索引, 你可以通过明确定义 try_files 来禁用它们,如下所示:

php_server {
    try_files {path} index.php
    root /root/to/your/app # 在这里明确添加根目录允许更好的缓存
}

这可以显著减少不必要的文件操作数量。 上述配置的 worker 等效项为:

route {
    php_server { # 如果完全不需要文件服务器,请使用 "php" 而不是 "php_server"
        root /root/to/your/app
        worker /path/to/worker.php {
            match * # 将所有请求直接发送到 worker
        }
    }
}

另一种具有 0 个不必要文件系统操作的方法是改用 php 指令并按路径将 文件与 PHP 分开。如果你的整个应用程序由一个入口文件提供服务,这种方法效果很好。 一个在 /assets 文件夹后面提供静态文件的示例配置可能如下所示:

route {
    @assets {
        path /assets/*
    }

    # /assets 后面的所有内容都由文件服务器处理
    file_server @assets {
        root /root/to/your/app
    }

    # 不在 /assets 中的所有内容都由你的索引或 worker PHP 文件处理
    rewrite index.php
    php {
        root /root/to/your/app # 在这里明确添加根目录允许更好的缓存
    }
}

# 占位符

你可以在 rootenv 指令中使用占位符。 但是,这会阻止缓存这些值,并带来显著的性能成本。

如果可能,请避免在这些指令中使用占位符。

# resolve_root_symlink

默认情况下,如果文档根目录是符号链接,FrankenPHP 会自动解析它(这对于 PHP 正常工作是必要的)。 如果文档根目录不是符号链接,你可以禁用此功能。

php_server {
    resolve_root_symlink false
}

如果 root 指令包含占位符,这将提高性能。 在其他情况下,收益将可以忽略不计。

# 日志

日志显然非常有用,但根据定义, 它需要 I/O 操作和内存分配,这会大大降低性能。 确保你正确设置日志级别, 并且只记录必要的内容。

# PHP 性能

FrankenPHP 使用官方 PHP 解释器。 所有常见的 PHP 相关性能优化都适用于 FrankenPHP。

特别是:

有关更多详细信息,请阅读专门的 Symfony 文档条目 (即使你不使用 Symfony,大多数提示也很有用)。

# 拆分线程池

应用程序与慢速外部服务交互是很常见的,例如在高负载下往往不可靠或持续需要 10 秒以上才能响应的 API。 在这种情况下,将线程池拆分以拥有专用的“慢速”池可能会很有益。这可以防止慢速端点消耗所有服务器资源/线程,并限制指向慢速端点的请求并发性,类似于连接池。

example.com {
    php_server {
        root /app/public # 你的应用程序根目录
        worker index.php {
            match /slow-endpoint/* # 所有路径为 /slow-endpoint/* 的请求都由这个线程池处理
            num 1 # 匹配 /slow-endpoint/* 的请求至少有 1 个线程
            max_threads 20 # 如果需要,允许最多 20 个线程处理匹配 /slow-endpoint/* 的请求
        }
        worker index.php {
            match * # 所有其他请求单独处理
            num 1 # 其他请求至少有 1 个线程,即使慢速端点开始挂起
            max_threads 20 # 如果需要,允许最多 20 个线程处理其他请求
        }
    }
}

通常,也建议通过使用消息队列等相关机制,异步处理非常慢的端点。

编辑此页面