fpm模式下讀取到is_cli為何為true

来源:https://www.cnblogs.com/HKUI/archive/2023/03/18/opcache-is-cli-true.html
-Advertisement-
Play Games

php-fpm下讀取到is_cli為true,不知道你們是否遇到過,我是遇到了。。。。 有人會說,即使為true又怎麼了,你是沒遇到有些根據is_cli來走不同邏輯判斷的,如果讀取的是錯的就會引起很大的問題。。。。 ...


目錄
php-fpm下讀取到is_cli為true,不知道你們是否遇到過,我是遇到了。。。。
有人會說,即使為true又怎麼了,你是沒遇到有些根據is_cli來走不同邏輯判斷的,如果讀取的是錯的就會引起很大的問題。。。。

問題出現和簡單排查

維護的老系統里有個上傳的服務,用的是比較老的codeigniter,構建完代碼後,突然發現 1個上傳url報路徑找不到
具體表現如下

因為這裡是a1.domain.com 去調取upload.domain.com,所以出現跨域(如果upload.domain.com 正常的話,是有設置跨域的),現在明顯設置跨域的失效了

直接打開鏈接看,如下圖

因為是線上,即使再自信沒改到這裡,也要趕緊聯繫運維同事回滾代碼,但是回滾後發現依然如此。
當時急的不行,讓測試同事讓他看看其它的上傳鏈接是否可正常上傳,發現其它的上傳(比如視頻上傳,其它的圖片的上傳)是沒問題的,唯一的區別就是走不走這個index.php入口文件

排查

因為當時已經晚上近10點了,使用的人也不多,一邊讓測試同學幫驗證。我這邊趕緊查代碼。日常開發用的不是CI框架,趕緊搜索

ERROR: Not Found The controller/method pair you requested was not found.

這個是哪提示出來的,
在項目中發現代碼位置如下,而且僅此一處

而且看到前面的is_cli,就是納悶我這是php-fpm的網頁請求,為何is_cli為true呢

追到is_cli的實現

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);
132
133                 // If a directory override is configured, it has to be set before any dynamic routing logic
134                 is_array($routing) && isset($routing['directory']) && $this->set_directory($routing['directory']);
135                 $this->_set_routing();
136
137                 // Set any routing overrides that may exist in the main index file
138                 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                 }
143
144                 log_message('info', 'Router Class Initialized');
145         }

結合上圖128,129行和上面is_cli函數的實現代碼,130行不可能為true啊

腦袋快要炸了,通過調試發現只要131行的$this->enable_query_strings為true,那麼上傳功能就沒問題

經過思考和猜測,嚴重懷疑是fpm讀取到了cli下的opcache

主要基於以下幾點

  • 其它入口(非index.php)的路徑沒問題
  • 命令行里有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 => 0
Cache misses => 0
Used memory => 36560720
Free memory => 231874736
Wasted memory => 0
Interned Strings Used memory => 415960
Interned Strings Free memory => 16361256
Cached scripts => 0
Cached keys => 0
Max keys => 16229
OOM restarts => 0
Hash keys restarts => 0
Manual restarts => 0

Directive => 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 => 1
opcache.memory_consumption => 256 => 256
opcache.interned_strings_buffer => 16 => 16
opcache.max_accelerated_files => 8000 => 8000
opcache.max_wasted_percentage => 10 => 10
opcache.consistency_checks => 0 => 0
opcache.force_restart_timeout => 3600 => 3600
opcache.revalidate_freq => 2 => 2
opcache.file_update_protection => 2 => 2
opcache.preferred_memory_model => no value => no value
opcache.blacklist_filename => no value => no value
opcache.max_file_size => 0 => 0
opcache.protect_memory => 0 => 0
opcache.save_comments => 1 => 1
opcache.fast_shutdown => 0 => 0
opcache.optimization_level => 0x7FFFBFFF => 0x7FFFBFFF
opcache.opt_debug_level => 0 => 0
opcache.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 => 0
opcache.file_cache_consistency_checks => 1 => 1
opcache.huge_code_pages => Off => Off

這裡有下麵幾個配置項對fpm下讀取到cli的緩存有關

zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=8000
opcache.max_wasted_percentage=10
opcache.use_cwd=1
opcache.force_restart_timeout=3600
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/

test.php
include/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.so
opcache.enable=1
opcache.enable_cli=1
opcache.file_cache=/tmp

opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=8000
opcache.max_wasted_percentage=10
opcache.use_cwd=1
opcache.force_restart_timeout=3600
opcache.validate_timestamps=1
opcache.revalidate_freq=2
opcache.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.php
496:
is_cli
STDIN
stdin
sapi
php_sapi_name

共用記憶體緩存與文件緩存

  • fpm在啟動或者重啟時
    • 如果發現代碼文件和緩存文件匹配,那麼會讀取文件的緩存到共用記憶體,所以使用文件緩存(可提前用opcache_compile_file生成opcache),在fpm重啟時,能更快的獲取opcache,減少記憶體使用
    • 如果發現代碼文件和緩存文件對不匹配(緩存不存在或者代碼文件有改變),那麼會重新生成緩存,並同步到文件緩存里
  • 文件修改,fpm檢測到了文件的變化,會重新生成共用記憶體緩存,並不會立馬更新到文件緩存里,fpm重啟 然後重新生成緩存後才會更新到文件緩存

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 前言 最近有個需求,需要使用JS快速讀取外部大數據文件(60w條記錄的表)。筆者嘗試過使用JS讀取Excel文件,但是跑了十幾分鐘仍未出結果,後來筆者嘗試將原數據保存為TXT文件,再從TXT文件中讀取數據,只需幾秒鐘即可讀取完畢。在此分享一下,也留著以後備用。 2 案例 為方便快速理解,筆者挑選 ...
  • 前端常見的十種佈局方式 作為一個開發小白,也是第一次編寫博客文章,若有錯誤請各位大牛大佬指正,輕噴!!! 我在學校接觸最多的就是前端,然後最近在學習新的前端知識,發現前端佈局常見的有很多種,不同的應用場景有不同的佈局方式,下麵就來簡單介紹一下吧。 靜態佈局 浮動佈局 定位佈局 柵格佈局 table布 ...
  • 此內容包含: html基礎 列表、表格 媒體元素 表單(重點) 1、HTML 基礎 目標: 會使用HTML5的基本結構創建網頁 會使用文本相關標簽排版文本信息 會使用圖像相關標簽實現圖文並茂的頁面 會使用標簽創建超鏈接、錨鏈接及功能性鏈接 1.1、什麼是HTML HTML:Hyper Text Ma ...
  • 定義 如果希望動態給某個類添加一些屬性或者方法,但是你又不希望這個類派生的對象受到影響,那麼裝飾器模式就可以給你帶來這樣的體驗。 它的定義就是在不改變原對象的基礎上,通過對其進行包裝拓展,使得原有對象可以動態具有更多功能,從而滿足用戶的更複雜需求。 舉個例子,一部手機,你可以買各種花里胡哨的手機殼等 ...
  • MVVM M:模型(Model):data V:視圖(View):模板 VM:視圖模型(ViewModel):Vue實例對象 Vue收到了MVVM模型的啟發,MVVM是vue實現數據驅動視圖和雙向數據綁定的核心原理,通過ViewModel監聽DOM和數據源的變化。 Object.defineProp ...
  • 這篇文章主要關註分散式鎖,包括加鎖和解鎖的過程,鎖的用法,加鎖帶來的代價,對性能的影響以及如何避免死鎖。 ...
  • 1. 巨型對象 1.1. humongous object 1.2. 大於等於區域一半大小的對象 1.3. 巨型對象被直接分配在老年代,所以它在新生代回收期間不會被釋放 1.4. G1區域的大小是2的冪,最小值是1 MB 1.5. 如果堆的最大值和初始值差別很大,就會有過多的G1區域,這種情況下應該 ...
  • Java基礎複習筆記 第01章:Java語言概述 1. Java基礎學習的章節劃分 第1階段:Java基本語法 Java語言概述、Java的變數與進位、運算符、流程式控制制語句(條件判斷、迴圈結構)、break\continue、 IDEA開發工具的使用、數組 第2階段:面向對象編程(基礎、進階、高級) ...
一周排行
    -Advertisement-
    Play Games
  • 在C#中使用SQL Server實現事務的ACID(原子性、一致性、隔離性、持久性)屬性和使用資料庫鎖(悲觀鎖和樂觀鎖)時,你可以通過ADO.NET的SqlConnection和SqlTransaction類來實現。下麵是一些示例和概念說明。 實現ACID事務 ACID屬性是事務處理的四個基本特征, ...
  • 我們在《SqlSugar開發框架》中,Winform界面開發部分往往也用到了自定義的用戶控制項,對應一些特殊的界面或者常用到的一些局部界面內容,我們可以使用自定義的用戶控制項來提高界面的統一性,同時也增強了使用的便利性。如我們Winform界面中用到的分頁控制項、附件顯示內容、以及一些公司、部門、菜單的下... ...
  • 在本篇教程中,我們學習瞭如何在 Taurus.MVC WebMVC 中進行數據綁定操作。我們還學習瞭如何使用 ${屬性名稱} CMS 語法來綁定頁面上的元素與 Model 中的屬性。通過這些步驟,我們成功實現了一個簡單的數據綁定示例。 ...
  • 是在MVVM中用來傳遞消息的一種方式。它是在MVVMLight框架中提供的一個實現了IMessenger介面的類,可以用來在ViewModel之間、ViewModel和View之間傳遞消息。 Send 接受一個泛型參數,表示要發送的消息內容。 Register 方法用於註冊某個對象接收消息。 pub ...
  • 概述:在WPF中,通過EventHandler可實現基礎和高級的UI更新方式。基礎用法涉及在類中定義事件,併在UI中訂閱以執行更新操作。高級用法藉助Dispatcher類,確保在非UI線程上執行操作後,通過UI線程更新界面。這兩種方法提供了靈活而可靠的UI更新機制。 在WPF(Windows Pre ...
  • 概述:本文介紹了在C#程式開發中如何利用自定義擴展方法測量代碼執行時間。通過使用簡單的Action委托,開發者可以輕鬆獲取代碼塊的執行時間,幫助優化性能、驗證演算法效率以及監控系統性能。這種通用方法提供了一種便捷而有效的方式,有助於提高開發效率和代碼質量。 在軟體開發中,瞭解代碼執行時間是優化程式性能 ...
  • 概述:Cron表達式是一種強大的定時任務調度工具,通過配置不同欄位實現靈活的時間規定。在.NET中,Quartz庫提供了簡便的方式配置Cron表達式,實現精準的定時任務調度。這種靈活性和可擴展性使得開發者能夠根據需求輕鬆地制定和管理定時任務,例如每天備份系統日誌或其他重要操作。 Cron表達式詳解 ...
  • 概述:.NET提供多種定時器,如System.Windows.Forms.Timer適用於UI,System.Web.UI.Timer用於Web,System.Diagnostics.Timer用於性能監控,System.Threading.Timer和System.Timers.Timer用於一般 ...
  • 問題背景 有同事聯繫我說,在生產環境上,訪問不了我負責的common服務,然後我去檢查common服務的health endpoint, 沒問題,然後我問了下異常,timeout導致的System.OperationCanceledException。那大概率是客戶端的問題,會不會是埠耗盡,用ne ...
  • 前言: 在本篇 Taurus.MVC WebMVC 入門開發教程的第四篇文章中, 我們將學習如何實現數據列表的綁定,通過使用 List<Model> 來展示多個數據項。 我們將繼續使用 Taurus.Mvc 命名空間,同時探討如何在視圖中綁定並顯示一個 Model 列表。 步驟1:創建 Model ...