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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...