fpm模式下讀取到is_cli為何為true
php-fpm下讀取到is_cli為true,不知道你們是否遇到過,我是遇到了。。。。
有人會說,即使為true又怎么了,你是沒遇到有些根據(jù)is_cli來走不同邏輯判斷的,如果讀取的是錯的就會引起很大的問題。。。。
問題出現(xiàn)和簡單排查
維護的老系統(tǒng)里有個上傳的服務(wù),用的是比較老的codeigniter,構(gòu)建完代碼后,突然發(fā)現(xiàn) 1個上傳url報路徑找不到
具體表現(xiàn)如下
因為這里是a1.domain.com
去調(diào)取upload.domain.com
,所以出現(xiàn)跨域(如果upload.domain.com
?正常的話,是有設(shè)置跨域的),現(xiàn)在明顯設(shè)置跨域的失效了
直接打開鏈接看,如下圖
因為是線上,即使再自信沒改到這里,也要趕緊聯(lián)系運維同事回滾代碼,但是回滾后發(fā)現(xiàn)依然如此。
當時急的不行,讓測試同事讓他看看其它的上傳鏈接是否可正常上傳,發(fā)現(xiàn)其它的上傳(比如視頻上傳,其它的圖片的上傳)是沒問題的,唯一的區(qū)別就是走不走這個index.php入口文件
排查
因為當時已經(jīng)晚上近10點了,使用的人也不多,一邊讓測試同學(xué)幫驗證。我這邊趕緊查代碼。日常開發(fā)用的不是CI框架,趕緊搜索
ERROR: Not Found The controller/method pair you requested was not found.
這個是哪提示出來的,
在項目中發(fā)現(xiàn)代碼位置如下,而且僅此一處
而且看到前面的is_cli,就是納悶我這是php-fpm的網(wǎng)頁請求,為何is_cli為true呢
追到is_cli的實現(xiàn)
if ( ! function_exists('is_cli'))
{ /**
* Is CLI?
*
* Test to see if a request was made from the command line.
*
* @return bool
*/
function is_cli() { return (PHP_SAPI === 'cli' OR defined('STDIN'));
}
}
后來一路追到ci的路由解析
system/core/Router.php
124 ? ? ? ? public function __construct($routing = NULL)
125 ? ? ? ? {126 ? ? ? ? ? ? ? ? $this->config =& load_class('Config', 'core');127 ? ? ? ? ? ? ? ? $this->uri =& load_class('URI', 'core');128 ? ? ? ? ? ? ? ? //var_dump(PHP_SAPI);129 ? ? ? ? ? ? ? ? //var_dump(defined('STDIN'));130 ? ? ? ? ? ? ? ? //var_dump( is_cli());131 ? ? ? ? ? ? ? ? $this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE);132133 ? ? ? ? ? ? ? ? // If a directory override is configured, it has to be set before any dynamic routing logic134 ? ? ? ? ? ? ? ? is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']);135 ? ? ? ? ? ? ? ? $this->_set_routing();136137 ? ? ? ? ? ? ? ? // Set any routing overrides that may exist in the main index file138 ? ? ? ? ? ? ? ? if (is_array($routing))139 ? ? ? ? ? ? ? ? {140 ? ? ? ? ? ? ? ? ? ? ? ? empty($routing['controller']) OR $this->set_class($routing['controller']);141 ? ? ? ? ? ? ? ? ? ? ? ? empty($routing['function']) ? OR $this->set_method($routing['function']);142 ? ? ? ? ? ? ? ? }143144 ? ? ? ? ? ? ? ? log_message('info', 'Router Class Initialized');145 ? ? ? ? }
結(jié)合上圖128,129行和上面is_cli函數(shù)的實現(xiàn)代碼,130行不可能為true啊
腦袋快要炸了,通過調(diào)試發(fā)現(xiàn)只要131行的$this->enable_query_strings
為true,那么上傳功能就沒問題
經(jīng)過思考和猜測,嚴重懷疑是fpm讀取到了cli下的opcache
主要基于以下幾點
其它入口(非index.php)的路徑?jīng)]問題
命令行里有php index.php 這種定時腳本在跑
opcache的配置
ri了一下如下
$ php --ri 'Zend opcache'Zend OPcache
Opcode Caching => Up and Running
Optimization => Enabled
SHM Cache => Enabled
File Cache => Enabled
Startup => OK
Shared memory model => mmap
Cache hits => 0Cache misses => 0Used memory => 36560720Free memory => 231874736Wasted memory => 0Interned Strings Used memory => 415960Interned Strings Free memory => 16361256Cached scripts => 0Cached keys => 0Max keys => 16229OOM restarts => 0Hash keys restarts => 0Manual restarts => 0Directive => Local Value => Master Value
opcache.enable => On => On
opcache.use_cwd => On => On
opcache.validate_timestamps => On => On
opcache.validate_permission => Off => Off
opcache.validate_root => Off => Off
opcache.inherited_hack => On => On
opcache.dups_fix => Off => Off
opcache.revalidate_path => Off => Off
opcache.log_verbosity_level => 1 => 1opcache.memory_consumption => 256 => 256opcache.interned_strings_buffer => 16 => 16opcache.max_accelerated_files => 8000 => 8000opcache.max_wasted_percentage => 10 => 10opcache.consistency_checks => 0 => 0opcache.force_restart_timeout => 3600 => 3600opcache.revalidate_freq => 2 => 2opcache.file_update_protection => 2 => 2opcache.preferred_memory_model => no value => no value
opcache.blacklist_filename => no value => no value
opcache.max_file_size => 0 => 0opcache.protect_memory => 0 => 0opcache.save_comments => 1 => 1opcache.fast_shutdown => 0 => 0opcache.optimization_level => 0x7FFFBFFF => 0x7FFFBFFFopcache.opt_debug_level => 0 => 0opcache.enable_file_override => Off => Off
opcache.enable_cli => On => On
opcache.error_log => no value => no value
opcache.restrict_api => no value => no value
opcache.lockfile_path => /tmp => /tmp
opcache.file_cache => /tmp => /tmp
opcache.file_cache_only => 0 => 0opcache.file_cache_consistency_checks => 1 => 1opcache.huge_code_pages => Off => Off
這里有下面幾個配置項對fpm下讀取到cli的緩存有關(guān)
zend_extension=opcache.soopcache.enable=1opcache.enable_cli=1opcache.memory_consumption=256opcache.interned_strings_buffer=16opcache.max_accelerated_files=8000opcache.max_wasted_percentage=10opcache.use_cwd=1opcache.force_restart_timeout=3600opcache.file_cache=/tmp
1.開啟了cli的opcache 即(enable_cli=1)
2.使用了二級文件緩存 即(opcache.file_cache=/tmp)
于是嘗試刪除opcache的文件緩存,然后重啟fpm,就好了
(實際上是,我打日志調(diào)著調(diào)著 突然自己好了,看fpm的日志是fpm觸發(fā)了自動重啟,我打日志時有修改了相關(guān)文件,fpm重啟時檢查文件更新重新生成了opcache)
后來為了防止這種情況再次發(fā)生就關(guān)閉了cli下的opcache,刪除opcache文件緩存,重啟fpm
然后我在測試上不斷復(fù)現(xiàn),發(fā)現(xiàn)可以穩(wěn)定復(fù)現(xiàn),實錘是fpm下讀取到了cli已經(jīng)生成好的緩存了
原起
這次的問題,我歸結(jié)為以下兩點
對opcache的機制認識不夠
CI框架這種fpm里和cli用了同樣的入口文件而且根據(jù)is_cli來進行路由解析,會在我上面的配置和使用下出問題
粗淺探索
測試代碼
現(xiàn)在有以下代碼
路徑為/data/www/emlog/op/
test.phpinclude/fun.php
invalidate
test.php
include "include/fun.php";var_dump(sapi());var_dump(is_cli());
include/fun.php
function sapi(){ return php_sapi_name();
}function is_cli() { ? ? ? ? return (PHP_SAPI === 'cli' OR defined('STDIN'));
}
invalidate.php
$files=[ '/data/www/emlog/op/test.php', '/data/www/emlog/op/include/fun.php',
];foreach($files as $f){ ? ?$r=opcache_invalidate($f,true); ? ?var_dump($r);
}
opcache配置
[opcache]zend_extension=opcache.soopcache.enable=1opcache.enable_cli=1opcache.file_cache=/tmpopcache.memory_consumption=256opcache.interned_strings_buffer=16opcache.max_accelerated_files=8000opcache.max_wasted_percentage=10opcache.use_cwd=1opcache.force_restart_timeout=3600opcache.validate_timestamps=1opcache.revalidate_freq=2opcache.revalidate_path=0
主要是前4個的配置
按照下圖操作
更清楚的圖片見?https://note.youdao.com/ynoteshare/index.html?id=2275a62e0fa926f2cf576940a1cd93d4&type=note&_time=1679154215415
is_cli為true時的緩存
[root@hkui-qy tmp]# cat 8fc9c56d14b6542c6ff7147207730f6b/data/www/emlog/op/include/fun.php.bin |strings
OPCACHE
8fc9c56d14b6542c6ff7147207730f6b0
%%1n
include/fun.php:235496:235544:/data/www/emlog/op
/data/www/emlog/op/include/fun.php
is_cli
sapi
php_sapi_name
is_cli為false時的緩存
[root@hkui-qy tmp]# cat 8fc9c56d14b6542c6ff7147207730f6b/data/www/emlog/op/include/fun.php.bin |strings
OPCACHE
8fc9c56d14b6542c6ff7147207730f6b`
include/fun.php:235648:235696:/data/www/emlog/op
/data/www/emlog/op/include/fun.php496:
is_cli
STDIN
stdin
sapi
php_sapi_name
共享內(nèi)存緩存與文件緩存
fpm在啟動或者重啟時
如果發(fā)現(xiàn)代碼文件和緩存文件匹配,那么會讀取文件的緩存到共享內(nèi)存,所以使用文件緩存(可提前用opcache_compile_file生成opcache),在fpm重啟時,能更快的獲取opcache,減少內(nèi)存使用
如果發(fā)現(xiàn)代碼文件和緩存文件對不匹配(緩存不存在或者代碼文件有改變),那么會重新生成緩存,并同步到文件緩存里
文件修改,fpm檢測到了文件的變化,會重新生成共享內(nèi)存緩存,并不會立馬更新到文件緩存里,fpm重啟 然后重新生成緩存后才會更新到文件緩存