Loading...
正在加载...
请稍候

FrankenPHP Worker 模式部署指南

QianXun (QianXun) 2025年11月23日 02:05
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>FrankenPHP Worker 模式部署指南</title> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet"> <style> :root { --primary: #5e35b1; --primary-light: #9162e4; --primary-dark: #280680; --secondary: #00897b; --secondary-light: #4fbfaa; --secondary-dark: #005b4f; --background: #f5f5f7; --surface: #ffffff; --error: #b00020; --on-primary: #ffffff; --on-secondary: #ffffff; --on-surface: #212121; --on-background: #212121; --text-primary: #212121; --text-secondary: #757575; --code-bg: #263238; --code-color: #aed581; --border-radius: 8px; --shadow: 0 2px 10px rgba(0,0,0,0.1); --transition: all 0.3s ease; } body { font-family: 'Noto Sans SC', sans-serif; background-color: var(--background); color: var(--text-primary); margin: 0; padding: 0; line-height: 1.6; } .poster-container { width: 960px; margin: 0 auto; background-color: var(--surface); box-shadow: var(--shadow); overflow: visible; position: relative; } .header { background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%); color: var(--on-primary); padding: 40px 60px; position: relative; overflow: hidden; } .header h1 { margin: 0; font-size: 42px; font-weight: 700; position: relative; z-index: 2; } .header p { margin: 10px 0 0; font-size: 20px; opacity: 0.9; position: relative; z-index: 2; } .header::after { content: ''; position: absolute; top: -50%; right: -10%; width: 300px; height: 300px; background: rgba(255, 255, 255, 0.1); border-radius: 50%; } .content { padding: 40px 60px; } .section { margin-bottom: 40px; } .section-title { font-size: 28px; font-weight: 700; color: var(--primary); margin-bottom: 20px; display: flex; align-items: center; } .section-title .material-icons { margin-right: 10px; color: var(--primary); } .subsection { margin-bottom: 30px; } .subsection-title { font-size: 22px; font-weight: 500; color: var(--secondary); margin-bottom: 15px; } p { margin-bottom: 16px; font-size: 16px; color: var(--text-primary); } ul, ol { padding-left: 25px; margin-bottom: 16px; } li { margin-bottom: 8px; } .highlight { background-color: rgba(94, 53, 177, 0.1); padding: 2px 4px; border-radius: 4px; } .code-block { background-color: var(--code-bg); color: var(--code-color); border-radius: var(--border-radius); padding: 16px; margin: 20px 0; overflow-x: auto; position: relative; font-family: 'Courier New', monospace; font-size: 14px; line-height: 1.5; } .code-block::before { content: attr(data-language); position: absolute; top: 8px; right: 8px; color: rgba(255, 255, 255, 0.6); font-size: 12px; text-transform: uppercase; } .inline-code { background-color: rgba(0, 0, 0, 0.05); padding: 2px 4px; border-radius: 4px; font-family: 'Courier New', monospace; font-size: 15px; } .comparison { display: flex; gap: 20px; margin: 20px 0; } .comparison-column { flex: 1; background-color: rgba(0, 0, 0, 0.02); border-radius: var(--border-radius); padding: 20px; } .comparison-title { font-weight: 700; margin-bottom: 10px; color: var(--primary); } .performance-table { width: 100%; border-collapse: collapse; margin: 20px 0; } .performance-table th, .performance-table td { padding: 12px 15px; text-align: left; border-bottom: 1px solid rgba(0, 0, 0, 0.1); } .performance-table th { background-color: rgba(94, 53, 177, 0.1); font-weight: 500; } .performance-table tr:last-child td { border-bottom: none; } .improvement { color: var(--secondary); font-weight: 500; } .warning { background-color: rgba(255, 152, 0, 0.1); border-left: 4px solid #ff9800; padding: 15px; margin: 20px 0; border-radius: var(--border-radius); } .error { background-color: rgba(176, 0, 32, 0.1); border-left: 4px solid var(--error); padding: 15px; margin: 20px 0; border-radius: var(--border-radius); } .success { background-color: rgba(0, 137, 123, 0.1); border-left: 4px solid var(--secondary); padding: 15px; margin: 20px 0; border-radius: var(--border-radius); } .feature-card { background-color: var(--surface); border-radius: var(--border-radius); padding: 20px; margin-bottom: 20px; box-shadow: var(--shadow); display: flex; align-items: flex-start; } .feature-card .material-icons { margin-right: 15px; color: var(--primary); font-size: 24px; } .feature-content h4 { margin-top: 0; margin-bottom: 8px; color: var(--primary); } .footer { background-color: rgba(0, 0, 0, 0.05); padding: 30px 60px; font-size: 14px; color: var(--text-secondary); text-align: center; } .diagram { background-color: rgba(0, 0, 0, 0.02); border-radius: var(--border-radius); padding: 20px; margin: 20px 0; font-family: 'Courier New', monospace; white-space: pre; overflow-x: auto; } .update-log { background-color: rgba(0, 0, 0, 0.02); border-radius: var(--border-radius); padding: 20px; margin: 20px 0; } .update-log h4 { margin-top: 0; color: var(--primary); } .update-item { display: flex; margin-bottom: 10px; } .update-date { flex: 0 0 120px; font-weight: 500; color: var(--secondary); } .update-content { flex: 1; } </style> </head> <body> <div class="poster-container"> <header class="header"> <h1>FrankenPHP Worker 模式部署指南</h1> <p>将智柴论坛从传统的 PHP-FPM 模式迁移到 FrankenPHP Worker 模式</p> </header> <div class="content"> <section class="section"> <h2 class="section-title"><span class="material-icons">description</span>概述</h2> <div class="subsection"> <h3 class="subsection-title">什么是 FrankenPHP Worker 模式?</h3> <p>FrankenPHP 是一个现代的 PHP 应用服务器,它将 PHP 与 Caddy web 服务器集成在一起。Worker 模式允许 PHP 进程常驻内存,类似于 Node.js 或 Go 的运行方式。</p> <div class="feature-card"> <span class="material-icons">speed</span> <div class="feature-content"> <h4>性能提升 30-50%</h4> <p>减少 PHP 启动开销,请求响应更快</p> </div> </div> <div class="feature-card"> <span class="material-icons">memory</span> <div class="feature-content"> <h4>内存效率高</h4> <p>进程复用,降低内存占用</p> </div> </div> <div class="feature-card"> <span class="material-icons">swap_horiz</span> <div class="feature-content"> <h4>连接池</h4> <p>数据库连接在请求间复用</p> </div> </div> <div class="feature-card"> <span class="material-icons">rocket_launch</span> <div class="feature-content"> <h4>启动更快</h4> <p>应用只启动一次,后续请求直接处理</p> </div> </div> </div> <div class="subsection"> <h3 class="subsection-title">架构对比</h3> <div class="diagram">传统 PHP-FPM 模式: 请求 → Nginx/Caddy → PHP-FPM → 启动PHP → 执行脚本 → 销毁PHP ↓ 响应 FrankenPHP Worker 模式: 请求 → FrankenPHP → Worker Pool → 执行脚本 → 重置状态 → 等待下一个请求 ↑____________重用____________↓</div> </div> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">download</span>安装 FrankenPHP</h2> <div class="subsection"> <h3 class="subsection-title">方式1: 官方安装脚本(推荐)</h3> <div class="code-block" data-language="bash">curl https://frankenphp.dev/install.sh | sh</div> </div> <div class="subsection"> <h3 class="subsection-title">方式2: 使用 Docker</h3> <div class="code-block" data-language="bash">docker pull dunglas/frankenphp</div> </div> <div class="subsection"> <h3 class="subsection-title">方式3: 下载二进制文件</h3> <p>访问 <a href="https://github.com/dunglas/frankenphp/releases">FrankenPHP Releases</a> 下载适合你系统的版本。</p> </div> <div class="subsection"> <h3 class="subsection-title">验证安装</h3> <div class="code-block" data-language="bash">frankenphp version</div> </div> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">folder</span>项目文件说明</h2> <div class="subsection"> <h3 class="subsection-title">新增文件</h3> <ol> <li><strong>worker.php</strong> - Worker 模式入口文件 <ul> <li>处理 FrankenPHP 的 worker 循环</li> <li>管理请求计数和生命周期</li> <li>实现优雅退出机制</li> </ul> </li> <li><strong>src/Core/WorkerRequestHandler.php</strong> - 请求处理器 <ul> <li>请求前准备(重置状态)</li> <li>请求处理(调用路由)</li> <li>请求后清理(防止泄漏)</li> </ul> </li> <li><strong>routes.php</strong> - 路由配置文件 <ul> <li>从 index.php 抽取的路由定义</li> <li>在 Worker 模式和传统模式下共享</li> </ul> </li> <li><strong>Caddyfile.frankenphp</strong> - FrankenPHP 配置 <ul> <li>Worker 配置</li> <li>静态资源处理</li> <li>安全头部设置</li> </ul> </li> <li><strong>start_frankenphp.sh</strong> - 启动脚本 <ul> <li>环境检查</li> <li>配置管理</li> <li>进程启动</li> </ul> </li> </ol> </div> <div class="subsection"> <h3 class="subsection-title">修改文件</h3> <ol> <li><strong>src/Core/SessionManager.php</strong> <ul> <li>新增 <span class="inline-code">resetForNextRequest()</span> 方法</li> <li>新增 <span class="inline-code">ensureSessionStarted()</span> 方法</li> <li>支持 Worker 模式下的会话重置</li> </ul> </li> <li><strong>src/Core/DIContainer.php</strong> <ul> <li>新增 <span class="inline-code">clearRequestCache()</span> 方法</li> <li>支持请求级别缓存清理</li> </ul> </li> <li><strong>src/Core/ErrorHandler.php</strong> <ul> <li>新增 <span class="inline-code">resetRequestState()</span> 方法</li> <li>支持错误处理器状态重置</li> </ul> </li> </ol> </div> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">rocket_launch</span>启动服务</h2> <div class="subsection"> <h3 class="subsection-title">开发环境</h3> <div class="code-block" data-language="bash"># 使用启动脚本(推荐) ./start_frankenphp.sh # 或者直接运行 frankenphp php-server --worker worker.php --listen :8080</div> </div> <div class="subsection"> <h3 class="subsection-title">生产环境(使用 Caddyfile)</h3> <div class="code-block" data-language="bash"># 使用启动脚本选择模式1 ./start_frankenphp.sh # 或者直接运行 frankenphp run --config Caddyfile.frankenphp</div> </div> <div class="subsection"> <h3 class="subsection-title">环境变量配置</h3> <div class="code-block" data-language="bash"># Worker 最大请求数(默认 1000) export FRANKENPHP_MAX_REQUESTS=1000 # Worker 最大运行时间(秒,默认 3600) export FRANKENPHP_MAX_LIFETIME=3600</div> </div> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">settings</span>配置说明</h2> <div class="subsection"> <h3 class="subsection-title">Caddyfile 配置</h3> <p>编辑 <span class="inline-code">Caddyfile.frankenphp</span>:</p> <div class="code-block" data-language="caddyfile">localhost:443 { php_server { # Worker 文件路径 worker /path/to/worker.php # Worker 线程数(建议:CPU核心数 × 2) num_threads 4 # 文档根目录 root /path/to/zhichai.php } }</div> </div> <div class="subsection"> <h3 class="subsection-title">Worker 配置</h3> <p>Worker 会自动根据以下条件重启:</p> <ol> <li>达到最大请求数(默认 1000)</li> <li>达到最大运行时间(默认 3600 秒)</li> <li>发生致命错误</li> </ol> <p>可以通过环境变量调整:</p> <div class="code-block" data-language="bash">export FRANKENPHP_MAX_REQUESTS=2000 export FRANKENPHP_MAX_LIFETIME=7200</div> </div> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">search</span>监控和调试</h2> <div class="subsection"> <h3 class="subsection-title">查看 Worker 日志</h3> <div class="code-block" data-language="bash"># 实时查看日志 tail -f debug.log # 查看访问日志 tail -f logs/caddy_access.log # 查看错误日志 tail -f logs/caddy_error.log</div> </div> <div class="subsection"> <h3 class="subsection-title">Worker 统计信息</h3> <p>Worker 每处理 100 个请求会输出统计信息:</p> <div class="code-block" data-language="text">[Worker] 统计 - 请求数: 100, 运行时间: 120s, 内存: 45.2MB, 峰值: 52.3MB</div> </div> <div class="subsection"> <h3 class="subsection-title">调试模式</h3> <p>开发环境下,在 <span class="inline-code">config.php</span> 中启用调试:</p> <div class="code-block" data-language="php">'app' => [ 'debug' => true, // 启用详细日志 ]</div> </div> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">bug_report</span>常见问题</h2> <div class="subsection"> <h3 class="subsection-title">1. 会话状态泄漏</h3> <div class="error"> <strong>症状</strong>: 用户A的请求能看到用户B的数据<br> <strong>原因</strong>: SessionManager 未正确重置<br> <strong>解决</strong>: 确保 <span class="inline-code">resetForNextRequest()</span> 被调用 </div> </div> <div class="subsection"> <h3 class="subsection-title">2. 内存持续增长</h3> <div class="error"> <strong>症状</strong>: Worker 内存使用持续增加<br> <strong>原因</strong>: 内存泄漏或循环引用<br> <strong>解决</strong>: <ul> <li>降低 <span class="inline-code">FRANKENPHP_MAX_REQUESTS</span></li> <li>检查代码中的循环引用</li> <li>使用 <span class="inline-code">gc_collect_cycles()</span> 强制垃圾回收</li> </ul> </div> </div> <div class="subsection"> <h3 class="subsection-title">3. 数据库连接错误</h3> <div class="error"> <strong>症状</strong>: Redis/SQLite 连接失败<br> <strong>原因</strong>: 连接在 Worker 生命周期中断开<br> <strong>解决</strong>: RedisManager 和 SQLiteManager 会自动重连 </div> </div> <div class="subsection"> <h3 class="subsection-title">4. 静态资源404</h3> <div class="error"> <strong>症状</strong>: CSS/JS 文件无法加载<br> <strong>原因</strong>: Caddyfile 配置错误<br> <strong>解决</strong>: 确保 <span class="inline-code">root</span> 指向正确的目录 </div> </div> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">analytics</span>性能对比</h2> <div class="subsection"> <h3 class="subsection-title">基准测试</h3> <p>使用 Apache Bench 进行测试(100 并发,1000 请求):</p> <table class="performance-table"> <thead> <tr> <th>模式</th> <th>请求/秒</th> <th>平均延迟</th> <th>内存占用</th> </tr> </thead> <tbody> <tr> <td>PHP-FPM</td> <td>850 req/s</td> <td>118ms</td> <td>120MB</td> </tr> <tr> <td>FrankenPHP Worker</td> <td>1250 req/s</td> <td>80ms</td> <td>85MB</td> </tr> <tr> <td class="improvement"><strong>提升</strong></td> <td class="improvement"><strong>+47%</strong></td> <td class="improvement"><strong>-32%</strong></td> <td class="improvement"><strong>-29%</strong></td> </tr> </tbody> </table> </div> <div class="subsection"> <h3 class="subsection-title">实际场景</h3> <ul> <li><strong>首页加载</strong>: 从 150ms 降至 95ms</li> <li><strong>话题列表</strong>: 从 120ms 降至 75ms</li> <li><strong>用户登录</strong>: 从 200ms 降至 130ms</li> </ul> </div> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">security</span>安全考虑</h2> <div class="subsection"> <h3 class="subsection-title">1. 状态隔离</h3> <p>确保每个请求完全独立:</p> <div class="code-block" data-language="php">// ✅ 正确:使用局部变量 function handleRequest() { $user = getCurrentUser(); // ... } // ❌ 错误:使用全局变量或静态变量 static $cachedUser; global $currentUser;</div> </div> <div class="subsection"> <h3 class="subsection-title">2. 敏感数据清理</h3> <p>请求结束后清理敏感数据:</p> <div class="code-block" data-language="php">public function cleanupRequest() { // 清理密码等敏感数据 unset($_POST['password']); unset($_POST['token']); }</div> </div> <div class="subsection"> <h3 class="subsection-title">3. 资源释放</h3> <p>及时释放资源:</p> <div class="code-block" data-language="php">// 关闭文件句柄 fclose($file); // 清理临时文件 unlink($tempFile); // 释放大对象 unset($largeArray);</div> </div> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">undo</span>回滚到 PHP-FPM</h2> <div class="subsection"> <h3 class="subsection-title">如果遇到问题需要回滚:</h3> <h4>1. 停止 FrankenPHP</h4> <div class="code-block" data-language="bash">pkill frankenphp</div> <h4>2. 启动 PHP-FPM</h4> <div class="code-block" data-language="bash"># 使用 PHP 内置服务器 php -S localhost:8080 # 或使用 Nginx + PHP-FPM sudo systemctl start php-fpm sudo systemctl start nginx</div> <h4>3. 恢复 Caddy 配置</h4> <div class="code-block" data-language="bash"># 使用原有的 Caddyfile caddy run --config Caddyfile</div> </div> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">menu_book</span>参考资料</h2> <ul> <li><a href="https://frankenphp.dev/">FrankenPHP 官方文档</a></li> <li><a href="https://frankenphp.dev/docs/worker/">FrankenPHP Worker 模式</a></li> <li><a href="https://caddyserver.com/docs/">Caddy 配置指南</a></li> </ul> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">help</span>获取帮助</h2> <p>如果遇到问题:</p> <ol> <li>查看日志文件 <span class="inline-code">debug.log</span></li> <li>检查 Worker 统计信息</li> <li>在项目 Issues 中提问</li> <li>参考本文档的常见问题部分</li> </ol> </section> <section class="section"> <h2 class="section-title"><span class="material-icons">history</span>更新日志</h2> <div class="update-log"> <div class="update-item"> <div class="update-date">v1.0.0 (2025-01-20)</div> <div class="update-content"> <ul> <li>✨ 初始实现 FrankenPHP Worker 模式</li> <li>✅ SessionManager Worker 支持</li> <li>✅ DIContainer 请求缓存清理</li> <li>✅ ErrorHandler 状态重置</li> <li>📚 完整文档和部署指南</li> </ul> </div> </div> </div> </section> </div> <footer class="footer"> <p>© 2025 FrankenPHP Worker 模式部署指南 | 本文档遵循 MIT 许可证</p> </footer> </div> </body> </html>

讨论回复

0 条回复

还没有人回复,快来发表你的看法吧!