# 11.4 后端部署 :::tip 概述 本节详细说明后端服务的部署配置,包括 Nginx、Apache、PHP 生产优化、定时任务配置,以及多数据库部署注意事项。 ::: ## Nginx 配置 ```nginx # ============================================================================= # Nginx 站点配置 — ThinkPHP 8 后端 API # ============================================================================= # 用途:生产环境 Nginx 反向代理,将 HTTP 请求转发给 PHP-FPM 处理 # 配置路径:/etc/nginx/sites-available/rxthinkcmf # 启用方式:ln -s /etc/nginx/sites-available/rxthinkcmf /etc/nginx/sites-enabled/ # ============================================================================= server { # 监听 80 端口(HTTP),生产环境建议改为 443(HTTPS) listen 80; # 绑定域名(替换为实际域名) server_name api.example.com; # 网站根目录指向 ThinkPHP 入口目录 # 注意:必须指向 public/ 子目录,而非项目根目录 root /www/api/public; index index.php; # ----------------------------------------------------------------- # URL 重写(ThinkPHP 必需) # ----------------------------------------------------------------- # 将所有非真实文件请求转发到 index.php,由 ThinkPHP 路由解析 # 例:GET /api/user/page → /index.php?s=/api/user/page location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=$1 last; } } # ----------------------------------------------------------------- # PHP-FPM 处理 # ----------------------------------------------------------------- # 所有 .php 请求通过 FastCGI 协议转发给 PHP-FPM 进程 # 127.0.0.1:9000 是 PHP-FPM 默认监听地址 location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; # PHP-FPM 监听地址 fastcgi_index index.php; # 默认索引文件 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # 引入标准 FastCGI 参数 # 超时设置(秒),防止长请求(如 Excel 导出)被 Nginx 提前断开 fastcgi_read_timeout 300; # PHP 脚本最长执行时间 fastcgi_send_timeout 300; # Nginx 向 PHP-FPM 发送请求的超时 } # ----------------------------------------------------------------- # 安全规则 # ----------------------------------------------------------------- # 禁止访问隐藏文件(如 .env、.git、.htaccess) location ~ /\. { deny all; } # 禁止上传目录执行 PHP(防止恶意文件上传后被当作脚本执行) location ~ /uploads/.*\.php$ { deny all; } # ----------------------------------------------------------------- # 静态资源缓存 # ----------------------------------------------------------------- # 常见静态文件类型设置 30 天浏览器缓存,减少重复请求 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 30d; add_header Cache-Control "public, immutable"; } # ----------------------------------------------------------------- # Gzip 压缩 # ----------------------------------------------------------------- # 对文本类响应启用压缩,减少传输体积(可节省 60%+ 带宽) gzip on; gzip_types text/plain text/css application/json application/javascript text/xml; gzip_min_length 1024; # 小于 1KB 的响应不压缩(压缩收益低) } ``` ### CDN / 负载均衡部署 当项目部署在 CDN、云防护或负载均衡后面时,PHP 获取到的 `REMOTE_ADDR` 是代理服务器的 IP(通常为 `127.0.0.1`),而非真实客户端 IP。 框架内置 `ProxyMiddleware` 全局中间件,会自动从 `X-Forwarded-For` / `X-Real-IP` 请求头中提取真实客户端 IP。只需在 `.env` 的 `[API]` 段配置受信代理 IP: ```ini [API] # 受信代理 IP(逗号分隔,支持 CIDR 网段) # 腾讯云 CDN 常用 IP 段(完整列表请参考云服务商文档) PROXY_IPS = 127.0.0.1,::1,101.226.0.0/15,112.65.0.0/16,140.207.0.0/16,180.101.0.0/16,182.254.0.0/16,223.166.0.0/16 ``` :::warning 重要 - 未配置 `PROXY_IPS` 时,默认信任 `127.0.0.1` 和 `::1` - **不要**在 Nginx 的 `fastcgi_param` 中手动覆盖 `REMOTE_ADDR`、`HTTP_X_REAL_IP`、`HTTP_X_FORWARDED_FOR`,否则会丢失 CDN 传递的真实 IP - 配置后需清除运行时缓存:`rm -rf runtime/*` ::: ## Apache 配置 项目 `public/.htaccess` 已配置好 URL 重写,确保启用 `mod_rewrite`: ```bash # 启用 Apache 重写模块(ThinkPHP 路由依赖此模块) sudo a2enmod rewrite # 编辑站点配置文件 sudo nano /etc/apache2/sites-available/000-default.conf ``` ```apache # ============================================================================= # Apache 虚拟主机配置 — ThinkPHP 8 后端 API # ============================================================================= # 配置路径:/etc/apache2/sites-available/000-default.conf # 生效命令:sudo systemctl restart apache2 # ============================================================================= # 绑定域名(替换为实际域名) ServerName api.example.com # 网站根目录指向 ThinkPHP 入口目录(必须是 public/) DocumentRoot /www/api/public # 允许 .htaccess 覆盖配置(ThinkPHP URL 重写依赖此设置) AllowOverride All # 允许 .htaccess 覆盖所有配置 Require all granted # 允许所有 IP 访问 # 安全:禁止上传目录执行 PHP Require all denied # 拒绝所有 .php 文件的访问 ``` ## PHP 生产环境优化 ### php.ini 推荐配置 ```ini ; ============================================================================= ; php.ini 生产环境推荐配置 ; ============================================================================= ; 配置路径:/etc/php/8.2/fpm/php.ini(Nginx + PHP-FPM 场景) ; 生效命令:sudo systemctl restart php8.2-fpm ; ============================================================================= ; ----------------------------------------------------------------------------- ; 错误处理(生产环境必须关闭错误显示) ; ----------------------------------------------------------------------------- display_errors = Off ; 不向浏览器显示错误(防止 SQL/路径泄露) error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED ; 记录除通知和弃用外的所有错误 ; ----------------------------------------------------------------------------- ; OPcache 加速(生产环境必须启用) ; ----------------------------------------------------------------------------- opcache.enable = 1 ; 启用 OPcache 字节码缓存 opcache.memory_consumption = 256 ; OPcache 共享内存大小(MB),按项目规模调整 opcache.interned_strings_buffer = 16 ; 驻留字符串内存(MB) opcache.max_accelerated_files = 10000 ; 最大缓存文件数(需大于项目 PHP 文件总数) opcache.validate_timestamps = 0 ; 不检查文件修改时间(性能最优,更新代码后需重启 PHP-FPM) opcache.save_comments = 1 ; 保留注释(ThinkPHP 注解依赖此配置) ``` :::warning OPcache 注意事项 `opcache.validate_timestamps = 0` 表示不检查文件修改时间,性能最优但更新代码后需要手动清除缓存:`php opcache:clear` 或重启 PHP-FPM。开发环境建议设为 `1`。 ::: ```ini ; ----------------------------------------------------------------------------- ; 内存与超时 ; ----------------------------------------------------------------------------- memory_limit = 256M ; 单脚本最大内存(Excel 导出建议 256M+) max_execution_time = 60 ; 单脚本最大执行时间(秒) max_input_time = 60 ; 解析请求数据的最大时间(秒) ; ----------------------------------------------------------------------------- ; 文件上传 ; ----------------------------------------------------------------------------- upload_max_filesize = 50M ; 单文件最大上传大小 post_max_size = 50M ; POST 请求最大大小(必须 >= upload_max_filesize) max_file_uploads = 20 ; 单次请求最大上传文件数 ; ----------------------------------------------------------------------------- ; 时区与会话 ; ----------------------------------------------------------------------------- date.timezone = Asia/Shanghai ; 时区(与 .env 中 APP.DEFAULT_TIMEZONE 一致) session.gc_maxlifetime = 7200 ; 会话过期时间(秒),默认 2 小时 ``` ### PHP-FPM 配置 ```ini ; ============================================================================= ; PHP-FPM 进程池配置 ; ============================================================================= ; 配置路径:/etc/php/8.2/fpm/pool.d/www.conf ; 生效命令:sudo systemctl restart php8.2-fpm ; ============================================================================= [www] ; 运行用户(Nginx 也需使用同一用户,否则权限冲突) user = www-data group = www-data ; ----------------------------------------------------------------------------- ; 进程管理方式 ; ----------------------------------------------------------------------------- ; dynamic = 动态管理(推荐生产环境),根据负载自动调整进程数 ; static = 固定进程数(高并发场景) pm = dynamic pm.max_children = 50 ; 最大子进程数(根据服务器内存调整,每个约 30-50MB) pm.start_servers = 5 ; 启动时的进程数 pm.min_spare_servers = 5 ; 最小空闲进程数(低于此值会自动创建) pm.max_spare_servers = 35 ; 最大空闲进程数(高于此值会自动销毁) pm.max_requests = 500 ; 单进程最大请求数(达到后重启,防内存泄漏) ; ----------------------------------------------------------------------------- ; 慢请求日志(排查性能问题) ; ----------------------------------------------------------------------------- slowlog = /var/log/php-fpm-slow.log ; 慢请求日志路径 request_slowlog_timeout = 5s ; 超过 5 秒的请求记录为慢请求 ``` ## 定时任务配置 ### Linux Crontab ```bash # 编辑当前用户的 crontab 定时任务 crontab -e # 添加定时任务:每分钟执行一次任务调度器 # 格式:分 时 日 月 周 命令 # * * * * * = 每分钟 # cd /www/api → 切换到项目目录(ThinkPHP 需要工作目录定位配置文件) # /usr/bin/php → PHP 绝对路径(crontab 环境变量不完整,必须用绝对路径) # think job:run → 执行 ThinkPHP 命令行任务调度 # >> /dev/null 2>&1 → 丢弃输出(有日志系统记录,不需要控制台输出) * * * * * cd /www/api && /usr/bin/php think job:run >> /dev/null 2>&1 # 验证 crontab 是否配置成功 crontab -l ``` ### Windows 计划任务 ```powershell # ============================================================================= # Windows 计划任务 — 每分钟执行一次任务调度器 # ============================================================================= # /create → 创建新任务 # /tn → 任务名称(在任务计划程序中显示的名称) # /tr → 要执行的命令 # cmd /c → 执行后关闭 cmd 窗口 # cd /d → 切换驱动器和目录(/d 支持跨盘符) # /sc minute → 调度频率为"分钟" # /mo 1 → 每 1 分钟执行一次 # /f → 强制覆盖同名任务 # ============================================================================= schtasks /create /tn "RXThinkCMF_Job" /tr "cmd /c cd /d D:\xampp8.2\htdocs\RXThinkCMF_ThinkPhp8_EleVue && C:\xampp\php\php.exe think job:run" /sc minute /mo 1 /f # 或通过图形界面:Win+R → 输入 taskschd.msc → 回车 ``` ### Daemon 守护进程(Supervisor) ```ini ; ============================================================================= ; Supervisor 进程守护配置 ; ============================================================================= ; 配置路径:/etc/supervisor/conf.d/rxthinkcmf-job.conf ; 生效命令:sudo supervisorctl reread && sudo supervisorctl update ; 优势:进程崩溃自动重启,比 crontab 更适合常驻进程 ; ============================================================================= [program:rxthinkcmf-job] command = php /www/api/think job:daemon ; 守护进程模式运行任务调度 directory = /www/api ; 工作目录 autostart = true ; Supervisor 启动时自动启动此进程 autorestart = true ; 进程退出后自动重启 user = www-data ; 运行用户 stdout_logfile = /var/log/rxthinkcmf-job.log ; 标准输出日志路径 ``` ```bash # 重新读取 Supervisor 配置文件(发现新增的 conf 文件) sudo supervisorctl reread # 更新进程状态(应用新配置) sudo supervisorctl update # 启动 rxthinkcmf-job 进程 sudo supervisorctl start rxthinkcmf-job # 查看进程状态 sudo supervisorctl status rxthinkcmf-job ``` ## 多数据库部署注意事项 ### MySQL ```bash # 确保 MySQL 服务已启动 sudo systemctl start mysql # 创建生产数据库 # CHARACTER SET utf8mb4 → 支持 emoji 等四字节字符 # COLLATE utf8mb4_general_ci → 不区分大小写的排序规则 mysql -u root -proot -e "CREATE DATABASE `rxthinkcmf.thinkphp8.elevue` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" # 导入初始化数据(项目自带的 SQL 文件) mysql -u root -proot rxthinkcmf.thinkphp8.elevue < document/mysql/rxthinkcmf.thinkphp8.elevue.sql ``` ### PostgreSQL ```bash # 确保 PostgreSQL 服务已启动 sudo systemctl start postgresql # 安装 PHP 的 PostgreSQL 扩展(如果尚未安装) sudo apt install php8.2-pgsql # 重启 PHP-FPM 使扩展生效 sudo systemctl restart php8.2-fpm # 创建数据库(-u postgres 以 postgres 用户执行) sudo -u postgres createdb rxthinkcmf.thinkphp8.elevue ``` ### SQLite ```bash # 创建数据库文件存放目录 mkdir -p /www/api/runtime # 创建空的 SQLite 数据库文件(touch 创建空文件即可) touch /www/api/runtime/database.sqlite # 设置文件权限(664 = owner/group 可读写,others 只读) chmod 664 /www/api/runtime/database.sqlite ``` ```ini ; .env 配置(切换到 SQLite 数据库) [DATABASE] TYPE = sqlite ; 数据库类型改为 sqlite DATABASE = /www/api/runtime/database.sqlite ; SQLite 文件路径 ``` ### 数据库连接池 高并发场景建议配置持久连接: ```ini ; php.ini — 使用 Unix Socket 连接 MySQL(比 TCP 更快) ; 前提:PHP-FPM 和 MySQL 在同一台服务器上 pdo_mysql.default_socket = /var/run/mysqld/mysqld.sock ``` ## 目录权限 ```bash # ----------------------------------------------------------------------------- # 项目目录(owner 可读写执行,group/others 可读和执行) # ----------------------------------------------------------------------------- chmod -R 755 /www/api/ # 目录 755,文件 644 chown -R www-data:www-data /www/api/ # 设置 owner 为 Web 服务器用户 # ----------------------------------------------------------------------------- # 运行时目录(必须可写 — ThinkPHP 缓存、日志、编译模板) # ----------------------------------------------------------------------------- chmod -R 777 /www/api/runtime/ # 777 确保 PHP-FPM 进程可写入 chown -R www-data:www-data /www/api/runtime/ # ----------------------------------------------------------------------------- # 上传目录(必须可写 — 用户上传文件存储位置) # ----------------------------------------------------------------------------- mkdir -p /opt/data/uploads # 创建上传目录(如不存在) chmod -R 755 /opt/data/uploads # 目录可写 chown -R www-data:www-data /opt/data/uploads # 设置 owner ``` ## SSL 证书(HTTPS) ```nginx # ============================================================================= # HTTPS 配置(生产环境必须启用) # ============================================================================= # HTTPS 站点配置 server { listen 443 ssl; # 监听 443 端口,启用 SSL server_name api.example.com; # SSL 证书文件路径(替换为实际证书路径) ssl_certificate /etc/ssl/certs/api.example.com.pem; # 证书文件(含证书链) ssl_certificate_key /etc/ssl/private/api.example.com.key; # 私钥文件 # ... 其他配置(location、fastcgi 等)同上方 Nginx 配置 ... } # HTTP 自动跳转 HTTPS(301 永久重定向) server { listen 80; # 监听 80 端口(HTTP) server_name api.example.com; return 301 https://$server_name$request_uri; # 301 跳转到 HTTPS } ```