如何正确发布PHP代码
几乎每一个 PHP 程序员都发布过代码,可能是通过 FTP 或者 **rsync ** 同步的,也可能是通过 svn 或者 git 更新的。一个活跃的项目可能每天都要发布若干次代码,但是现实却是很少有人注意其中的细节,实际上这里面有好多坑,很可能你就在坑中却浑然不知。
一个正确实现的发布系统至少应该支持原子发布。如果说每一个版本都表示一个独立的状态的话,那么在发布期间,任何一次请求只能在单一状态下被执行。如此称之为支持原子发布;反之如果在发布期间,一次请求跨越不同的状态,那么就不能称之为原子发布。我们不妨举个例子来说明一下:假设一次请求需要 include
两个 PHP 文件,分别是 a.php
和 b.php
,当 include a.php
完成后,发布代码,接着 include b.php
,如果处理不当的话,那么就可能会导致旧版本的 a.php
和新版本的 b.php
同时存在于同一个请求之中,换句话说就是没有实现原子发布。
开源世界里有很多不错的发布代码工具,比如 ruby 社区的 capistrano,其流程大致就是发布代码到一个全新的目录,然后再软链接到真正的发布目录。
├── current -> releases/v1
└── releases
├── v1
│ ├── foo.php
│ └── bar.php
└── v2
├── foo.php
└── bar.php
不过鉴于 PHP 本身的特殊性,如果只是简单套用上面的流程,那么将很难实现真正的原子发布。要理清个中缘由,还需要了解一下 PHP 中的两个 Cache
的概念:
- opcode cache
- realpath cache
先聊聊 opcode cache
,基本就是 apc
或者 zend opcode
,关于它的作用,大家都已经很熟悉,不必多言,需要注意的是 apc
的 bug 很多,比如开启了 apc.enable_cli 配置后就会有很多灵异问题,所以说 opcode cache
还是尽可能使用 zend opcache
吧,如果需要缓存数据,可以用 apcu。此外 apc
和 zend opcode
对缓存键的选择有所差异:apc
选择的是文件的 inode
,zend opcode
选择的是文件的 path
。
再聊聊 realpath cache
,它的作用是缓冲获取文件信息的 IO 操作,大多数时候它对我们而言是透明的,以至于很多人都不知道它的存在,需要注意的是 realpath cache
是进程级别的,也就是说,每一个 php-fpm
进程都有自己独立的 realpath cache
。
假设在发布代码期间,opcode cache
或者 realpath cache
里的数据出现过期,那么就会出现一部分缓存是旧文件,一部分缓存是新文件的非原子发布的情况,为了避免出现这种情况,我们应该保证缓存过期时间足够长,最好是除非我们手动刷新,否则永远不过期,对应到配置上就是:关闭