翼度科技»论坛 编程开发 PHP 查看内容

fpm模式下读取到is_cli为何为true

10

主题

10

帖子

30

积分

新手上路

Rank: 1

积分
30
目录

php-fpm下读取到is_cli为true,不知道你们是否遇到过,我是遇到了。。。。
有人会说,即使为true又怎么了,你是没遇到有些根据is_cli来走不同逻辑判断的,如果读取的是错的就会引起很大的问题。。。。
问题出现和简单排查

维护的老系统里有个上传的服务,用的是比较老的codeigniter,构建完代码后,突然发现 1个上传url报路径找不到
具体表现如下

因为这里是a1.domain.com 去调取upload.domain.com,所以出现跨域(如果upload.domain.com 正常的话,是有设置跨域的),现在明显设置跨域的失效了
直接打开链接看,如下图

因为是线上,即使再自信没改到这里,也要赶紧联系运维同事回滚代码,但是回滚后发现依然如此。
当时急的不行,让测试同事让他看看其它的上传链接是否可正常上传,发现其它的上传(比如视频上传,其它的图片的上传)是没问题的,唯一的区别就是走不走这个index.php入口文件
排查

因为当时已经晚上近10点了,使用的人也不多,一边让测试同学帮验证。我这边赶紧查代码。日常开发用的不是CI框架,赶紧搜索
  1. ERROR: Not Found The controller/method pair you requested was not found.
复制代码
这个是哪提示出来的,
在项目中发现代码位置如下,而且仅此一处

而且看到前面的is_cli,就是纳闷我这是php-fpm的网页请求,为何is_cli为true呢
追到is_cli的实现
  1. if ( ! function_exists('is_cli'))
  2. {
  3.         /**
  4.          * Is CLI?
  5.          *
  6.          * Test to see if a request was made from the command line.
  7.          *
  8.          * @return         bool
  9.          */
  10.         function is_cli()
  11.         {
  12.                 return (PHP_SAPI === 'cli' OR defined('STDIN'));
  13.         }
  14. }
复制代码
后来一路追到ci的路由解析
system/core/Router.php
  1. 124         public function __construct($routing = NULL)
  2. 125         {
  3. 126                 $this->config =& load_class('Config', 'core');
  4. 127                 $this->uri =& load_class('URI', 'core');
  5. 128                 //var_dump(PHP_SAPI);
  6. 129                 //var_dump(defined('STDIN'));
  7. 130                 //var_dump( is_cli());
  8. 131                 $this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE);
  9. 132
  10. 133                 // If a directory override is configured, it has to be set before any dynamic routing logic
  11. 134                 is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']);
  12. 135                 $this->_set_routing();
  13. 136
  14. 137                 // Set any routing overrides that may exist in the main index file
  15. 138                 if (is_array($routing))
  16. 139                 {
  17. 140                         empty($routing['controller']) OR $this->set_class($routing['controller']);
  18. 141                         empty($routing['function'])   OR $this->set_method($routing['function']);
  19. 142                 }
  20. 143
  21. 144                 log_message('info', 'Router Class Initialized');
  22. 145         }
复制代码
结合上图128,129行和上面is_cli函数的实现代码,130行不可能为true啊
脑袋快要炸了,通过调试发现只要131行的$this->enable_query_strings为true,那么上传功能就没问题
经过思考和猜测,严重怀疑是fpm读取到了cli下的opcache

主要基于以下几点

  • 其它入口(非index.php)的路径没问题
  • 命令行里有php index.php  这种定时脚本在跑
  • opcache的配置
  1. ri了一下如下
  2. $ php --ri 'Zend opcache'
  3. Zend OPcache
  4. Opcode Caching => Up and Running
  5. Optimization => Enabled
  6. SHM Cache => Enabled
  7. File Cache => Enabled
  8. Startup => OK
  9. Shared memory model => mmap
  10. Cache hits => 0
  11. Cache misses => 0
  12. Used memory => 36560720
  13. Free memory => 231874736
  14. Wasted memory => 0
  15. Interned Strings Used memory => 415960
  16. Interned Strings Free memory => 16361256
  17. Cached scripts => 0
  18. Cached keys => 0
  19. Max keys => 16229
  20. OOM restarts => 0
  21. Hash keys restarts => 0
  22. Manual restarts => 0
  23. Directive => Local Value => Master Value
  24. opcache.enable => On => On
  25. opcache.use_cwd => On => On
  26. opcache.validate_timestamps => On => On
  27. opcache.validate_permission => Off => Off
  28. opcache.validate_root => Off => Off
  29. opcache.inherited_hack => On => On
  30. opcache.dups_fix => Off => Off
  31. opcache.revalidate_path => Off => Off
  32. opcache.log_verbosity_level => 1 => 1
  33. opcache.memory_consumption => 256 => 256
  34. opcache.interned_strings_buffer => 16 => 16
  35. opcache.max_accelerated_files => 8000 => 8000
  36. opcache.max_wasted_percentage => 10 => 10
  37. opcache.consistency_checks => 0 => 0
  38. opcache.force_restart_timeout => 3600 => 3600
  39. opcache.revalidate_freq => 2 => 2
  40. opcache.file_update_protection => 2 => 2
  41. opcache.preferred_memory_model => no value => no value
  42. opcache.blacklist_filename => no value => no value
  43. opcache.max_file_size => 0 => 0
  44. opcache.protect_memory => 0 => 0
  45. opcache.save_comments => 1 => 1
  46. opcache.fast_shutdown => 0 => 0
  47. opcache.optimization_level => 0x7FFFBFFF => 0x7FFFBFFF
  48. opcache.opt_debug_level => 0 => 0
  49. opcache.enable_file_override => Off => Off
  50. opcache.enable_cli => On => On
  51. opcache.error_log => no value => no value
  52. opcache.restrict_api => no value => no value
  53. opcache.lockfile_path => /tmp => /tmp
  54. opcache.file_cache => /tmp => /tmp
  55. opcache.file_cache_only => 0 => 0
  56. opcache.file_cache_consistency_checks => 1 => 1
  57. opcache.huge_code_pages => Off => Off
复制代码
这里有下面几个配置项对fpm下读取到cli的缓存有关
  1. zend_extension=opcache.so
  2. opcache.enable=1
  3. opcache.enable_cli=1
  4. opcache.memory_consumption=256
  5. opcache.interned_strings_buffer=16
  6. opcache.max_accelerated_files=8000
  7. opcache.max_wasted_percentage=10
  8. opcache.use_cwd=1
  9. opcache.force_restart_timeout=3600
  10. opcache.file_cache=/tmp
复制代码

  • 1.开启了cli的opcache 即(enable_cli=1)
  • 2.使用了二级文件缓存 即(opcache.file_cache=/tmp)
于是尝试删除opcache的文件缓存,然后重启fpm,就好了
(实际上是,我打日志调着调着 突然自己好了,看fpm的日志是fpm触发了自动重启,我打日志时有修改了相关文件,fpm重启时检查文件更新重新生成了opcache)
后来为了防止这种情况再次发生就关闭了cli下的opcache,删除opcache文件缓存,重启fpm
然后我在测试上不断复现,发现可以稳定复现,实锤是fpm下读取到了cli已经生成好的缓存了
原起

这次的问题,我归结为以下两点

  • 对opcache的机制认识不够
  • CI框架这种fpm里和cli用了同样的入口文件而且根据is_cli来进行路由解析,会在我上面的配置和使用下出问题
粗浅探索

测试代码

现在有以下代码
路径为/data/www/emlog/op/
  1. test.php
  2. include/fun.php
  3. invalidate
复制代码
test.php
  1. include "include/fun.php";
  2. var_dump(sapi());
  3. var_dump(is_cli());
复制代码
include/fun.php
  1. function sapi(){
  2.         return php_sapi_name();
  3. }
  4. function is_cli()
  5. {
  6.          return (PHP_SAPI === 'cli' OR defined('STDIN'));
  7. }
复制代码
invalidate.php
  1. $files=[
  2.         '/data/www/emlog/op/test.php',
  3.         '/data/www/emlog/op/include/fun.php',
  4. ];
  5. foreach($files as $f){
  6.     $r=opcache_invalidate($f,true);
  7.     var_dump($r);
  8. }
复制代码
opcache配置
  1. [opcache]
  2. zend_extension=opcache.so
  3. opcache.enable=1
  4. opcache.enable_cli=1
  5. opcache.file_cache=/tmp
  6. opcache.memory_consumption=256
  7. opcache.interned_strings_buffer=16
  8. opcache.max_accelerated_files=8000
  9. opcache.max_wasted_percentage=10
  10. opcache.use_cwd=1
  11. opcache.force_restart_timeout=3600
  12. opcache.validate_timestamps=1
  13. opcache.revalidate_freq=2
  14. opcache.revalidate_path=0
复制代码
主要是前4个的配置
按照下图操作

更清楚的图片见 https://note.youdao.com/ynoteshare/index.html?id=2275a62e0fa926f2cf576940a1cd93d4&type=note&_time=1679154215415
is_cli为true时的缓存
  1. [root@hkui-qy tmp]# cat 8fc9c56d14b6542c6ff7147207730f6b/data/www/emlog/op/include/fun.php.bin |strings
  2. OPCACHE
  3. 8fc9c56d14b6542c6ff7147207730f6b0
  4. %%1n
  5. include/fun.php:235496:235544:/data/www/emlog/op
  6. /data/www/emlog/op/include/fun.php
  7. is_cli
  8. sapi
  9. php_sapi_name
复制代码
is_cli为false时的缓存
  1. [root@hkui-qy tmp]# cat 8fc9c56d14b6542c6ff7147207730f6b/data/www/emlog/op/include/fun.php.bin |strings
  2. OPCACHE
  3. 8fc9c56d14b6542c6ff7147207730f6b`
  4. include/fun.php:235648:235696:/data/www/emlog/op
  5. /data/www/emlog/op/include/fun.php
  6. 496:
  7. is_cli
  8. STDIN
  9. stdin
  10. sapi
  11. php_sapi_name
复制代码
共享内存缓存与文件缓存


  • fpm在启动或者重启时

    • 如果发现代码文件和缓存文件匹配,那么会读取文件的缓存到共享内存,所以使用文件缓存(可提前用opcache_compile_file生成opcache),在fpm重启时,能更快的获取opcache,减少内存使用
    • 如果发现代码文件和缓存文件对不匹配(缓存不存在或者代码文件有改变),那么会重新生成缓存,并同步到文件缓存里

  • 文件修改,fpm检测到了文件的变化,会重新生成共享内存缓存,并不会立马更新到文件缓存里,fpm重启 然后重新生成缓存后才会更新到文件缓存

来源:https://www.cnblogs.com/HKUI/p/opcache-is-cli-true.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具