FrankenPHP不能单进程部署多个服务实例,必须用systemd或supervisor启动多个独立worker进程,各自监听不同端口、加载不同绝对路径脚本,实现完全隔离与可靠管理。

FrankenPHP 不能靠单进程“部署多个服务实例”
FrankenPHP 本身不是进程管理器,它的 frankenphp-worker 命令只接受一个 --script 参数,且该参数必须是绝对路径。试图在一条命令里传多个 --script,会直接报错:unknown flag: --script。它也不支持“一个实例跑多个 worker 脚本”的抽象——每个 worker 进程绑定且仅执行一个 PHP 入口文件,彼此无共享、无通信、无调度。
真正可行的方式:用 systemd 或 supervisor 启多个独立 worker 实例
这是生产环境唯一可靠的做法。每个实例是一个完整、隔离的 FrankenPHP worker 进程,各自监听不同端口、加载不同脚本、拥有独立日志和资源限制。
-
--addr必须互不冲突(例如:8081、:8082),否则第二个实例启动时因端口被占而立即退出 -
--script必须是绝对路径(如/var/www/app1/worker.php),相对路径会导致failed to open stream: No such file or directory - 推荐为每个服务建单独的 systemd service 文件,例如
frankenphp-queue.service和frankenphp-notifier.service,便于启停、日志分离(journalctl -u frankenphp-queue)、内存限制(MemoryMax=256M) - 不要在
worker.php内部靠$_ENV或配置做“多租户路由”,这会破坏进程隔离性,导致信号处理异常、内存泄漏,且无法横向扩缩容
Caddyfile 里不能“把域名转发给某个 worker 实例”
这是常见误解。php_server 是 HTTP 请求处理器,运行在主线程池中;而 frankenphp-worker 是后台常驻任务(如队列监听、邮件推送),两者不在同一抽象层。Caddy 不会、也不能把 api.example.com 的请求“转发给 :8081 上的 worker 进程”——worker 不处理 HTTP 请求。
- 如果你需要按域名分发 Web 流量(比如
app1.example.com和app2.example.com),应该用 Caddy 的多server块 +php_server,共享同一个 FrankenPHP 进程 - 如果你需要两个长期运行的后台任务(比如一个消费 Redis 队列,一个轮询 SMTP 状态),就必须起两个
frankenphp-worker实例,并由 systemd/supervisor 管理它们的生命周期 - worker 实例之间完全隔离,如需通信,只能走外部中间件(Redis、PostgreSQL NOTIFY、HTTP API 等)
Docker 场景下怎么组织多个 worker?
一个容器只应运行一个主进程。强行在一个 FrankenPHP 容器里塞多个 frankenphp-worker,会破坏容器的信号传递和健康检查逻辑。正确做法是:
立即学习“PHP免费学习笔记(深入)”;
- 每个 worker 实例单独打包成一个容器(例如
frankenphp-queue、frankenphp-scheduler) - 或在宿主机用 systemd 管理所有 worker,容器只负责 Web 流量(即 Caddy + PHP 线程池)
- 如果坚持单容器多 worker,必须引入额外 init 进程(如
tini)并自行处理 SIGTERM 转发、日志聚合、退出码收集——复杂度陡增,不推荐
容易被忽略的关键点:worker 实例的 --addr 端口只是用于健康探测或调试(比如 Prometheus 拉取指标),它和 Web 流量无关;真正的业务隔离靠的是进程边界,不是端口路由。



















