You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Nginx+PHP-FPM环境下实现原子化部署且不丢失任何请求?

如何在Nginx+PHP-FPM环境下实现原子化部署且不丢失任何请求?

我太懂你这种不想让用户看到503、不想错过任何请求的部署焦虑了!之前维护PHP应用的时候也踩过类似的坑,咱们直接说两个简单可行的方案,不用搞复杂的多服务器集群:

方案一:符号链接原子替换 + PHP-FPM优雅重载

这个方案核心是用原子性的目录切换配合PHP-FPM的平滑重载,完全避免代码切换的中间状态,同时让旧进程处理完所有 pending 请求:

  • 第一步:把新版本的代码上传到一个临时目录,比如/var/www/app_v2,确保所有文件都上传完毕、权限设置正确
  • 第二步:用原子操作替换代码的主目录。如果你的应用目录是/var/www/app,建议把它做成软链接,执行:
    ln -sfn /var/www/app_v2 /var/www/app
    
    这个ln -sf操作是原子的,系统不会出现“一半旧代码一半新代码”的状态
  • 第三步:给PHP-FPM发送USR2信号执行优雅重载:
    kill -USR2 $(cat /var/run/php-fpm.pid)
    
    这时候旧的PHP-FPM进程会继续处理当前所有请求,不会接受新请求;新的进程会加载新目录下的代码(包括重新初始化OPcache),待旧进程处理完所有请求后自动退出。

为啥之前你觉得USR2不行?其实是OPcache的缓存问题——但用原子目录替换后,新进程加载的是全新的目录,OPcache会重新缓存新代码,旧进程处理完请求就消失,完全不会有新旧代码混合的情况,也不需要调整opcache.revalidate_freq。

方案二:Graceful Stop + 原子代码替换 + 快速启动

如果你的PHP-FPM版本对USR2支持不太友好,或者你更倾向于先停旧进程再启新的,那可以用这个零空窗的操作流程:

  • 第一步:提前把新代码准备好,比如放在/var/www/app_new
  • 第二步:给PHP-FPM发送SIGQUIT信号执行优雅停止:
    kill -SIGQUIT $(cat /var/run/php-fpm.pid)
    
    这个信号会让PHP-FPM立即停止接受新请求,但会把当前所有pending请求处理完再退出,不会中途中断请求
  • 第三步:原子替换代码目录(这一步一定要快,用mv操作也是原子的):
    mv /var/www/app_new /var/www/app
    
  • 第四步:立刻启动PHP-FPM:
    systemctl start php-fpm
    
    因为第三步是原子操作,而且旧进程是处理完请求才退出的,新进程启动的间隙极短,Nginx几乎不会碰到后端不可用的情况。如果还是担心,可以给Nginx加个简单的配置:在fastcgi_pass所在的location块里加上:
    fastcgi_next_upstream error timeout;
    
    让Nginx在碰到临时错误时自动重试一次,彻底避免503。

为啥不用直接stop/start?

你之前试的stop→更新→start流程之所以会出503,是因为stop会强制杀死所有PHP-FPM进程,不管有没有正在处理的请求,而且中间的更新步骤有明显的空窗期,Nginx找不到后端就返回503。而上面两个方案要么让旧进程处理完请求再退出,要么把空窗期压缩到几乎可以忽略的程度。

如果你的业务量极大,单服务器确实可能需要多节点的上游集群来做无缝部署,但大部分中小应用用上面的简单方案就完全能满足“100%处理请求”的需求啦!

备注:内容来源于stack exchange,提问作者LiBorecek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:40:29