配置推荐
opcache.enable=1
opcache.validate_timestamps=1
opcache.revalidate_freq=1
opcache.file_cache=/tmp
opcache.huge_code_pages=1
opcache.save_comments=0
opcache.max_accelerated_files=30000
huge_code_pages
在系统中开启HugePages, 然后开启Opcache的huge_code_pages,提升PHP7的性能。
$sudo sysctl vm.nr_hugepages=512
分配512个预留的大页内存:
$ cat /proc/meminfo | grep Huge
AnonHugePages: 106496 kB
HugePages_Total: 512
HugePages_Free: 504
HugePages_Rsvd: 27
HugePages_Surp: 0
Hugepagesize: 2048 kB
然后在php.ini中加入:
opcache.huge_code_pages=1
PHP会把自身的text段, 以及内存分配中的huge都采用大内存页来保存, 减少TLB miss, 从而提高性能.
file_cache
opcache.file_cache=/tmp
配置二级缓存目录并启用二级缓存。 启用二级缓存可以在 SHM 内存满了、服务器重启或者重置 SHM 的时候提高性能。 默认值为空字符串 “”,表示禁用基于文件的缓存。
save_comments
opcache.save_comments=0
可以根据项目情况设置。如果禁用,脚本文件中的注释内容将不会被包含到操作码缓存文件, 这样可以有效减小优化后的文件体积。 禁用此配置指令可能会导致一些依赖注释或注解的 应用或框架无法正常工作, 比如: Doctrine, Zend Framework 2 以及 PHPUnit。
max_accelerated_files
OPcache 哈希表中可存储的脚本文件数量上限。 真实的取值是在质数集合 { 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987 } 中找到的第一个大于等于设置值的质数。 设置值取值范围最小值是 200,最大值在 PHP 5.5.6 之前是 100000,PHP 5.5.6 及之后是 1000000。建议设置至少要大于项目文件总数。
max_wasted_percentage
浪费内存的上限,以百分比计。 如果达到此上限,那么 OPcache 将产生重新启动续发事件。
如上是官方解释,实际上这个配置有个前提,是共享内存不足或者缓存的hash keys达到上限时才会触发
慎用enable_cli=true
如果项目框架里有PHP_SAPI === 'cli'
类似的判断,请不要开启enable_cli=true。因为开启这个并且设置了file_cache,php-fpm和cli会共用file_cache,如果php-fpm先执行了,再用cli方式执行脚本,那么PHP_SAPI的返回并不是’cli’。而且官方文档里明确写着:仅针对 CLI 版本的 PHP 启用操作码缓存, 通常被用来测试和调试。总之,请慎用。
链接目录的坑
.
├── webroot -> releases/v1
└── releases
├── v1
│ ├── foo.php
│ └── config -> conf/v1
└── v2
├── foo.php
└── config -> conf/v2
webroot是nginx路由的根目录,本身是个链接目录,然后config作为配置又链接到conf目录。
这种链接结构的目录强烈不推荐,因为这个会造成opcache不会刷新,主要是因为 opcache 是通过 realpath cache 获取文件信息,即便软链接已经指向了新位置,但是如果 realpath cache 里还保存着旧数据的话,opcache 依然无法知道新代码的存在。查阅相关资料并验证了如下解决方案:
* 重启PHP-FPM
* 修改NGINX配置,这样会有一定的IO消耗
##修改前
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $document_root;
##修改后
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
- 等待realpath cache自动刷新,php.ini默认刷新时间是两分钟
realpath_cache_ttl = 120
- opcache.revalidate_path=1 社区里讨论修改opcache.ini参数,经过验证不起作用
- 最完美的解决方案就是不用链接,比如说config链接,完全可以在发布的时候直接将conf的代码拷贝到对应的位置。至于webroot根目录,完全可以弃用,直接配置nginx conf root=releases/v1即可。
发布负载高的问题
opcache cache miss引发
发布时,很有可能opcache大量未命中导致瞬间负载高,尤其是发布路径改变后。其中比较常规的解决方式是,先把代码脚本进行预热,再改nginx新的root路径,然后再nginx reload,这样就有效的避免负载高的问题。代码如下:
function opcache_compile_files($dir) {
$cnt = 0;
foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $v) {
if(!$v->isDir() && preg_match('%\.php$%', $v->getRealPath())) {
$file = $v->getRealPath();
$ret = opcache_compile_file($file);
if (!$ret) {
echo 0;
exit(1);
}
$cnt++;
}
}
return $cnt;
}
opcache_compile_files(/xxx/release_new);
共享内存或者hash keys不足导致reset
春哥在升级PHP7后,发现负载剧增,尝试了以上解决方案,未果。后来发现是由于发布目录每次变更,导致缓存文件数过多,然后导致opcache reset,当时我们的文件数是40000多,然后内存是1G,其中有大量的wasted_memory,后来通过扩大共享内存解决。当时重启的opcache状态如下,供参考:
{"opcache_enabled":true,"file_cache":"\/tmp","cache_full":false,"restart_pending":true,"restart_in_progress":false,"memory_usage":{"used_memory":401770392,"free_memory":6016448,"wasted_memory":665954984,"current_wasted_percentage":62.02189102768898},"interned_strings_usage":{"buffer_size":12582912,"used_memory":8375296,"free_memory":4207616,"number_of_strings":132795},"opcache_statistics":{"num_cached_scripts":35858,"num_cached_keys":57589,"max_cached_keys":130987,"hits":4163507423,"start_time":1534407006,"last_restart_time":0,"oom_restarts":0,"hash_restarts":0,"manual_restarts":0,"misses":110453,"blacklist_misses":0,"blacklist_miss_ratio":0,"opcache_hit_rate":99.99734718691077}}
其中注意一下这个字段opcache.max_wasted_percentage,该字段的具体含义通过看源码得知,是当内存不足时,浪费内存的上限百分比,还有一点需要注意:依据PHP字节码缓存的场景,OPCache的内存管理设计非常简单,快速读写,不释放内存,过期数据置为Wasted。当Wasted内存大于设定值时,自动重启OPCache机制,清空并重新生成缓存。
安全漏洞
- opcache.file_cache_only=0
- opcache.validate_timestamp=1
- 谨慎地审查我们的代码,并检查网站中是否存在文件上传漏洞
这三点尽量注意,就可以避免一定的安全问题
点赞