摘要:經過各種復現和文檔查詢,發現是的擴展在和同時使用時,會導致無法正確的處理緩存文件,最終影響服務。但是在正常的服務下,卻并不會查找該文件。這個指令對于的文件同樣有效。文本編輯器以及等程序卻并不是這樣操作的,從而導致有可能緩沖了殘缺的文件。
最近遇到一個非常奇怪的bug,在主機PHP代碼版本回退的過程中,導致備機服務不可用。
經過各種復現和文檔查詢,發現是PHP的apc擴展在和rsync同時使用時,會導致無法正確的處理緩存文件,最終影響服務。解決方案官方也有提供,加上一行配置:
# php.ini [apc] apc.stat_ctime=1
下面我們來說明下這個問題出現的機制。
關鍵點:使用了PHP+apc擴展+rsync主從同步機制
故障表現:引入時找不到文件平臺服務上線更新后,訪問平臺服務時報錯信息:
Warning: include(Yii.php): failed to open stream: No such file or directory in /home/disk4/htdocs/oss_debug/protected/lib/Yii/framework/YiiBase.php on line 421 Warning: include(): Failed opening "Yii.php" for inclusion (include_path=".:/home/work/lnmp/weblib/phplib:/home/work/lnmp/lib/php") in /home/disk4/htdocs/oss_debug/protected/lib/Yii/framework/YiiBase.php on line 421 Fatal error: Class "Yii" not found in /home/disk4/htdocs/oss_debug/index.php on line 42
這里的提示信息表明,問題出現在YiiBase.php文件中,在421行引入Yii.php時找不到該文件,而這里的include為相對路徑,當前的引入路徑為.:/home/work/lnmp/weblib/phplib:/home/work/lnmp/lib/php,多個引入路徑以:分割,所以這里會在./,/home/work/lnmp/weblib/phplib,/home/work/lnmp/lib/php三個目錄下查找該文件,分別檢索了一下,發現確實均不存在該文件。
但是在正常的服務下,卻并不會查找該文件。具體為什么會去查找該文件,我猜測是先判斷Yii類是否存在,不存在就去引入Yii.php,而Yii類在yii.php文件中有定義,因此猜測是沒有正確引入yii.php導致。
# yii.php這個問題沒有深究,因為最后發現故障跟這個點無關。
復現一個小問題:改變目錄后無法服務你只需要將你的服務目錄換個名字即可復現,如你當前的服務目錄是/home/work/lnmp/htdocs/oss/,你將它重名為/home/work/lnmp/htdocs/oss2,這個時候你就會發現服務受到了影響:
# 訪問 domain.com/oss2/index.php Warning: file_get_contents(/home/work/lnmp/htdocs/oss/version): failed to open stream: No such file or directory in /home/work/lnmp/htdocs/oss/index.php on line 26 Warning: require_once(/home/work/lnmp/htdocs/oss/protected/lib/Yii/framework/yii.php): failed to open stream: No such file or directory in /home/work/lnmp/htdocs/oss/index.php on line 38 Fatal error: require_once(): Failed opening required "/home/work/lnmp/htdocs/oss6/protected/lib/Yii/framework/yii.php" (include_path=".:/home/work/lnmp/weblib/phplib:/home/work/lnmp/lib/php") in /home/work/lnmp/htdocs/oss6/index.php on line 38可以看到當我們訪問oss2目錄時,程序卻依然在嘗試讀取oss目錄下的文件,這時文件自然不存在,因此報錯。那么這是為什么呢?
原因是我們使用了PHP的apc擴展。
PHP的服務過程
學習過計算機原理的同學,都了解語言分為編譯型語言和解釋型語言,由于語言是人來編寫的,而機器無法直接執行,因此,在代碼被執行前需要經歷一個編譯成機器可以識別的操作碼的過程。編譯型語言在執行前提前編譯好,然后發布;解釋型語言先發布,在執行時即時編譯。因此我們常說編譯型語言的性能好,主要就是快在這個地方。
PHP屬于解釋型語言,常規的執行流程是:
Nginx轉發請求給PHP主進程
主進程引入代碼文件
PHP解釋器會先將代碼切分為Token
生成抽象語法樹
生成機器可以直接執行的操作碼
PHP虛擬機執行操作碼
如果文件有引入其他文件,循環執行上述2-6步驟
執行完成,返回結果
可以看到每次請求過來,都會對文件做一次編譯和緩存,那么這樣會非常影響效率,為了保證PHP的靈活性,同時提升效率,我們需要對編譯好的操作碼進行緩存。這就是apc擴展做的事情:
判斷文件是否有更新
如果更新,重新編譯并緩存
否則,直接讀取緩存的操作碼
apc擴展apc擴展文檔
Alternative PHP Cache (APC 可選 PHP 緩存) 是一個開放自由的 PHP opcode 緩存。它的目標是提供一個自由、 開放,和健全的框架,用于緩存、優化 PHP 中間代碼。該擴展也提供了一些內置的方法,可以用于手動設置或清空緩存。
清空緩存的方法:apc_clear_cache()。調用這個方法后可以解決因apc緩存過期文件導致的bug。另外,我們需要關注的幾個配置項:
apc.stat integer
是否啟用腳本更新檢查。 改變這個指令值要非常小心。 默認值 On 表示APC在每次請求腳本時都檢查腳本是否被更新, 如果被更新則自動重新編譯和緩存編譯后的內容。但這樣做對性能有不利影響。 如果設為 Off 則表示不進行檢查,從而使性能得到大幅提高。 但是為了使更新的內容生效,你必須重啟Web服務器(譯者注:如果采用cgi/fcgi類似的,需重啟cgi/fcgi進程)。 生產服務器上腳本文件很少更改, 可以通過禁用本選項獲得顯著的性能提升。這個指令對于include/require的文件同樣有效。但是需要注意的是, 如果你使用的是相對路徑,APC就必須在每一次include/require時都進行檢查以定位文件。 而使用絕對路徑則可以跳過檢查,所以鼓勵你使用絕對路徑進行include/require操作。
apc.stat_ctime integer
驗證ctime(創建時間)可以避免SVN或者rsync帶來的問題,確保自上次緩存統計inode沒有改變。APC通常只檢查mtime(修改時間)。apc.file_update_protection integer
當你在一個運行中的服務器上修改文件時,你應當執行原子操作。 也就是先寫進一個臨時文件,然后將該文件重命名(mv)到最終的名字。 文本編輯器以及 cp, tar 等程序卻并不是這樣操作的,從而導致有可能緩沖了殘缺的文件。 默認值 2 表示在訪問文件時如果發現修改時間距離訪問時間小于 2 秒則不做緩沖。 那個不幸的訪問者可能得到殘缺的內容,但是這種壞影響卻不會通過緩存擴大化。 如果你能確保所有的更新操作都是原子操作,那么可以用 0 關閉此特性。 如果你的系統由于大量的IO操作導致更新緩慢,你就需要增大此值。可以看到,apc擴展可能會導致兩個問題:
rsync/svn配合使用時存在無法正確處理文件緩存的問題
可能讀到殘缺文件,導致影響部分人的請求
針對這兩個問題,也分別提供了解決方案:
# php.ini [apc] # 啟動ctime檢查 stat_ctime=1 # 默認值為2,變大這個值 file_update_protection=5雖然文檔中有說明,但還是有很多人會遇到這種問題,可以參考:
stack overflow問題:Problems with APC on publish
官方Issue:apc.include_once_override turn on issue
在遇到這個問題時,除了上面的配置解決問題,還可以:
PHP代碼中執行apc_clear_cache()
重啟php-fpm進程
另外,我們可以將apc擴展安裝時包含的apc.php文件放到web服務目錄下,就可以可視化的觀察apc擴展的緩存情況。
服務使用了rsync同步這次故障的一個關鍵因素是使用了rsync同步,我的服務架構是:
導致這個問題的原因探究具體為什么在apc擴展跟rsync同時使用會產生這個bug,我沒有看源碼,不太了解,但我做了一些大膽的猜測,下面的內容不夠清楚和正確,希望大家能給我更精確的指導:
參考資料
這里可以看出文件是怎么檢查是否有更新的,而問題也就出現在這一部分,沒有辦法判斷文件是否被更新,同時正確讀取到緩存的文件。PHP手冊 - APC運行時配置:https://www.php.net/manual/zh...
stack overflow - Problems with APC on publish:https://stackoverflow.com/que...
PHP官方issue - apc.include_once_override turn on issue:https://bugs.php.net/bug.php?...
php可選緩存APC:https://www.cnblogs.com/hf805...
關于上線系統的一些想法 (for php):http://bikong0411.github.io/2...
如何刷新APC類加載器緩存?:http://cn.voidcc.com/question...
rsync文件同步服務:https://xdays.me/rsync%E6%96%...
APC"s Include Once Override breaks install:https://www.drupal.org/projec...
《PHP 7底層設計和源碼實現》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/31231.html
摘要:配合實現上傳進度條配合實現上傳進度條,一直以為是無法做到進度條一個動態的功能,原來還是自己的知識面不夠,看到了擴展相關的一些文章,利用擴展達到了動態實現效果。根據找到相關的文章自己并在此基礎上做了一些小修改。 PHP配合apc實現上傳進度條PHP配合apc實現上傳進度條, 一直以為PHP是無法做到進度條一個動態的功能,原來還是自己的知識面不夠,看到了apc擴展相關的一些文章,利用apc...
摘要:表示需要運行庫,缺少它將會在接來下的過程中彈出類似的提示運行庫下載如果以前安裝過,則不必再裝。回車回車回車即可將服務創建,關閉窗口。下的安裝有一個臨時密碼問題。有空再把下的安裝記錄一下。 最近想更新Web服務器上的軟件,查了一下apache、php、mysql版本都很高了,有些變動還很大,所以先在Win上安裝熟悉一下,下面是安裝配置記錄: 系統:64位Windows7時間:2017年3...
摘要:表示需要運行庫,缺少它將會在接來下的過程中彈出類似的提示運行庫下載如果以前安裝過,則不必再裝。回車回車回車即可將服務創建,關閉窗口。下的安裝有一個臨時密碼問題。有空再把下的安裝記錄一下。 最近想更新Web服務器上的軟件,查了一下apache、php、mysql版本都很高了,有些變動還很大,所以先在Win上安裝熟悉一下,下面是安裝配置記錄: 系統:64位Windows7時間:2017年3...
閱讀 3398·2021-10-11 11:06
閱讀 2182·2019-08-29 11:10
閱讀 1944·2019-08-26 18:18
閱讀 3255·2019-08-26 13:34
閱讀 1559·2019-08-23 16:45
閱讀 1037·2019-08-23 16:29
閱讀 2797·2019-08-23 13:11
閱讀 3226·2019-08-23 12:58