
在当今云原生与微服务大行其道的时代,PHP 应用面临着「冷启动延迟高」「进程管理复杂」「性能瓶颈难以突破」等痛点。
FrankenPHP 正是为了解决这些问题而生:它将 Caddy 服务器与 PHP 运行时深度融合,内嵌 Let's Encrypt 自动 HTTPS、支持 HTTP/2/3、Early Hints 预提示,以及可选的 Worker 模式常驻进程,从根本上消除冷启动与外部依赖。
这篇文章将带你逐步了解 FrankenPHP 的核心架构原理,手把手演示从环境安装、Caddyfile 配置到高并发实战、微服务嵌入,再到生产部署与监控调优的全流程。
📚 目录
- 📖 简介
- 🧱 架构原理
- 🔧 安装与环境准备
- 💾 二进制安装
- 🐳 Docker 容器
- 🍺 Homebrew(macOS/Linux)
 
- 🚀 快速起步
- 🗂️ 最简示例
- 🔁 脚本模式
 
- ⚙️ 配置详解
- 🧾 Caddyfile 结构与指令
- 🧪 常用环境变量
- 📝 PHP 运行时配置
 
- ✨ 核心特性实操
- 👷 Worker 模式
- 🚦 Early Hints(HTTP 103)
- 🔌 实时推送(WebSocket/SSE)
 
- 🧩 主流框架集成
- ⚡ Laravel
- 🎼 Symfony
- 📝 WordPress
 
- 🔗 Go 应用嵌入
- 📈 性能调优与生产部署
- 🔍 性能压测工具
- 🔐 安全与 TLS
- ☸️ Kubernetes 部署示例
 
- 🩺 监控与故障排查
- 📋 日志管理
- 📊 Prometheus 指标
- 🛠️ 调试技巧
 
- 🛒 实战案例:电商下单服务
- ❓ 常见问题解答
📖 简介
FrankenPHP 将 Caddy HTTP 服务器 与 PHP 运行时 深度融合,提供一体化、高性能的 PHP 服务平台:
- 自动 HTTPS:内置 Let's Encrypt 证书管理,支持 HTTP/2/3
- Zero Cold Start:Worker 模式常驻进程,避免二次启动延迟
- 实时能力:原生 WebSocket 和 SSE 支持
- Early Hints:HTTP 103 提示,实现资源预加载
- 单二进制部署:无外部 PHP-FPM、Caddy 进程依赖,简化运维
适用于中小型团队快速构建、以及大规模微服务化场景。
🧱 架构原理
Client ↔ Caddy 核心 ↔ 嵌入式 PHP SAPI ↔ Worker 池 ↔ 应用业务代码- Caddy 核心:负责 TLS、路由、静态资源、HTTP 特性(压缩、Early Hints)
- 嵌入式 PHP SAPI:将 PHP 引擎及扩展编译进二进制,通过内存管道与 Caddy 交互
- Worker 池:可选长驻模式,基于线程/进程池调度 PHP 脚本执行,持久化连接池、缓存
🔧 安装与环境准备
💾 二进制安装(生产推荐)
            
            
              bash
              
              
            
          
          curl -fsSL https://frankenphp.dev/install.sh | sh
sudo mv frankenphp /usr/local/bin/
# 验证安装
frankenphp --version- 支持 x86_64 Linux & macOS,内置 PHP 8.4 与常用扩展
- 无额外依赖,直接启动即可
🐳 Docker 容器(开发/测试)
            
            
              yaml
              
              
            
          
          # docker-compose.yml
version: '3.8'
services:
  web:
    image: dunglas/frankenphp:latest
    volumes:
      - ./public:/app/public
      - ./config:/etc/frankenphp
    environment:
      SERVER_NAME: 'example.com'
    ports:
      - '80:80'
      - '443:443'
            
            
              bash
              
              
            
          
          docker-compose up -d- 将配置目录挂载到 /etc/frankenphp,便于自定义php.ini
- 支持 GPU 加速镜像(另行指定镜像标签)
🍺 Homebrew(macOS/Linux)
            
            
              bash
              
              
            
          
          brew tap dunglas/frankenphp/frankenphp
brew install frankenphp🚀 快速起步
🗂️ 最简示例
- 
项目目录 textmy-app/ ├── public/ │ └── index.php └── Caddyfile
- 
index.php php<?php echo "Hello, FrankenPHP!";
- 
Caddyfile caddyfilelocalhost { php_server file_server }
- 
启动 bashcd my-app frankenphp run
浏览器访问 http://localhost,显示 "Hello, FrankenPHP!"。
🔁 脚本模式
- 
兼容 PHP CLI: bashfrankenphp php-cli scripts/cleanup.php --force
- 
支持 argv、STDIN/STDOUT,与php-cli用法一致
⚙️ 配置详解
🧾 Caddyfile 结构与指令
            
            
              caddyfile
              
              
            
          
          { # 全局配置
  servers {
    http_port     80
    https_port    443
    early_hints         # HTTP/103
    enable_full_duplex  # 双工支持
  }
  frankenphp {
    num_threads    4         # 并发线程数
    max_wait_time  30s       # 等待线程超时
    php_ini {                # 覆盖 php.ini 设置
      memory_limit        1G
      max_execution_time  60
      upload_max_filesize 200M
    }
    worker {                 # Worker 模式
      file      public/index.php
      num       6            # Worker 进程数
      max_jobs  500          # 单个 Worker 最大请求数
      watch     public/**/*.php  # 热重载
    }
  }
}
example.com {
  root   * /app/public
  encode gzip br
  php_server {
    split .php
    env   APP_ENV=production
    timeouts {
      read_timeout  10s
      write_timeout 10s
    }
  }
  file_server
}- split .php:仅- .php请求走 PHP,其余走静态
- encode:开启 Brotli/Gzip 压缩
- timeouts:精细化控制读写超时
🧪 常用环境变量
| 变量 | 说明 | 默认 | 
|---|---|---|
| SERVER_NAME | TLS 证书域名 | 无 | 
| CADDY_GLOBAL_OPTIONS | 注入全局 Caddy 配置 | 无 | 
| FRANKENPHP_CONFIG | 注入 frankenphp 块配置 | 无 | 
| PHP_INI_SCAN_DIR | 附加 PHP 配置目录 | /etc/frankenphp/conf.d | 
📝 PHP 运行时配置
在 PHP_INI_SCAN_DIR 目录中创建 .ini 文件。例如 /etc/frankenphp/conf.d/custom.ini:
            
            
              ini
              
              
            
          
          display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
opcache.enable=1
opcache.memory_consumption=512
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0  # 生产环境关闭文件监测✨ 核心特性实操
👷 Worker 模式
- 
优势:进程常驻,应用启动仅一次,高并发场景性能稳定。 
- 
实战案例:Redis 连接池 worker.php:php<?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379); while ($req = frankenphp_receive_request()) { $count = $redis->incr('hits'); frankenphp_send_response("Hits: {$count}"); }Caddyfile 配置: caddyfilefrankenphp { worker { file worker.php num 4 max_jobs 1000 watch worker.php } }
🚦 Early Hints(HTTP 103)
目的:提前通知浏览器加载关键资源。
            
            
              php
              
              
            
          
          <?php
header("Link: </app.css>; rel=preload", false, 103);
header("Link: </app.js>; rel=preload", false, 103);
echo "<html><body>Hello with Early Hints</body></html>";🔌 实时推送(WebSocket/SSE)
Caddyfile:
            
            
              caddyfile
              
              
            
          
          servers { enable_full_duplex } 
route /ws* {
  websocket
  reverse_proxy localhost:9000
}后端可使用 Swoole 或 Ratchet 实现服务端逻辑。
🧩 主流框架集成
⚡ Laravel
            
            
              caddyfile
              
              
            
          
          laravel.app {
  root   * /app/public
  encode gzip br
  php_server {
    split .php
    env   APP_ENV=production
    worker {
      file     artisan
      args     serve --port=8000
      num      4
      watch    app/**/*.php
    }
  }
  file_server
}🎼 Symfony
            
            
              caddyfile
              
              
            
          
          symfony.local {
  root   * /srv/symfony/public
  php_server
  file_server
}📝 WordPress
            
            
              caddyfile
              
              
            
          
          wp.local {
  root * /var/www/html
  php_server
  file_server
}🔗 Go 应用嵌入
            
            
              go
              
              
            
          
          package main
import (
  "net/http"
  "github.com/dunglas/frankenphp"
)
func main() {
  frankenphp.Init(
    frankenphp.WithNumThreads(4),
    frankenphp.WithPhpIni(map[string]string{"memory_limit":"256M"}),
    frankenphp.WithWorkers(
      frankenphp.WorkerConfig{File:"public/index.php", Num:4, Watch:[]string{"public/**/*.php"}}, 
    ),
  )
  mux := http.NewServeMux()
  mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    frankenphp.ServeHTTP(w, r)
  }))
  http.ListenAndServe(":8080", mux)
}📈 性能调优与生产部署
🔍 性能压测工具
- wrk:高并发压力测试
- hey:多维度报告
- autocannon:Node.js 压测工具
            
            
              bash
              
              
            
          
          wrk -t8 -c200 -d60s https://example.com🔐 安全与 TLS
- 自动 HTTPS,无需手动配置证书
- 建议全站强制 HTTPS,并配置 HSTS:
            
            
              caddyfile
              
              
            
          
          header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"☸️ Kubernetes 部署示例
            
            
              yaml
              
              
            
          
          apiVersion: apps/v1
kind: Deployment
metadata:
  name: frankenphp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frankenphp
  template:
    metadata:
      labels:
        app: frankenphp
    spec:
      containers:
      - name: web
        image: dunglas/frankenphp:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /app/public
          name: app-code
        env:
        - name: SERVER_NAME
          value: "example.com"
      volumes:
      - name: app-code
        configMap:
          name: my-app-configmap
---
apiVersion: v1
kind: Service
metadata:
  name: frankenphp-svc
spec:
  selector:
    app: frankenphp
  ports:
  - port: 80
    targetPort: 80
  type: LoadBalancer🩺 监控与故障排查
📋 日志管理
- 内置访问日志与错误日志,默认输出到 stdout
- 可挂载至文件或收集至 ELK/Fluentd 等集中式系统
- 实时查看:
            
            
              bash
              
              
            
          
          frankenphp logs -f📊 Prometheus 指标
在 Caddyfile 中启用 /metrics 路由:
            
            
              caddyfile
              
              
            
          
          :80 {
  metrics /metrics
}可采集指标包括:
- frankenphp_requests_total
- frankenphp_active_threads
- frankenphp_worker_jobs_total
🛠️ 调试技巧
- PHP 错误显示(仅开发环境):
            
            
              ini
              
              
            
          
          display_errors = On
error_reporting = E_ALL- 使用 Xdebug 在 IDE 中断点调试 Worker 进程
🛒 实战案例:电商下单服务
- 
业务需求:记录用户下单、库存扣减、异步发货通知 
- 
目录结构: textecommerce/ ├── public/index.php ├── src/ │ ├── OrderController.php │ └── Inventory.php ├── worker/notify.php └── Caddyfile
- 
核心代码: - 
OrderController.php:php<?php namespace Src; class OrderController { public function placeOrder($data) { // 验证、订单入库 Inventory::deduct($data['sku'], $data['qty']); // 异步通知 frankenphp_queue_task('worker/notify.php', $data); return ['status' => 'accepted']; } }
- 
notify.php:php<?php while ($job = frankenphp_receive_task()) { // 发送邮件/短信通知 Mailer::send($job['user_email'], 'Order Confirmed', ...); frankenphp_finish_task($job); }
 
- 
- 
Caddyfile: caddyfile{ frankenphp { worker { file worker/notify.php num 2 max_jobs 100 } } } ecommerce.local { root * /app/public php_server file_server }
- 
启动与测试: 
            
            
              bash
              
              
            
          
          frankenphp run
curl -X POST http://ecommerce.local/order -d '{"sku": "ABC", "qty": 1, "user_email": "user@example.com"}'❓ 常见问题解答
- 
如何优雅重启 Workers? 修改监控目录文件,Worker 会自动重载 
- 
如何扩展 PHP 扩展? 在 PHP_INI_SCAN_DIR下添加extension=xxx.so
- 
如何集成 MySQL 持久连接? 在 Worker 中使用 PDO 或 mysqli 常驻连接,多 Worker 共享连接池