女主宣言

开发时我们经常会使用各种各样的组件,系统提供的、第三方集成的,但组件经常会给我们挖不大不小的坑儿,需要我们一个个地去踩,这里说说这些年我们正在踩的坑儿 —— 之opcache

 

发现问题

同事A在发布新版本到线上后,例行检查,却发现没有达到预期的效果;

查代码、查服务器,无果;

半晌之后,说让我给看看。

A做事比较细心,一听他这么说,我心想:不会掉坑儿里了吧?

 

于是接手:

从lvs里摘下一台服务器,本地绑定host;

重启nginx,现象依旧;

重启fpm,现象消失,嗯,这里了;

查看fpm配置,正常;

查看php配置,没有发现问题,哦,用opcache了,难道这里?

 

继续尝试

禁用opcache,再发布一个版本,现象消失;

哦,opcache,妳肿么了?

 

鉴于opcache成熟应用了这么久,猜测这个问题应该是在特定的环境下才会触发;

特定什么的,查找起来就费事儿了,那就碰运气吧;

先stack trace,看stack能给个hint不?

启用opcache,再发布一个版本,打开项目的报错功能,故意访问一个不存在的controller,发现stack里出现了不同的版本号,wtf ?

不同版本号,你俩怎么共事起来了?你们之间的共同点,不就是link之前指向你、后来指向他么;咦,symlink?

 

内容截取:

exception 'QFrameRunException' with message'Controller File('/home/q/server/project_1002/src/application/controllers/1Controller.php')is Not Exist!!!

' in/home/q/php/QFrame/base/QFrameutils.php:114

Stack trace:

#0/home/q/php/QFrame/web/QFrameweb.php(168):QFrameBizResult::ensureNotFalse(false, 'Controller File...')

#1/home/q/php/QFrame/web/QFrameweb.php(150): QFrameWeb->dispatch()

#2/home/q/php/QFrame/web/QFrameweb.php(129): QFrameWeb->runController('1')

#3 /home/q/php/QFrame/web/QFrameweb.php(37):QFrameWeb->processRequest()

#4 /home/q/server/project_1001/src/www/index.php(13):QFrameWeb->run()

#5 {main}

 

求救:

我们的代码框架+服务器配置跟其他同事的环境类似,这个问题应该有同事遇到过,那么领导收集的信息应该是最全的,找领导!

一反馈,还真有同事遇到过,但没解决掉,领导怀疑是opcache+symlink的组合出了问题;

google之,发现lerdorf(php创始人)在github上回复的这个问题,soga!

 

该填坑儿了!

---------------事件整理---------------

环境

opcache+symlink

 

现象

运行时发现不会使用新版本

 

解决办法

nginx配置修改

fastcgi_paramSCRIPT_FILENAME $realpath_root$fastcgi_script_name;

fastcgi_paramDOCUMENT_ROOT $realpath_root;

 

 解析

opcache依赖的是opcode arrays,结构如下:

["/home/q/server/project_1001/src/www/index.php"]=>

array(6) {

        ["full_path"]=>

        string(72) "/home/q/server/project_1001/src/www/index.php"

        ["hits"]=>

        int(18)

        ["memory_consumption"]=>

        int(3976)

        ["last_used"]=>

        string(24) "Thu Nov 17 15:52:26 2016"

        ["last_used_timestamp"]=>

        int(1479369146)

        ["timestamp"]=>

        int(1478779049)

}

 

以不同版本的文件为例:

drwxr-xr-x 7 xxx xxx  4096 11月 10 19:43project_1001

drwxr-xr-x 7 xxx xxx  4096 11月 10 19:57project_1002

lrwxrwxrwx 1 xxx xxx    54 11月 18 10:06 project -> /home/q/server/project_1002

 

project先link到project_1001,opcache缓存index.php文件时,key=/home/q/server/project_1001/src/www/index.php,value是文件信息(见上述结构);

project后link到project_1002,opcache利用fastcgi参数DOCUMENT_ROOT、SCRIPT_FILENAME来读取path时,发现path没有变化,导致不会解析到project_1002,即project_1002不会生效;

但如果在fastcgi配置为$realpath_root,就可以让path发生变化,来保证project_1002生效;

 

PS:

我是在php5.5环境尝试的,php的其他版本仅供参考;

lerdorf指出了其他的解决方案,分明针对是path是relative path、web server是apache的情况,有兴趣的同学可以关注;

over

 

坑!坑!坑!opcache的坑我已经踩到腿残_重启